【问题标题】:Count how many objects of each value has in array计算每个值在数组中有多少个对象
【发布时间】:2019-04-30 16:47:11
【问题描述】:

我有这个数组:

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 count = []

for (var i = 0; i < markers.length; i++) {
  count[markers[i].group] = count[markers[i].group] + 1 || 1 ;
}

输出这个结果:

count = [
candy: 1
foods: 3
fruits: 2
liquids: 2
]

我想在另一部分使用这个值,为此我需要将数组结构更改为如下所示:

count = [
{"item": "candy","qnt":1},
{"item": "foods","qnt":3},
{"item": "fruits","qnt":2},
{"item": "liquids","qnt":2}
]

我知道我可以这样做:

var total_fruits = 0;
for (var i = 0; i < markers.length; i++) {
  if (markers[i].group == "fruits"){
    total_fruits++
  }
}

但是想象一下,对于一组超过 50 种类型,我需要多少个 if...

我会将html部分中的值与item值使用相同的类是这样的:

<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>

有什么建议或如何改进?

【问题讨论】:

  • 在您的原始代码中,您使用数组作为对象,但它是错误的。
  • @ibrahimmahrir 这不是他们已经拥有的(除非错误使用数组类型)?
  • 做你已经在做的事情可能是最简单的,然后获取结果对象,并将其转换为你想要的对象列表。所以你不必每次都在数组中为你的组找到元素

标签: javascript jquery arrays multidimensional-array


【解决方案1】:

您可以使用reduce() 一步构建您想要的对象。这将构建一个以group 为键的对象。要仅获取数组,请获取该对象的 Object.values()

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))

【讨论】:

    【解决方案2】:

    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 );

    【讨论】:

      【解决方案3】:

      您可以通过Array.reduce 执行此操作,按group 进行分组,然后使用Object.entriesArray.map 获得所需的输出格式:

      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 })))

      【讨论】:

      • 好答案!即使它使用多个方法调用也很清楚。出于某种原因,我总是过度考虑分配(r[c.group] || 0) + 1 始终是积累数量时的最佳方式。 +1
      【解决方案4】:

      预期的输出不正确。一个数组不能有这样的键和值。你可能需要一个对象。

      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)

      【讨论】:

      • 没有 OP 的输出是 count = [ candy: 1 foods: 3 fruits: 2 liquids: 2 ] ,它是一个数组而不是一个对象
      • 你是对的,OP 在问题中的输出是不可能重现的,因为没有办法让键/值对出现在这样的方括号之间。但是 OP 要求使用带有 { item: name, qnt: count } 的对象数组的格式,而不是 { name: count },正如您的回答所表明的那样。
      【解决方案5】:

      (在我看来)最好的方法是使用像 { item: count } 这样的格式,除了 count 应该是对象 {} 而不是数组 [](参见 @brk's answer) .如果您希望输出是对象数组,则只需使用缓存对象(它将保存 count 数组中的计数对象的索引):

      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'
        }
      }
      

      【讨论】:

        【解决方案6】:

        您可以使用reduce 方法返回一个包含项目和数量的新数组。

        我们使用三元语句来确定累加器数组是否已经包含findIndex 的组类型。如果不是,我们推送新类型的 qnt 为 1,如果是,我们简单地增加 qnt 值。

        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);

        【讨论】:

        • 我不知道reduce方法,这也是解决这个问题的好方法。比生成另一个数组更快?
        • @RogerHN 从技术上讲,它确实创建了另一个数组,但实际上它可能更快。好处是它天生就是一个函数式方法,并且不会用不必要的变量和操作污染全局范围。
        【解决方案7】:
        count = {
          candy: 1
          foods: 3
          fruits: 2
          liquids: 2
        }
        

        从初始数据计算分组计数后,您可以将其发送到视图以生成html。 (它是一个对象,而不是数组)。 生成 html 时,您可以像这样遍历其属性:

        //Add ul
        for(var item in count) {
           //Get data and add li (item = candy, count[item] = 1)
        }
        

        【讨论】:

          【解决方案8】:

          您可以使用您的代码

          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 ;
          }
          

          然后将您的对象转换为数组:

          var finalArray = [];
          for(var key in count)
            finalArray.push({item: key, qnt: count[key]});
          }
          

          【讨论】:

          • 谢谢,这个方法很简单!我不知道哪个答案是最快的方法,但是这种方法很简单。
          【解决方案9】:

          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);

          这或者正如上面有人所说,使用 reduce 也有效。 由于多个映射和过滤器,这也相当低效

          【讨论】:

          • 我关心的是性能,因为原始数组有一千多个条目,应用程序有很多复杂的功能。但这也是解决问题的好方法,支持 Colby!谢谢!
          猜你喜欢
          • 1970-01-01
          • 2019-07-24
          • 2018-07-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-17
          相关资源
          最近更新 更多