Javascript - clearing duplicates from an array object - javascript

Hi
I have a javascript array object rapresenting the amount of items sold in a given country, like this:
var data = [{'c1':'USA', 'c2':'Item1', 'c3':100},
{'c1':'Canada', 'c2':'Item1', 'c3':120},
{'c1':'Italy', 'c2':'Item2', 'c3':140},
{'c1':'Italy', 'c2':'Item2', 'c3':110}]
I need to avoid duplicates (as you may see, the last two 'records' have the same Country and the same Item) and sum the amounts; if I was getting data from a database I would use the DISTINCT SUM clause, but what about it in this scenario? Is there any good jquery trick?

You could use an object as a map of distinct values, like this:
var distincts, index, sum, entry, key;
distincts = {};
sum = 0;
for (index = 0; index < data.length; ++index) {
entry = data[index];
key = entry.c1 + "--sep--" + entry.c2;
if (!distincts[key]) {
distincts[key] = true;
sum += entry.c3;
}
}
How that works: JavaScript objects are maps, and since access to properties is an extremely common operation, a decent JavaScript implementation tries to make property access quite fast (by using hashing on property keys, that sort of thing). You can access object properties using a string for their name, by using brackets ([]), so obj.foo and obj["foo"] both refer to the foo property of obj.
And so:
We start with an object with no properties.
As we loop through the array, we create unique key from c1 and c2. It's important that the "--sep--" string be something that cannot appear in c1 or c2. If case isn't significant, you might throw a .toLowerCase in there.
If distincts already has a value for that key, we know we've seen it before and we can ignore it; otherwise, we add a value (true in this case, but it can be just about anything other than false, undefined, 0, or "") as a flag indicating we've seen this unique combination before. And we add c3 to the sum.
But as someone pointed out, your last two entries aren't actually the same; I'm guessing that was just a typo in the question...

jQuery may have an array function for this, but because your two Italy objects are not distinctly unique, your asking for a custom solution. You want to populate a array and check it for duplicates as you go:
var data = [{'c1':'USA', 'c2':'Item1', 'c3':100},
{'c1':'Canada', 'c2':'Item1', 'c3':120},
{'c1':'Italy', 'c2':'Item2', 'c3':140},
{'c1':'Italy', 'c2':'Item1', 'c3':110}]
var newArray = [];
var dupeCheck = {}; // hash map
for(var i=0; i < data.length; i++){
if(!dupeCheck[data[i].c1]){
newArray.push(data[i]);
dupeCheck[data[i].c1] = true;
}
}

test
HTML:
<div id="test"></div>
JS:
var data = [{'c1':'USA', 'c2':'Item1', 'c3':100},
{'c1':'Canada', 'c2':'Item1', 'c3':120},
{'c1':'Italy', 'c2':'Item2', 'c3':140},
{'c1':'Italy', 'c2':'Item1', 'c3':110}];
var
l = data.length, // length
f = "", // find
ix = "", // index
d = []; // delete
for (var i = 0; i < l; i++) {
ix = data[i].c1 + "_" + data[i].c2 + "__";
//var re = new RegExp(ix);
//if (re.test(f))
if (f.indexOf(ix) != -1)
d.push(i);
else
f += ix;
}
for (var i1 = 0; i1 < d.length; i1++){
$("#test").append("<div>for delete: "+d[i1]+"</div>");
}
EDIT
Although chrome works much faster, works only in chrome faster the example with indexOf, then in IE/Opera/Firefox/Safary works faster with an object.
The code created by "# TJ Crowder" is much more efficient.

Related

Create variables based on array

I have the following array and a loop fetching the keys (https://jsfiddle.net/ytm04L53/)
var i;
var feeds = ["test_user_201508_20150826080829.txt:12345","test_user_list20150826:666","test_list_Summary20150826.txt:321"];
for (i = 0; i < feeds.length; i++) {
var feed = feeds[i];
alert(feed.match(/\d+$/));
}
The array will always contain different number of keys, What I would like to do is either use these keys as variables and assign the value after the : semicolon as its value or just create a new set of variables and assign the values found on these keys to them.
How can I achieve this? so that I can then perform some sort of comparison
if (test_user > 5000) {dosomething}
update
Thanks for the answers, how can I also create a set of variables and assign the array values to them? For instance something like the following.
valCount(feeds.split(","));
function valCount(t) {
if(t[0].match(/test_user_.*/))
var testUser = t[0].match(/\d+$/);
}
Obviously there is the possibility that sometimes there will only be 1 key in the array and some times 2 or 3, so t[0] won't always be test_user_
I need to somehow pass the array to a function and perform some sort of matching, if array key starts with test_user_ then grab the value and assign it to a define variable.
Thanks guys for all your help!
You can't (reasonably) create variables with dynamic names at runtime. (It is technically possible.)
Instead, you can create object properties:
var feeds = ["test_user_201508_20150826080829.txt:12345","test_user_list20150826:666","test_list_Summary20150826.txt:321"];
var obj = {};
feeds.forEach(function(entry) {
var parts = entry.split(":"); // Splits the string on the :
obj[parts[0]] = parts[1]; // Creates the property
});
Now, obj["test_user_201508_20150826080829.txt"] has the value "12345".
Live Example:
var feeds = ["test_user_201508_20150826080829.txt:12345","test_user_list20150826:666","test_list_Summary20150826.txt:321"];
var obj = {};
feeds.forEach(function(entry) {
var parts = entry.split(":");
obj[parts[0]] = parts[1];
});
snippet.log(obj["test_user_201508_20150826080829.txt"]);
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
You can do it like this, using the split function:
var i;
var feeds = ["test_user_201508_20150826080829.txt:12345","test_user_list20150826:666","test_list_Summary20150826.txt:321"];
for (i = 0; i < feeds.length; i++) {
var feed = feeds[i];
console.log(feed.split(/[:]/));
}
This outputs:
["test_user_201508_20150826080829.txt", "12345"]
["test_user_list20150826", "666"]
["test_list_Summary20150826.txt", "321"]
Use the split method
var feeds = ["test_user_201508_20150826080829.txt:12345","test_user_list20150826:666","test_list_Summary20150826.txt:321"];
feedMap = {}
for (i = 0; i < feeds.length; i++) {
var temp = feeds[i].split(':');
feedMap[temp[0]] = temp[1];
}
Yields:
{
"test_user_201508_20150826080829.txt":"12345",
"test_user_list20150826":"666",
"test_list_Summary20150826.txt":"321"
}
And can be accessed like:
feedMap["test_user_201508_20150826080829.txt"]
Here is a codepen
it is not very good idea but if you really need to create variables on-the-run here's the code:
for (i = 0; i < feeds.length; i++)
{
var feed = feeds[i];
window[feed.substring(0, feed.indexOf(":"))] = feed.match(/\d+$/);
}
alert(test_user_201508_20150826080829)
Of course you cannot have any variable-name-string containing banned signs (like '.')
Regards,
MichaƂ

How to sort an array based on values of another array?

I have an array that has following values
Nata_sha_AD8_02_ABA
Jack_DD2_03_K
Alex_AD8_01_PO
Mary_CD3_03_DC
John_DD2_01_ER
Daniel_AD8_04_WS
I want to group them based on following array ['AD8','CD3','DD2','PD0']; and sort each group based on number of each value. So the output should be
Alex_AD8_01_PO
Nata_sha_AD8_02_ABA
Daniel_AD8_04_WS
Mary_CD3_03_DC
John_DD2_01_ER
Jack_DD2_03_K
So far, I wrote following code, but it does not work properly, and I am stuck here.
var temparr = [];
var order = 1000;
var pos = -1;
var temp = -1;
var filterArray= ['AD8','CD3','DD2','PD0'];
for (i =0; i< filterArray.length; i++) {
for (j =0; j < myarray.length; j++) {
if(filterArray[i].toUpperCase().search(myarray[j])>0){
temp = str.substring(myarray[j].indexOf(filterArray[i])+4, myarray[j].indexOf(filterArray[i]+6);
if(temp < order){
pos = j;
order = temp;
}
if(j == myarray.length-1){ //reached end of the loop
temparr.push(myarray[pos]);
order = 1000;
}
}
}
}
Using the first sort parameter you can pass a function to run to sort the array. This function receives 2 values of the array, and should compare them and return less than 0 if the first is lower than the second, higher than 0 if it is higher, or 0 if they are the same. In my proposition, I split the name and "token" part of the values, and then compare the tokens to order them correctly. Using the indexOf on the filterArray allows me to compare the position of the tags accordingly.
var array_to_sort = ['Natasha_AD8_02',
'Jack_DD2_03',
'Alex_AD8_01',
'Mary_CD3_03',
'John_DD2_01',
'Daniel_AD8_04'
];
var filterArray = ['AD8', 'CD3', 'DD2', 'PD0'];
array_to_sort.sort(function(a, b) {
a_token = a.substr(a.indexOf('_')+1); //Remove the name part as it is useless
b_token = b.substr(b.indexOf('_')+1);//Remove the name part as it is useless
if(a_token.substr(0,3) == b_token.substr(0,3)){//If the code is the same, order by the following numbers
if(a_token > b_token){return 1;}
if(a_token < b_token){return -1;}
return 0;
}else{ //Compare the position in the filterArray of each code.
if(filterArray.indexOf(a_token.substr(0,3)) > filterArray.indexOf(b_token.substr(0,3))){return 1;}
if(filterArray.indexOf(a_token.substr(0,3)) < filterArray.indexOf(b_token.substr(0,3))){return -1;}
return 0;
}
});
document.write(array_to_sort);
EDIT: This method will sort in a way that the filterArray can be in any order, and dictates the order wanted. After updates from OP this may not be the requirement... EDIT2: the question being modified more and more, this solution will not work.
My solution.
The only restriction this solution has has is that your sort array has to be sorted already. The XXn_nn part can be anywhere in the string, but it assumes the nn part always follows the XXn part (like DD3_17).
var result=new Array();
var p,x;
//loop the 'search' array
for(var si=0,sl=sort.length;si<sl;si++){
//create new tmp array
var tmp=new Array();
//loop the data array
for(var ai=0,al=arr.length;ai<al;ai++){
var el=arr[ai];
//test if element still exists
if(typeof el=='undefined' || el=='')continue;
//test if element has 'XXn_nn' part
if(arr[ai].indexOf(sort[si]) > -1){
//we don't now where the 'XXn_nn' part is, so we split on '_' and look for it
x=el.split('_');
p=x.indexOf(sort[si]);
//add element to tmp array on position nn
tmp[parseInt(x[p+1])]=el;
//remove element from ariginal array, making sure we don't check it again
arr.splice(ai,1);ai--;
}
}
//remove empty's from tmp array
tmp=tmp.filter(function(n){return n!=undefined});
//add to result array
result=result.concat(tmp);
}
And a working fiddle
On the basis that the filtering array is in alphabetical order, and that every string has a substring in the format _XXN_NN_ that you actually want to sort on, it should be sufficient simply to sort based on extracting that substring, without reference to filterArray:
var names = ['Nata_sha_AD8_02_ABA', 'Jack_DD2_03_K', 'Alex_AD8_01_PO', 'Mary_CD3_03_DC', 'John_DD2_01_ER', 'Daniel_AD8_04_WS'];
names.sort(function(a, b) {
var re = /_((AD8|CD3|DD2|PD0)_\d\d)_/;
a = a.match(re)[1];
b = b.match(re)[1];
return a.localeCompare(b);
});
alert(names);

Google Sites Listitem

I am working with the google sites list item.
The classes are Here and Here
I have been able to iterate through the columns and put all of the column headers in to one array with the following code.
//Global
var page = getPageByUrl(enter URL here)
var name = page.getName();
function getInfo() {
var columns = page.getColumns();
//Get Column Names
for (var j in columns) {
var cName =columns[j].getName();
columnList.push(cName);
}
}
Now I want to be able to get each row of the listitem and put it in its own array.
I can add the variable
function getInfo() {
var columns = page.getColumns();
var listItems = page.getListItems();//new variable
//Get Column Names
for (var j in columns) {
var cName =columns[j].getName();
columnList.push(cName);
}
}
Now that I have the variable the output is [ListItem, ListItem, ListItem, ListItem]
So I can use a .length and get a return of 4.
So now I know I have 4 rows of data so based on my wants I need 4 arrays.
Small interjection here, Not a coder by trade but code as a precursor to wants becoming needs.
A buddy of mine who is a JS coder by trade showed me this code which does work. With the logger added by me.
for (var i in listItems) {
if (listItems.hasOwnProperty(i)) {
item = listItems[i];
for (var x = 0; x < columnList.length; x++) {
attrib = item.getValueByName(columnList[x]);
Logger.log("Logging value of get list page get value by name = " + columnList[x] + " " + attrib);
}
}
}
Which brings the total code to
var name = page.getName();
var listItems = page.getListItems();
var listCount = listItems.length
var listList = [];
var columns = page.getColumns();
var name = columns[0].getName();
var item, attrib = 0;
var columnList = [];
Logger.log(listItems);
Logger.log(name + " was last updated " + page.getLastUpdated());
Logger.log(name + " was last edited " + page.getLastEdited());
var listCount = 0;
//Get Column Names
for (var j in columns) {
var cName =columns[j].getName();
columnList.push(cName);
}
Logger.log(columnList);
// Get index of Due Date
var dueDateValue = columnList.indexOf("Due Date");
Logger.log("The index of due date is " + dueDateValue);
for (var i in listItems) {
if (listItems.hasOwnProperty(i)) {
item = listItems[i];
for (var x = 0; x < columnList.length; x++) {
attrib = item.getValueByName(columnList[x]);
Logger.log("Logging value of get list page get value by name = " + columnList[x] + " " + attrib);
}
}
}
}`
Forgive the above code as it has been a bit of a sketch pad trying to work this out.
I am a bit behind on understanding what is happening here
for (var i in items) { // This is for each item in the items array
if (items.hasOwnProperty(i)) {
if items is an array, how can we use has own property? Doesn't that belong to an object? Does an array become an object?
My questions are two category fold.
Category # 1
What is happening with the hasOwnProperty?
-Does the array become an object and thus can be passed to .hasOwnProperty value
Category # 2
Is this the only way to take the values from the listitem and populate an array
- If it is, is there some way to delimit so I can pass each row into it's own array
- If it isn't , why does it work with the hasOwnProperty and why doesn't it work without it in the example below
for (var i in listItems) {
for (var y = 0; y < columnList.length; y++) {
item = listItems[i];
listList = item.getValueByName(columnList[x]);
Logger.log("Logging my version of list naming " + listList);
}
In which I get a "Invalid argument: name (line 41" response. Highlighting the
listList = item.getValueByName(columnList[x]);
Not looking for a handout but I am looking to understand the hasOwnPropertyValue further.
My current understanding is that hasOwnValue has to do with prototyping ( vague understanding ) which doesn't seem to be the case in this instance
and it has to depend on a object which I described by confusion earlier.
To clarify my want:
I would like to have each row of listitems in its own array so I can compare an index value and sort by date as my current column headers are
["Project", "Start Date" , "End Date"]
Any and all help is much appreciated for this JS beginner of 2 weeks.
An array can be inside of an object as the value of a member:
{"myFirstArray":"[one,two,blue]"}
The above object has one member, a name/value pair, where the value of the member is an array.
Here is a link to a website that explains JSON.
Link To JSON.org
JSON explained by Mozilla
There are websites that will test the validity of an object:
Link to JSONLint.com
An array has elements, and elements in an array can be other arrays. So, there can be arrays inside of arrays.
.hasOwnProperty returns either true or false.
Documentation hasOwnProperty
Interestingly, I can use the hasOwnProperty method in Apps Script on an array, without an error being produced:
function testHasProp() {
var anArrayTest = [];
anArrayTest = ['one', 'two', 'blue'];
Logger.log(anArrayTest);
var whatIsTheResult = anArrayTest.hasOwnProperty('one');
Logger.log(whatIsTheResult);
Logger.log(anArrayTest);
}
The result will always be false. Using the hasOwnProperty method on an array doesn't change the array to an object, and it's an incorrect way of using Javascript which is returning false.
You could put your list values an object instead of an array. An advantage to an object is being able to reference a value by it's property name regardless of where the property is indexed. With an array, you need to know what the index number is to retrieve a specific element.
Here is a post that deals with adding properties to an object in JavaScript:
StackOverflow Link
You can either use dot notation:
objName.newProperty = 'newvalue';
or brackets
objName["newProperty"] = 'newvalue';
To add a new name/value pair (property) to an object.

JavaScript stop referencing object after pass it to a function

I know JavaScript passes Objects by reference and thus I'm having a lot of trouble with the following code:
function doGradeAssignmentContent(dtos) {
var x = 5;
var allPages = [];
var stage = new App.UI.PopUpDisplay.PopUpStageAssignmentGrader(null, that);// pass launch element
for(var i = 0; i < dtos[0].result.students.length; ++i) {
var pagesSet = [];
for(var j = 0; j < dtos[0].result.questions.length; ++j) {
var questionObject = jQuery.extend(true, {}, new Object());
questionObject = dtos[0].result.questions[j];
if(dtos[0].result.students[i].answers[j].assignmentQuestionId === questionObject.questionId) {// expected, if not here something is wrong
questionObject.answer = dtos[0].result.students[i].answers[j].studentAnswer;
questionObject.pointsReceived = dtos[0].result.students[i].answers[j].pointsReceived;
} else {
var theAnswer = findAssociatedStudentAnswer(questionObject.questionId, dtos[0].result.students[i].answers[j]);
if(theAnswer !== null) {
questionObject.answer = theAnswer.studentAnswer;
questionObject.pointsReceived = theAnswer.pointsReceived;
} else {
alert("Unexpected error. Please refresh and try again.");
}
}
pagesSet[pagesSet.length] = new App.UI.PopUpDisplay.StageAssignmentGradingPages[dtos[0].result.questions[j].questionType.charAt(0).toUpperCase() + dtos[0].result.questions[j].questionType.slice(1) + "QuestionAssignmentGradingPage"](j + 1, questionObject);
}
var studentInfo = {};
studentInfo.avatar = dtos[0].result.students[i].avatar;
studentInfo.displayName = dtos[0].result.students[i].displayName;
stage.addPageSet(pagesSet, studentInfo);
}
stage.launch();
}
First let me show you what the result (dtos) looks like so you can better understand how this function is parsing it:
The result (dtos) is an Object and looks something like:
dtos Array
dtos[0], static always here
dtos[0].result, static always here
dtos[0].questions Array
dtos[0].questions.index0 - indexN. This describes our Questions, each one is an Object
dtos[0].students Array
dtos[0].students[0]-[n].answers Array. Each student array/Object has an Answers array. Each student will have as many elements in this answers Array that there were questions in dtos[0].questions. Each element is an Object
Now what we do in this here is create this Object stage. Important things here are it has an array called "this.studentsPages". This array will ultimately have as many entries as there were students in dtos[0].students.
So we loop through this for loop disecting the dtos array and creating a pagesSet array. Here comes my problem. On the first iteration through the for loop I create this questionObject element. I also have tried just doing var questionObject = {}, but what you see now was just an attempt to fix the problem I was seeing, but it didn't work either.
So at the end of the first iteration of the outer for loop I call stage.addPageSet, this is what happens here:
var pageObject = [];
pageObject["questions"] = pageSet;
pageObject["displayName"] = studentInfo.displayName;
this.studentsPages[this.studentsPages.length] = pageObject;
if(this.studentsPages.length === 1) {// first time only
for(var i = 0; i < pageSet.length; ++i) {
this.addPage(pageSet[i]);
}
}
The important thing to take notice of here is where I add pageObject on to this.studentsPages which was an empty array before the first call. pageObject now has pageSet plus a little bit more information. Remember, pageSet was an Object and thus passed by reference.
On the next iteration of the for loop, when I hit this line:
questionObject.answer = dtos[0].result.students[i].answers[j].studentAnswer;
It goes wrong. This changes the local copy of questionObject, BUT it also changes the copy of questionObjec that was passed to addPageSet and added to the studentsPages array in the first iteration. So, if I only had 2 students coming in, then when all is said and done, studentsPages hold 2 identical Objects. This should not be true.
The problem is questionObject in the doGradeAssignmentContent function is keeping a reference to the Object created on the previous iteration and then overrides it on all subsequent iterations.
What can I do to fix this?
Thanks for the help!
With out having looked at it too closely I believe you need to change the following:
// Before:
var questionObject = jQuery.extend(true, {}, new Object());
questionObject = dtos[0].result.questions[j];
// After:
var questionObject = jQuery.extend(true, {}, dtos[0].result.questions[j]);
I didn't look too closely if there are other instances in the code where this needs to be applied, but the core concept is to utilize jQuery's deep copy to generate a duplicate of the object you do not wish to retain a reference to.

won't loop array javascript

Why cant i access my array?
function map(array){
for(i=0; i <=array.length; i++){
var location=array[i].location;
console.log("loc"+location);
var user = array[i].from_user;
console.log("user"+user);
var date = array[i].created_at;
var profile_img = array[i].profile_img;
var text = array[i].text;
var contentString = text;
//geocode(user,date, profile_img, text, contentString,location);
}
}
It gives me undefined for every element.I want to access it and pass the variables to the geocode function.
data structure:
array=[{user: a,user_id: b,date: c,profile_img: d,text: e,contentString: f,url:
g,location:o},{user: a,user_id: b,date: c,profile_img: d,text: e,contentString:
f,url:g,location:o},{user: a,user_id: b,date: c,profile_img: d,text:
e,contentString: f,url: g,location:s}];
dont worry about the values..!
I forgot to mention when i first made the post(question). the location of the array is inserted in the previous function whereas the array didn't include the attribute location from previous functions
When calling the function, use the object literal construct enclosed in an array literal, otherwise all values will be returned as undefined. This is how you should call your function:
map([{ // array literal enclosing an object literal
location : 1,
from_user : 2,
created_at : 3,
profile_img: 4,
text : 5
}]);
Moreover, in your loop, change:
for (i = 0; i <= array.length; i++ ) ...
...to
for (var i = 0; i < array.length; i++) ...
If you have a pre-defined array, name it and pass it to the function like this:
map(arrayObj)
If the array you pass in has a length of 0, the way you're looping it is going to try and access the list element at 0, which is going to be undefined.
However, regardless of the contents of your array, this line will always cause you trouble:
for(i=0; i <=array.length; i++)
When check the length property of your array, it is telling you the number of elements in the array. Since arrays use 0 based indexing, you're going to overrun the bounds of your array with this loop everytime.
var myArray = [1, 2, 3];
myArrary[0]; // This is 1
myArray[2]; // This is 3
Since you are looping between 0 and the length of the array, which happens to be 3, the last element you attempt to access will no exist.
myArray[3]; // Undefined
You need to check i < array.length rather than i <= array.length.
Your code is fine. With the provided input and function you get this:
loco
userundefined
loco
userundefined
locs
userundefined
The reason every user is coming up as undefined is because of:
var user = array[i].from_user;
console.log("user"+user);
The objects you are passing in do not have a from_user property, so naturally it comes up as undefined. Maybe you meant array[i].user_id?
Also, as Aesthete pointed out, you're running outside the bounds of your array because of the way you're checking for length. Do this instead:
for(var i = 0, n = array.length; i < n; i++) {
// your code in here
}
Notice that I preface i with var so it does not become an implicit global. Also, I declare a second variable n so that you only need to access array.length once. This is common practice.
So, putting it all together:
function map(array){
for(var i = 0, n = array.length; i < n; i++){
var location=array[i].location;
console.log("loc"+location);
var user = array[i].user_id;
console.log("user"+user);
var date = array[i].created_at;
var profile_img = array[i].profile_img;
var text = array[i].text;
var contentString = text;
//geocode(user,date, profile_img, text, contentString,location);
}
}
array=[{user: 'a',user_id: 'b',date: 'c',profile_img: 'd',text: 'e',contentString: 'f',url:
'g',location:'o'},{user: 'a',user_id: 'b',date: 'c',profile_img: 'd',text: 'e',contentString:
'f',url:'g',location:'o'},{user: 'a',user_id: 'b',date: 'c',profile_img: 'd',text:
'e',contentString: 'f',url: 'g',location:'s'}];
map(array);
Notice I changed your object properties to strings - this is because you did not give values for these, but you probably don't want to do this. Output is:
loco
userb
loco
userb
locs
userb
All is well. If you are still getting undefined for location then your error must lie with the o property of the objects you're passing in.

Categories

Resources