【问题标题】:Summarize the frequency of array of objects总结对象数组的频率
【发布时间】:2019-12-27 06:03:36
【问题描述】:

假设我有以下对象数组。

data = [
  { x: 1, y: 1 },
  { x: 2, y: 2 },
  { x: 3, y: 3 },
  { x: 2, y: 2 },
  { x: 1, y: 1 },
  { x: 1, y: 2 },
  { x: 1, y: 1 }
]

我需要总结数组中相同对象的频率。输出将如下所示:

summary = [
  { x: 1, y: 1, f: 3 },
  { x: 1, y: 2, f: 1 },
  { x: 2, y: 2, f: 2 },
  { x: 3, y: 3, f: 1 }
]

现在我有这个代码

const summary = data.map((item, index, array) => {
  return { x: item.x, y: item.y, f: array.filter(i => i === item).length };
});

但我想我可以使用reduceincludes 做得更好。有什么想法吗?

【问题讨论】:

  • 你应该使用reducemap 总是返回一个元素个数相同的数组,但是你想组合等价的元素。
  • i === item 不适用于对象。它要求对象相同,而不仅仅是具有相同的属性。见stackoverflow.com/questions/201183/…
  • @Barmar 事实上,如果这段代码工作正常,我还没有检查好。我知道我需要进一步删除map 之后的重复项。无论如何感谢您的建议。

标签: javascript arrays object ecmascript-6 counting


【解决方案1】:

缩减为一个对象,其键唯一地表示一个对象,其值为对象(具有xyf 属性)。在每次迭代中,增加相应键的 f 属性,或者在累加器上创建键(如果它尚不存在):

const data = [
  { x: 1, y: 1 },
  { x: 2, y: 2 },
  { x: 3, y: 3 },
  { x: 2, y: 2 },
  { x: 1, y: 1 },
  { x: 1, y: 2 },
  { x: 1, y: 1 }
];
const countObj = data.reduce((a, obj) => {
  const objString = obj.x + '_' + obj.y;
  if (!a[objString]) {
    a[objString] = { ...obj, f: 1 };
  } else {
    a[objString].f++;
  }
  return a;
}, {});
const output = Object.values(countObj);
console.log(output);

【讨论】:

    【解决方案2】:

    不要使用map - 你最好像这样使用reduce

    const summary = Object.values(data.reduce((a, { x, y }) => {
      a[`${x}-${y}`] = a[`${x}-${y}`] || { x, y, f: 0 };
      a[`${x}-${y}`].f++;
      return a;
    }, {}));
    

    【讨论】:

      【解决方案3】:
      Object.values(data.reduce((sum, i) => {
          i_str = JSON.stringify(i); // objects can't be keys
          sum[i_str] = Object.assign({}, i, {f: sum[i_str] ? sum[i_str].f+1 : 1});
          return sum;
      }, {}));
      

      注意:

      1. 此 sn-p 将适用于任意对象的数组,只要它们是可字符串化的。
      2. 结果未排序,因为对象键未排序。如果这是一个问题,请随意排序。
      3. 您所做的是计算对象在数组中存在的次数。您可能想要对象外部的结果,而不是嵌入其中的结果。类似这样的事情可能更易于管理,将对象描述的映射返回到计数:
      data.reduce((sum, i) => {
          i_str = JSON.stringify(i); // objects can't be keys
          sum[i_str] = sum[i_str] ? sum[i_str]+1 : 1;
          return sum;
      }, {});
      

      【讨论】:

      • 这对我来说似乎是最优雅和最通用的方法,所以我把支票交给了你。但是很高兴看到您发布答案的所有好主意,谢谢:)
      【解决方案4】:

      基于Array#reduce 的简单解决方案如下:

      const data = [
        { x: 1, y: 1 },
        { x: 2, y: 2 },
        { x: 3, y: 3 },
        { x: 2, y: 2 },
        { x: 1, y: 1 },
        { x: 1, y: 2 },
        { x: 1, y: 1 }
      ];
      
      const summary = data.reduce((frequencySummary, item) => {
        
        /* Find a match for current item in current list of frequency summaries */
        const itemMatch = frequencySummary.find(i => i.x === item.x && i.y === item.y)
        
        if(!itemMatch) {
          
          /* If no match found, add a new item with inital frequency of 1 to the result */
          frequencySummary.push({ ...item, f : 1 });
        }
        else {
          
          /* If match found, increment the frequency count of that match */
          itemMatch.f ++;
        }
        
        return frequencySummary;
      
      }, []);
      
      console.log(summary)

      【讨论】:

        【解决方案5】:

        我知道使用 reduce 可能更好,但我倾向于使用 forEach 和 findIndex 以获得更好的可读性。

        var data = [
          { x: 1, y: 1 },
          { x: 2, y: 2 },
          { x: 3, y: 3 },
          { x: 2, y: 2 },
          { x: 1, y: 1 },
          { x: 1, y: 2 },
          { x: 1, y: 1 }
        ];
        
        var summary = [];
        
        data.forEach(function(d){
          var idx = summary.findIndex(function(i){
            return i.x === d.x && i.y === d.y;
          });
        
          if(idx < 0){
            var sum = Object.assign({}, d);
            sum.f = 1;
            summary.push(sum);
          } else {
            summary[idx].f = summary[idx].f + 1;
          }
        });
        
        console.log(summary);
        

        【讨论】:

          【解决方案6】:

          创建嵌套对象。外部对象使用x 值作为键,嵌套对象包含y 值作为键,值是频率。

          data = [
            { x: 1, y: 1 },
            { x: 2, y: 2 },
            { x: 3, y: 3 },
            { x: 2, y: 2 },
            { x: 1, y: 1 },
            { x: 1, y: 2 },
            { x: 1, y: 1 }
          ];
          
          const nested = data.reduce((a, {x, y}) => {
            a[x] = a[x] || {};
            a[x][y] = a[x][y] ? a[x][y] + 1 : 1
            return a;
          }, {});
          const summary = [];
          Object.keys(nested).forEach(x => Object.keys(nested[x]).forEach(y => summary.push({x, y, f: nested[x][y]})));
          
          console.log(summary);

          【讨论】:

            【解决方案7】:

            您可以使用 reduceMap,将 x 和 y 用作键,在每次迭代时检查 Map 上是否已经存在相同的键,而不是仅将 f 计数增加 1 如果不超过设置为1

            const data = [{ x: 1, y: 1 },{ x: 2, y: 2 },{ x: 3, y: 3 },{ x: 2, y: 2 },{ x: 1, y: 1 },{ x: 1, y: 2 },{ x: 1, y: 1 }];
            
            const countObj = data.reduce((a, obj) => {
              const objString = obj.x + '_' + obj.y;
              let value = a.get(objString) || obj
              let f = value && value.f  || 0
              a.set(objString, { ...value, f: f+1 })
              return a;
            }, new Map());
            
            console.log([...countObj.values()]);

            【讨论】:

              猜你喜欢
              • 2021-04-18
              • 2021-04-23
              • 1970-01-01
              • 1970-01-01
              • 2023-03-10
              • 2015-06-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多