最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Count how many objects of each value has in array - Stack Overflow

programmeradmin1浏览0评论

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
Add a ment  | 

9 Answers 9

Reset to default 4

You 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

发布评论

评论列表(0)

  1. 暂无评论