I have this array:
var markers = [
{
"type":"Chocolate",
"name":"KitKat",
"group":"candy",
"icon":"candy",
"coords":[5246,8980],
},
{
"type":"Fruit",
"name":"Orange",
"group":"fruits",
"icon":"fruis",
"coords":[9012,5493],
},
{
"type":"Fruit",
"name":"Banana",
"group":"fruits",
"icon":"fruis",
"coords":[9012,5493],
},
{
"type":"Food",
"name":"Rice",
"group":"foods",
"icon":"foods",
"coords":[6724,9556],
},
{
"type":"Food",
"name":"Meat",
"group":"foods",
"icon":"foods",
"coords":[6724,9556],
},
{
"type":"Food",
"name":"Beam",
"group":"foods",
"icon":"foods",
"coords":[6724,9556],
},
{
"type":"Liquid",
"name":"Water",
"group":"liquids",
"icon":"liquids",
"coords":[6724,9556],
},
{
"type":"Liquid",
"name":"Coffe",
"group":"liquids",
"icon":"liquids",
"coords":[6724,9556],
},
]
Which I want to count how many items each group has in this array.
I managed to count it with this:
var count = []
for (var i = 0; i < markers.length; i++) {
count[markers[i].group] = count[markers[i].group] + 1 || 1 ;
}
Which outputs this result:
count = [
candy: 1
foods: 3
fruits: 2
liquids: 2
]
I want to use this values in another part, and for that I need to change the array structure, to something like this:
count = [
{"item": "candy","qnt":1},
{"item": "foods","qnt":3},
{"item": "fruits","qnt":2},
{"item": "liquids","qnt":2}
]
I know that I could do something like this:
var total_fruits = 0;
for (var i = 0; i < markers.length; i++) {
if (markers[i].group == "fruits"){
total_fruits++
}
}
But imagine how many if's I will need for a group of more than 50 types...
I will use the values in the html part with the same class that the item value is like this:
<ul>
<li class="candy">
<span class="qnt">1</span>
</li>
<li class="fruits">
<span class="qnt">2</span>
</li>
<li class="foods">
<span class="qnt">3</span>
</li>
<li class="liquids">
<span class="qnt">2</span>
</li>
</ul>
Any suggestions or how to improve this?
I have this array:
var markers = [
{
"type":"Chocolate",
"name":"KitKat",
"group":"candy",
"icon":"candy",
"coords":[5246,8980],
},
{
"type":"Fruit",
"name":"Orange",
"group":"fruits",
"icon":"fruis",
"coords":[9012,5493],
},
{
"type":"Fruit",
"name":"Banana",
"group":"fruits",
"icon":"fruis",
"coords":[9012,5493],
},
{
"type":"Food",
"name":"Rice",
"group":"foods",
"icon":"foods",
"coords":[6724,9556],
},
{
"type":"Food",
"name":"Meat",
"group":"foods",
"icon":"foods",
"coords":[6724,9556],
},
{
"type":"Food",
"name":"Beam",
"group":"foods",
"icon":"foods",
"coords":[6724,9556],
},
{
"type":"Liquid",
"name":"Water",
"group":"liquids",
"icon":"liquids",
"coords":[6724,9556],
},
{
"type":"Liquid",
"name":"Coffe",
"group":"liquids",
"icon":"liquids",
"coords":[6724,9556],
},
]
Which I want to count how many items each group has in this array.
I managed to count it with this:
var count = []
for (var i = 0; i < markers.length; i++) {
count[markers[i].group] = count[markers[i].group] + 1 || 1 ;
}
Which outputs this result:
count = [
candy: 1
foods: 3
fruits: 2
liquids: 2
]
I want to use this values in another part, and for that I need to change the array structure, to something like this:
count = [
{"item": "candy","qnt":1},
{"item": "foods","qnt":3},
{"item": "fruits","qnt":2},
{"item": "liquids","qnt":2}
]
I know that I could do something like this:
var total_fruits = 0;
for (var i = 0; i < markers.length; i++) {
if (markers[i].group == "fruits"){
total_fruits++
}
}
But imagine how many if's I will need for a group of more than 50 types...
I will use the values in the html part with the same class that the item value is like this:
<ul>
<li class="candy">
<span class="qnt">1</span>
</li>
<li class="fruits">
<span class="qnt">2</span>
</li>
<li class="foods">
<span class="qnt">3</span>
</li>
<li class="liquids">
<span class="qnt">2</span>
</li>
</ul>
Any suggestions or how to improve this?
Share Improve this question asked Nov 28, 2018 at 17:40 RogerHNRogerHN 6141 gold badge13 silver badges31 bronze badges 3- In your original code, you are using an array as an object which works but it's wrong. – ibrahim mahrir Commented Nov 28, 2018 at 17:44
- @ibrahimmahrir Isn't that what they already have (barring the incorrect use of array type)? – Heretic Monkey Commented Nov 28, 2018 at 17:46
- It would possibly be easiest to do what your already doing, and then take that resulting object, and convert it into the list of objects like you want. So you don't have to find the element in the array for your group each time – Taplar Commented Nov 28, 2018 at 17:47
9 Answers
Reset to default 4You can build the object you want in one step with reduce()
. This will build an object keyed to group
. To get just the array, take the Object.values()
of that object:
var markers = [{"type":"Chocolate","name":"KitKat","group":"candy","icon":"candy","coords":[5246,8980],},{"type":"Fruit","name":"Orange","group":"fruits","icon":"fruis","coords":[9012,5493],},{"type":"Fruit","name":"Banana","group":"fruits","icon":"fruis","coords":[9012,5493],},{"type":"Food","name":"Rice","group":"foods","icon":"foods","coords":[6724,9556],},{"type":"Food","name":"Meat","group":"foods","icon":"foods","coords":[6724,9556],},{"type":"Food","name":"Beam","group":"foods","icon":"foods","coords":[6724,9556],},{"type":"Liquid","name":"Water","group":"liquids","icon":"liquids","coords":[6724,9556],},{"type":"Liquid","name":"Coffe","group":"liquids","icon":"liquids","coords":[6724,9556],},]
let counts = markers.reduce((obj, {group}) => {
if(!obj[group]) obj[group] = {"item": group, "qnt":1} // make a count item if it doesn't exist
else obj[group].qnt++ // or increment it
return obj
}, {})
console.log(Object.values(counts))
var markers = [{
"type": "Chocolate",
"name": "KitKat",
"group": "candy",
"icon": "candy",
"coords": [5246, 8980],
},
{
"type": "Fruit",
"name": "Orange",
"group": "fruits",
"icon": "fruis",
"coords": [9012, 5493],
},
{
"type": "Fruit",
"name": "Banana",
"group": "fruits",
"icon": "fruis",
"coords": [9012, 5493],
},
{
"type": "Food",
"name": "Rice",
"group": "foods",
"icon": "foods",
"coords": [6724, 9556],
},
{
"type": "Food",
"name": "Meat",
"group": "foods",
"icon": "foods",
"coords": [6724, 9556],
},
{
"type": "Food",
"name": "Beam",
"group": "foods",
"icon": "foods",
"coords": [6724, 9556],
},
{
"type": "Liquid",
"name": "Water",
"group": "liquids",
"icon": "liquids",
"coords": [6724, 9556],
},
{
"type": "Liquid",
"name": "Coffe",
"group": "liquids",
"icon": "liquids",
"coords": [6724, 9556],
}
];
var temp = markers.reduce( function ( results, marker ) {
results[ marker.group ] = ( results[ marker.group ] || 0 ) + 1;
return results;
}, {});
//now convert the object to a list like you want it
temp = Object.keys( temp ).map( function ( group ) {
return { group: group, quantity: temp[ group ] };
} );
console.log( temp );
You can do this via Array.reduce
to do the grouping by group
and then use Object.entries
with Array.map
to get the desired output format:
var data = [ { "type":"Chocolate", "name":"KitKat", "group":"candy", "icon":"candy", "coords":[5246,8980], }, { "type":"Fruit", "name":"Orange", "group":"fruits", "icon":"fruis", "coords":[9012,5493], }, { "type":"Fruit", "name":"Banana", "group":"fruits", "icon":"fruis", "coords":[9012,5493], }, { "type":"Food", "name":"Rice", "group":"foods", "icon":"foods", "coords":[6724,9556], }, { "type":"Food", "name":"Meat", "group":"foods", "icon":"foods", "coords":[6724,9556], }, { "type":"Food", "name":"Beam", "group":"foods", "icon":"foods", "coords":[6724,9556], }, { "type":"Liquid", "name":"Water", "group":"liquids", "icon":"liquids", "coords":[6724,9556], }, { "type":"Liquid", "name":"Coffe", "group":"liquids", "icon":"liquids", "coords":[6724,9556], }, ]
const group = data.reduce((r,c) => (r[c.group] = (r[c.group] || 0) + 1, r), {})
console.log(Object.entries(group).map(([k,v]) => ({ item: k, qnt: v })))
The expected output is not right.An Array cannot have key & value like that. You may need an object for that.
var markers = [{
"type": "Chocolate",
"name": "KitKat",
"group": "candy",
"icon": "candy",
"coords": [5246, 8980],
},
{
"type": "Fruit",
"name": "Orange",
"group": "fruits",
"icon": "fruis",
"coords": [9012, 5493],
},
{
"type": "Fruit",
"name": "Banana",
"group": "fruits",
"icon": "fruis",
"coords": [9012, 5493],
},
{
"type": "Food",
"name": "Rice",
"group": "foods",
"icon": "foods",
"coords": [6724, 9556],
},
{
"type": "Food",
"name": "Meat",
"group": "foods",
"icon": "foods",
"coords": [6724, 9556],
},
{
"type": "Food",
"name": "Beam",
"group": "foods",
"icon": "foods",
"coords": [6724, 9556],
},
{
"type": "Liquid",
"name": "Water",
"group": "liquids",
"icon": "liquids",
"coords": [6724, 9556],
},
{
"type": "Liquid",
"name": "Coffe",
"group": "liquids",
"icon": "liquids",
"coords": [6724, 9556],
},
]
let count = markers.reduce(function(acc, curr) {
if (acc[curr.type]) {
acc[curr.type] += 1;
} else {
acc[curr.type] = 1;
}
return acc;
}, {})
console.log(count)
/* if you need an array of objects then ,instead of object ,
pass an empty array as the accumulator. Then in that
accumulator search if the type exist using findIndex.
If it returns -1 then create a new object with
required values and push it in the accumulator,
else update the value of qnt at that specific index*/
let count1 = markers.reduce(function(acc, curr) {
let getItemIndex = acc.findIndex(function(item) {
return item.item === curr.group
});
if (getItemIndex === -1) {
let obj = {
item: curr.group,
qnt: 1
}
acc.push(obj)
} else {
acc[getItemIndex].qnt += 1;
}
return acc;
}, [])
console.log(count1)
The best way (in my opinion) is to use a format like { item: count }
which you already have except for that count
should be an object {}
instead of an array []
(see @brk's answer). If you want the output to be an array of objects, then just use a cache object (that will hold the index of the count object from the count
array):
var count = [], cache = {};
for(var i = 0; i < markers.length; i++) {
var marker = markers[i];
if(cache.hasOwnProperty(marker.type)) { // if the current marker type has already been encountered
count[cache[marker.type]].qnt++; // then just retrieve the count object and increment its 'qnt'
} else { // otherwise
cache[marker.type] = count.push({ // create a count object for this type and store its index in the cache object
type: marker.type,
qnt: 1
}) - 1; // - 1 because 'push' returns the new array length, thus the index is 'length - 1'
}
}
You can use the reduce
method to return a new array with the items and quantities.
We use a ternary statement to determine if the accumulator array already contains the group type with findIndex
. If it doesn't we push the new type with a qnt of 1, if it does we simply increment the qnt value.
markers.reduce((ms, m) => (ms.findIndex(o => o.item === m["group"]) > 0) ?
(ms[ms.findIndex(o => o.item === m["group"])]["qnt"]++, ms) :
(ms.push({ qnt: 1, item: m["group"]}), ms), []);
var markers=[{type:"Chocolate",name:"KitKat",group:"candy",icon:"candy",coords:[5246,8980]},{type:"Fruit",name:"Orange",group:"fruits",icon:"fruis",coords:[9012,5493]},{type:"Fruit",name:"Banana",group:"fruits",icon:"fruis",coords:[9012,5493]},{type:"Food",name:"Rice",group:"foods",icon:"foods",coords:[6724,9556]},{type:"Food",name:"Meat",group:"foods",icon:"foods",coords:[6724,9556]},{type:"Food",name:"Beam",group:"foods",icon:"foods",coords:[6724,9556]},{type:"Liquid",name:"Water",group:"liquids",icon:"liquids",coords:[6724,9556]},{type:"Liquid",name:"Coffe",group:"liquids",icon:"liquids",coords:[6724,9556]}];
let r = markers.reduce((ms, m) => (ms.findIndex(o => o.item === m["group"]) > 0) ?
(ms[ms.findIndex(o => o.item === m["group"])]["qnt"]++, ms) :
(ms.push({ qnt: 1, item: m["group"]}), ms), []);
console.log(r);
count = {
candy: 1
foods: 3
fruits: 2
liquids: 2
}
After calculating the grouping counts from the initial data, you can send it to view to generate the html. (It is an object, not array). When generating html, you can iterate over its properties like this:
//Add ul
for(var item in count) {
//Get data and add li (item = candy, count[item] = 1)
}
You can use your code
var count = {} // I made an Object out of your Array, the proper way to do this
for (var i = 0; i < markers.length; i++) {
count[markers[i].group] = count[markers[i].group] + 1 || 1 ;
}
And convert your Object into an array afterwards:
var finalArray = [];
for(var key in count)
finalArray.push({item: key, qnt: count[key]});
}
var markers = [{"type":"Chocolate","name":"KitKat","group":"candy","icon":"candy","coords":[5246,8980]},{"type":"Fruit","name":"Orange","group":"fruits","icon":"fruis","coords":[9012,5493]},{"type":"Fruit","name":"Banana","group":"fruits","icon":"fruis","coords":[9012,5493]},{"type":"Food","name":"Rice","group":"foods","icon":"foods","coords":[6724,9556]},{"type":"Food","name":"Meat","group":"foods","icon":"foods","coords":[6724,9556]},{"type":"Food","name":"Beam","group":"foods","icon":"foods","coords":[6724,9556]},{"type":"Liquid","name":"Water","group":"liquids","icon":"liquids","coords":[6724,9556]},{"type":"Liquid","name":"Coffe","group":"liquids","icon":"liquids","coords":[6724,9556]}]
counts = []
markers.map(marker => counts.filter(type => type.name == marker.group).length> 0 ? counts.filter(type=>type.name ==marker.group)[0].count ++ : counts.push({'name':marker.group,'count':1}));
console.log(counts);
This or as someone said above, using reduce also works. This is also rather inefficient due to multiple maps and filters