Get unique objects from array based on single attribute - javascript

Let's say we have the following:
node[1].name = "apple";
node[1].color = "red";
node[2].name = "cherry";
node[2].color = "red";
node[3].name = "apple";
node[3].color = "green";
node[4].name = "orange";
node[4].color = "orange;
if I use jQuery.unique(node) I will get all the original nodes because they all have a different name OR color. What I want to do is only get the nodes with a unique name, which should return
node[1] (apple)
node[2] (cherry)
node[4] (orange)
It should not return 3 because it is the same fruit, even though we have green and red apples.

Use Array.filter and a temporary array to store the dups:
function filterByName(arr) {
var f = []
return arr.filter(function(n) {
return f.indexOf(n.name) == -1 && f.push(n.name)
})
}
Demo: http://jsfiddle.net/mbest/D6aLV/6/

This approach (forked from #David's) should have better performance for large inputs (because object[] is O(1)).
function filter(arr, attribute) {
var out = [],
seen = {}
return arr.filter(function(n) {
return (seen[n[attribute]] == undefined)
&& (seen[n[attribute]] = 1);
})
}
console.log(filter(node, 'name'));
http://jsfiddle.net/LEBBB/1/

What about doing it like this?
var fruitNames = [];
$.each($.unique(fruits), function(i, fruit) {
if (fruitNames.indexOf(fruit.name) == -1) {
fruitNames.push(fruit.name);
$('#output').append('<div>' + fruit.name + '</div>');
}
});
Here is a working fiddle.
Obviously, instead of output.append I could just add the current fruit to uniqueFruit[] or something.

Related

Getting value of first index within jQuery array using .each()

I have a JSON array returned via ajax that looks like:
"stuff": [["2","66%"], ["3","42%"],...
Problem
I want to match the zeroth index of each element in this array to a variable outside of the loop and if it matches, I want to return the percentage next to it.
I don't know the syntax in jQuery for this. Please have a look at my code below and see if that's correct or not:
var percentage = 0;
var stuffarr = jsonobj['stuff'];
var stuffID = jsonobj['stuff_id']
if (!stuffID || 0 === stuffID.length){
$("#stuff-element").html("--");
}
else {
var percentage = $.each(stuffarr, function (index, value) {
if(value[0] == stuffID)
return value[1]
});
}
Firstly, a bit of terminology. The data structure you have is an Object which holds several properties. It has nothing to do with JSON after it has been deserialised.
With regard to your issue, there's no jQuery required as you can use find() to find the item in the array by the stuffID variable's value. Try this:
var obj = {
"stuff": [
["2", "66%"],
["3", "42%"]
],
"stuff_id": "3"
}
var percentage = 0;
var stuffArr = obj['stuff'];
var stuffId = obj['stuff_id']
if (!stuffId || 0 === stuffId.length) {
$("#stuff-element").html("--");
} else {
percentage = stuffArr.find(function(el) {
return el[0] == stuffId;
})[1];
}
console.log(percentage);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
try this
var percentage = stuffarr.find(function (value) {
if(value[0] == stuffID)
return value[1];
})[1];
The return statement you used returns the result of the function(..) inside the .each(..) function and is not the return value of the .each(..) function.
the valid return value is boolean which 'tells' the .each(..) function whether it should continue or not. instead use the following syntax:
var ret = '';
$(arr).each(function () {
var curr = $(this);
//console.log(curr);
if(curr[0] == stuffID){
//console.log('>>found: ' + curr[1]);
ret = curr[1];
//if found -> break loop
return false;
}
});
comment: you should consider instead of using an inner array data structure use an object, this is a more 'correct' data structure:
// "stuff": [{"id":"2","percent":"66%"}, {"id":"3","percent":"42%"},...
var ret = '';
$(arr).each(function () {
var curr = this;
//console.log(curr.id);
if(curr.id == stuffID){
//console.log('>>found: ' + curr.percent);
ret = curr.percent;
//if found -> break loop
return false;
}
});
#LiverpoolOwen approach is clean and nice, and if you want to use it combining with the object approach do this:
arr.find(function (value) {
if(value.id == stuffID)
return value;
}).percent;

javascript - sorting array by order of another array

My goal is to sort this div
<div id="myDiv">3xOrange;2xBlue;1xRed;1xRed;1xRed;1xOrange;2xBlue;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;2xOrange;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;20xBlue;33xRed;20xBlue;33xRed;2xBlue;3xRed;51xBlue;51xRed;</div>
by another div in this order
<div id="array"> Blue: 1,Red: 2,Orange: 3, </div>
So my Wanted result is to get result like this
2xBlue;1xBlue;1xBlue;2xBlue;3xRed;3xRed;1xRed;1xRed;2xOrange;3xOrange ......
I aware for the first div needs to be used string split something like this .split('x')[1];
So far I have this code:
var init_arr;
var scorer;
window.onload=function() {
scorer=document.getElementById("array").innerHTML;
init_arr = document.getElementById("myDiv").innerHTML;
var final_arr = init_arr.sort(function(a,b) {
return scorer[a]-scorer[b];
});
}
alert(final_arr);
but getting error TypeError: init_arr.sort is not a function I guess init_arr and scorer are objects not strings
Please Help
This answer deletes the rest of the strings with ; or ,, treats array like a part of a JSON string, and sort with the part after the x.
window.onload = function() {
var init_arr = document.getElementById("myDiv").innerHTML.split(';'),
scorer = JSON.parse('{' + document.getElementById("array").innerHTML + '}');
init_arr.sort(function(a, b) {
var aa = a.split('x')[1],
bb = b.split('x')[1];
return scorer[aa] - scorer[bb];
});
alert(init_arr);
};
<div id="myDiv">3xOrange;2xBlue;1xRed;1xRed;1xRed;1xOrange;2xBlue;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;2xOrange;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;20xBlue;33xRed;20xBlue;33xRed;2xBlue;3xRed;51xBlue;51xRed</div>
<div id="array">"Blue": 1,"Red": 2,"Orange": 3</div>
But I really suggest to use real arrays for data and objects for sorting order. And not any parts of HTML code.
Well, I felt dummy after playing around to help you after seeing the first answer, but here it goes.
<div id="myDiv">3xOrange;2xBlue;1xRed;1xRed;1xRed;1xOrange;2xBlue;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;2xOrange;3xRed;1xBlue;1xRed;2xBlue;3xRed;1xBlue;1xRed;20xBlue;33xRed;20xBlue;33xRed;2xBlue;3xRed;51xBlue;51xRed;</div>
<div id="array"></div>
var init;
var final;
var scorer;
scorer = document.getElementById("array");
init = document.getElementById("myDiv");
init = init.textContent.split(/\;/);
init = init.filter(function(item) {
return item.length > 0;
})
.map(function(item) {
item = item.split(/x/);
var obj = {
color: item[1],
amount: parseInt(item[0])
}
return obj;
});
final = init.reduce(function(scored, item) {
if(scored[item.color] === undefined) {
scored[item.color] = 0;
}
scored[item.color] += item.amount;
return scored;
}, {});
final = Object.keys(final)
.sort(function(item1, item2) {
return final[item1].amount - final[item2].amount;
})
.map(function(key) {
return key + ' :' + final[key];
});
scorer.textContent = final.join(', ');
At least it was funny to play with map, filter, reduce and sort
This is the sort of thing you could do:
function sort() {
var scorer;
var scorerLookup;
var sortedLookup;
//First we figure out the sort order
scorer = document.getElementById("array").innerHTML.split(',');
scorer.sort(function(a, b) {
aVal = parseInt(a.split(':')[1].trim());
bVal = parseInt(b.split(':')[1].trim());
return aVal - bVal;
});
console.log(scorer);
//Now put the sort order into an object so we can easily lookup values
scorerLookup = {};
for (var i = 0; i < scorer.length; i++) {
var tempVal = scorer[i].split(':');
scorerLookup[tempVal[0].trim()] = parseInt(tempVal[1].trim());
}
console.log(scorerLookup);
//Now sort the main list
init_arr = document.getElementById("myDiv").innerHTML.split(';');
init_arr.sort(function(a, b) {
aVal = scorerLookup[a.split('x')[1]];
bVal = scorerLookup[b.split('x')[1]];
return aVal - bVal;
});
console.log(init_arr);
}
window.onload=sort();
It needs more error trapping really (for blank values, etc) - but it should give you the general idea.

Is there a way to pull all select menu’s selected index in my html to add values to different arrays of my choice. A lot of code for 1 select menu

The function finds which tv character the user compares to based on their answers to my questions. My code now is very inefficient for multiple select menus!!! Maybe an object that takes all selectmenus in html and allows me to assign array values based on the selected index of a selectmenu.
function onSelectMenuBlur() {
"use strict";
/*list of arrays that will be added to when the user selects an option in a selectmenu.*/
var rickArray = [];
var shaneArray = [];
var bobArray = [];
var carolArray = [];
var lArray = [];
var sm = document.getElementById("selectmenu");
.onchange function that determines what array will be added to depending on the option selected in the select menu. This function will add an array value of 1 once to an array. Seems like an inefficient way, especially with multiple selectmenus!
sm.onchange = function() {
if(sm.selectedIndex + 1 === 1) {
rickArray.push(1);
shaneArray.pop();
bobArray.pop();
carolArray.pop();
lArray.pop();
alert(rickArray.length);
}
else if(sm.selectedIndex + 1 === 2) {
shaneArray.push(1);
rickArray.pop();
bobArray.pop();
carolArray.pop();
lArray.pop();
alert(shaneArray.length);
}
else if(sm.selectedIndex + 1 === 3) {
bobArray.push(1);
rickArray.pop();
shaneArray.pop();
carolArray.pop();
lArray.pop();
alert(bobArray.length);
}
else if(sm.selectedIndex + 1 === 4) {
carolArray.push(1);
rickArray.pop();
shaneArray.pop();
bobArray.pop();
lArray.pop();
alert(carolArray.length);
}
else if(sm.selectedIndex + 1 === 5) {
lArray.push(1);
rickArray.pop();
shaneArray.pop();
bobArray.pop();
carolArray.pop();
alert(lArray.length);
}
else{}
};
.onblur purpose to find array with biggest length or value out of all selectmenus to determine which person associated with the array the user is like. Again seems like an inefficient way to handle!
sm.onblur = function() {
var rickL = rickArray.length;
var shaneL = shaneArray.length;
var bobL = bobArray.length;
var carolL = carolArray.length;
var lL = lArray.length;
// unfinished if else statement !!
if(rickL > shaneL && rickL > bobL && rickL > carolL && rickL > lL) {
alert("you are Rick Grimes");
}
else{
alert("you are someone else");
}
};
}
Use a 2-dimensional array instead of separate arrays for each character, and then use the selected index as an index into the array.
var characters = [[], [], [], [], []];
sm.onchange = function() {
for (var i = 0; i < characters.length; i++) {
if (i == this.selectedIndex) {
characters[i].push(1);
alert(characters[i].length);
} else {
characters[i].pop();
}
}
};
To get the character names in there, make it an array of objects.
characters = [
{ name: "Rick",
array: []
},
{ name: "Carol",
array: []
},
...
}
Then you would use characters[i].array.push(1). And then when you want to say which character they are, find the object with the longest array and then print its .name.

Array text to numbers, find matching values and sort

I have an array which looks like this:
["1,8", "4,6,8", "8,9", "6,9"]
1/ I would like to turn it in to this
[1,8,4,6,8,8,9,6,9]
2/ I would then like to find matching values, by looking for the most number:
[8]
This first has been solved with this:
var carArray = ["1,8", "4,6,8,7,7,7,7", "8,9", "6,9"];
//1) create single array
var arr = carArray.join().split(',');
//2) find most occurring
var counts = {}; //object to hold count for each occurence
var max = 0, maxOccurring;
arr.forEach(function(el){
var cnt = (counts[el] || 0); //previous count
counts[el] = ++cnt;
if(cnt > max && cnt > 1){ //only register if more than once (cnt>1)
max=cnt;
maxOccurring = el;
}
});
if(maxOccurring){
//there was an element more than once, maxOccuring contains that element
setResult('Most occuring: ' + maxOccurring + ' (' + max + ' times)');
}
else{
//3)/4) ???
setResult('sorting?');
}
//below is only for test display purposes
function setResult(res){
console.log(res);
}
3/ If the are no matching values like this
[1,8,4,6,5,7]
4/ Then I need to compare this array to another array, such as this
[6,7,4,1,2,8,9,5]
If the first number in <4> array above appears in <3> array, then get that number, ie in the above example I need to get 6. The <4> array will be static values and not change. The numbers is <3> will be dynamic.
EDIT Not the most elegant of answers, but I do have something working now. I didn't compare the original array directly with the second array, instead used simple if/else statements to do what I needed:
var carArray = ["1,5", "4", "8,2", "3,9,1,1,1"];
//1) create single array
var arr = carArray.join().split(',');
//2) find most occurring
var counts = {}; //object to hold count for each occurence
var max = 0, maxOccurring;
arr.forEach(function(el){
var cnt = (counts[el] || 0); //previous count
counts[el] = ++cnt;
if(cnt > max && cnt > 1){ //only register if more than once (cnt>1)
max=cnt;
maxOccurring = el;
}
});
if(maxOccurring){
//there was an element more than once, maxOccuring contains that element
console.log('Most occuring: ' + maxOccurring + ' (' + max + ' times)');
console.log(maxOccurring);
}
else {
// If not occuring, match from a list
if(jQuery.inArray("6", arr) !== -1) { console.log('6'); }
else if(jQuery.inArray("9", arr) !== -1) { console.log('9'); }
else if(jQuery.inArray("7", arr) !== -1) { console.log('7'); }
else if(jQuery.inArray("5", arr) !== -1) { console.log('5'); }
else if(jQuery.inArray("4", arr) !== -1) { console.log('4'); }
else if(jQuery.inArray("1", arr) !== -1) { console.log('1'); }
else { console.log('not found'); }
}
Example Fiddle
Step 1 is fairly easy by using javascript's join and split methods respectively:
var arr = carArray .join().split(',');
For step 2, several methods can be used, the most common one using an object and using the elements themselves as properties. Since you only need to get the most occurring value if there is a reoccurring value, it can be used in the same loop:
var counts = {}; //object to hold count for each occurence
var max = 0, maxOccurring;
arr.forEach(function(el){
var cnt = (counts[el] || 0); //previous count
counts[el] = ++cnt;
if(cnt > max && cnt > 1){ //only register if more than once (cnt>1)
max=cnt;
maxOccurring = el;
}
});
After the above, the variable maxOccurring will contain the reoccurring value (if any) and max will contain the times it occured
For step 4 the easiest way is to loop through the compare array and get the element that occurs in the input array:
var cmpArr = ['6','7','4','1','2','8','9','5'];
//find the first occurrence inside the cmpArr
res = function(){ for(var i= 0 ; i < cmpArr.length; i++){ if(arr.indexOf(cmpArr[i]) !== -1)return cmpArr[i];}}();
The above uses an in place function which is called immediately to be able to use return. You could also just use a loop and assign res when found, then break from the loop.
Last update, an alternate fiddle where the above is converted to a single function: http://jsfiddle.net/v9hhsdny/5/
Well first of all the following code results in four matching answers since the jQuery selectors are the same.
var questionAnswer1 = $(this).find('input[name=questionText]').val();
var questionAnswer2 = $(this).find('input[name=questionText]').val();
var questionAnswer3 = $(this).find('input[name=questionText]').val();
var questionAnswer4 = $(this).find('input[name=questionText]').val();
var carArray = [questionAnswer1, questionAnswer2, questionAnswer3, questionAnswer4];
You could use the eq(index) method of jQuery to select the appropriate element. However having 4 inputs with the same name is a bad practice.
Well lets say that the carArray has 4 different values which all consist out of comma separated numbers. You could then do the following:
var newArr = [];
carArray.forEach(function(e) {
e.split(",").forEach(function(n) {
newArr.push(n);
});
});
Well then we got to find the most occurring number. JavaScript doesn't have any functions for that so we will have to find an algorithm for that. I found the following algorithm on this stackoverflow page
var count = function(ary, classifier) {
return ary.reduce(function(counter, item) {
var p = (classifier || String)(item);
counter[p] = counter.hasOwnProperty(p) ? counter[p] + 1 : 1;
return counter;
}, {})
}
var occurances = count(newArr);
It isn't clear to me what you're trying to do in step 3 and 4, so can't answer those at the moment.
var ary = ["1,8", "4,6,8", "8,9", "6,9"];
var splitted = ary.reduce(function(acc, item) {
return acc.concat(item.split(','));
}, []);
var occurences = splitted.reduce(function(acc, item) {
if (!acc.hasOwnProperty(item)) acc[item] = 0;
acc[item] += 1;
return acc;
},{}),
biggest = Object.keys(occurences).reduce(function (acc, key) {
if (occurences[key] > acc.occurences) {
acc.name = key;
acc.occurences = occurences[key];
}
return acc;
},{'name':'none','occurences':0}).name;
var vals=["1,8", "4,6,8", "8,9", "6,9"];
// 1) turn into number array
var arrNew=[];
for(var i=0; i<vals.length; i++)
{
arrLine=vals[i].split(",");
for (var j=0;j<arrLine.length;j++) { arrNew.push (parseInt(arrLine[j])) }
}
//result:
alert(arrNew.join(";");
// 2) find most common
var found=[];
for(var i=0; i<arrNew.length; i++) {
// make an array of the number of occurrances of each value
if (found["num"+newArray[i]]) {
found["num"+newArray[i]] ++ ;
} else {
found["num"+newArray[i]]=1;
}
}
var mostCommon={count:0,val:"ROGUE"};
for (x in found) {
if (found[x] > mostCommon.count) {
mostCommon.count=found[x].count;
mostCommon.val=x;
}
}
// result :
alert(mostCommon.val);
//3) not quite sure what you meant there
// 4) unique values:
// at this point the 'found' list contains unique vals
var arrUnique=[];
for (x in found) {
arrUnique.push[x];
}
// result :
alert(arrUnique.join(";"))
//sort:
arrUnique.sort(function(a, b){return a-b});
(This won't work in most browsers) but on a side note, when ES6 becomes widely supported, your solution could look like this:
var arr1 = ["1,8", "4,6,8", "8,9", "6,9"];
var arr2 = arr1.join().split(',');
var s = Array.from(new Set(arr2)); //Array populated by unique values, ["1", "8", "4", "6", "9"]
Thought you might like to see a glimpse of the future!
1.
var orgArray = ['1,8', '4,6,8', '8,9', '6,9'];
var newArray = [];
for (var i in orgArray) {
var tmpArray = orgArray[i].split(',');
for (var j in tmpArray) {
newArray.push(Number(tmpArray[j]));
}
}
2.
var counts = {};
var most = null;
for (var i in newArray) {
var num = newArray[i];
if (typeof counts[num] === 'undefined') {
counts[num] = 1;
} else {
++(counts[num]);
}
if (most == null || counts[num] > counts[most]) {
most = num;
} else if (most != null && counts[num] === counts[most]) {
most = null;
}
}
I don't understand the question 3 and 4 (what "unique order" means) so I can't answer those questions.

count total duplicates in js array

I'm looping through all classnames in my html body.
I'd like to store the classname with textSize value. Each time there is a duplicate value for a given classname, I want to increment its textSize.
$("*").each(function() {
classname = $(this).get(0).className;
myarray.push({"className" : classname, "textSize" : 5});
Here, I attempt to sort the classnames, then get a count for each duplicate:
myarray.sort();
var current = null;
var dupCount = 0;
for (var i = 0; i < myarray.length-1; i++) {
if (myarray[i]["className"] !== "") {
if (myarray.indexOf(myarray[i]["className"]) == -1) {
log(myarray[i]["className"]);
}
else {
log("DUP");
myarray[i]["textSize"] = myarray[i]["textSize"] += 5;
dupCount++;
}
}
}
log(myarray[i]["className"]);, shown in the image below, clearly shows duplicates:
Yet, log("DUP"); is never called once. Why is that?
Moreover, why doesn't myarray.sort(); sort them alphabetically? If it did that, I could just do if (myarray[i]["className"] === myarray[i++]["className"]) { to check if the value equals the next value in the array. But sort doesn't work.
Edit:
So when looping through, I should be able to alter the css per classname, right?
for(var classname in classes) {
console.log(classes[classname].textSize);
var $val = $(classes[classname]);
$val.css({
"color" : "blue",
"fontSize": $val.textSize+"px"
});
}
This doesn't work even though console.log(classes[classname].textSize); gives text sizes per element
Try using an object instead of an array, using class names as the keys:
var classes = {};
$("*").each(function() {
var classname = $(this).get(0).className;
var c = classes[classname] ||
(classes[classname] = { className: classname, textSize: 0 });
c.textSize += 5;
});
for(var classname in classes) {
console.log(classes[classname]);
}
Remember that any element can have multiple classes. If you want to account for that, you'll have to split up the class names:
$("*").each(function() {
var classnames = $(this).get(0).className.split(' ');
for(var i=0; i<classnames.length; i++) {
var classname = classnames[i];
var c = classes[classname] ||
(classes[classname] = { className: classname, textSize: 0 });
c.textSize += 5;
}
});
See this demonstration: http://jsfiddle.net/FSBhv/
UPDATE: the OP clarified that he wants to set the text size on the elements based on the number of elements that have that class. Doing that will take a slightly different approach (we'll actually have to store the elements):
var eltsByClass = {};
$("*").each(function() {
var $this = $(this);
$this.get(0).className.split(' ').forEach(function(cname) {
var c = eltsByClass[cname] ||
(eltsByClass[cname] = []);
c.push($this.get(0));
});
});
for(var cname in eltsByClass) {
var elts = eltsByClass[cname];
$(elts).css('font-size', (elts.length + 1) * 5 + 'px');
}
Store your data in an object, not an Array. And it can be simplified as:
var classes = {};
$("*").each(function() {
var classname = $(this).get(0).className;
classes[classname] = (classes[classname] || 0) + 5;
});
for(var classname in classes) {
console.log(classname + " textSize:" + classes[classname]);
}
myarray.indexOf(myarray[i]["className"]) is a problem.
You know that myarray contains only objects, but you're asking for the position of a string within that list. That's never going to return anything but -1.
Consider using a second array to store the frequency count, instead of trying to do both with myarray.
var frequency = {};
myarray.forEach(function (i) {
frequency[i.className] = frequency[i.className] || 0
frequency[i.className]++;
}
This will hely you to sort array alphabetically based on classname
var myarray=[];
$("*").each(function()
{
classname = $(this).get(0).className;
myarray.push({"className" : classname, "textSize" : 5});
});
function compare(a,b)
{
if (a.className < b.className)
return -1;
if (a.className > b.className)
return 1;
return 0;
}
myarray.sort(compare);

Categories