Javascript 总结对象数组的频率

Javascript 总结对象数组的频率,javascript,arrays,object,ecmascript-6,counting,Javascript,Arrays,Object,Ecmascript 6,Counting,假设我有以下对象数组 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,

假设我有以下对象数组

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

但是我想我可以通过使用
reduce
includes
做得更好。有什么想法吗?

简化为一个对象,其关键帧唯一地表示一个对象,其值就是对象(具有
x
y
f
属性)。在每次迭代中,增加相应键的
f
属性,或者在累加器上创建该键(如果该键尚不存在):

const数据=[
{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=数据减少((a,obj)=>{
常量objString=obj.x+'.'+obj.y;
如果(!a[objString]){
a[objString]={…obj,f:1};
}否则{
a[objString].f++;
}
返回a;
}, {});
常量输出=对象值(countObj);

控制台日志(输出)不要使用
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;
}, {}));

基于的简单解决方案如下所示:

const数据=[
{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((频率摘要,项)=>{
/*在当前频率摘要列表中查找当前项的匹配项*/
const itemMatch=frequencysummmary.find(i=>i.x==item.x&&i.y==item.y)
如果(!itemMatch){
/*如果未找到匹配项,则在结果中添加初始频率为1的新项*/
推送({…项,f:1});
}
否则{
/*如果找到匹配项,则增加该匹配项的频率计数*/
itemMatch.f++;
}
返回频率汇总;
}, []);

log(summary)
我知道使用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);
var数据=[
{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汇总=[];
data.forEach(函数(d){
var idx=summary.findIndex(函数(i){
返回i.x==d.x&&i.y==d.y;
});
if(idx<0){
var sum=Object.assign({},d);
总和f=1;
推送(sum);
}否则{
摘要[idx].f=摘要[idx].f+1;
}
});
控制台日志(摘要);

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

数据=[
{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
返回a;
}, {});
const summary=[];
forEach(y=>summary.push({x,y,f:nested[x][y]}));

控制台日志(摘要)
您可以使用
减少
映射
,使用x和y作为键,在每次迭代中检查相同的键是否已经出现在映射上,而不是仅仅增加
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=数据减少((a,obj)=>{
常量objString=obj.x+'.'+obj.y;
让value=a.get(objString)| | obj
设f=value&&value.f | | 0
a、 集合(对象字符串,{…值,f:f+1})
返回a;
},新地图());
log([…countObj.values()])
注:

  • 此代码段将用于任意对象的数组,只要它们是可字符串化的
  • 结果不排序,因为对象键不排序。如果这是一个问题,请随意排序
  • 你要做的是计算一个对象在数组中存在的次数。您可能希望结果位于对象外部,而不是嵌入其中。沿着这些路线的东西可能更易于管理,返回对象描述到计数的映射:

  • 您应该使用
    reduce
    map
    始终返回具有相同数量元素的数组,但您希望组合等效元素。
    i===item
    不适用于对象。它要求对象是相同的,而不仅仅是具有相同的属性。请看@Barmar确实,我没有检查好这段代码是否工作正常。我知道我需要进一步删除
    映射后的重复项。无论如何,谢谢你的建议。对我来说,这似乎是最优雅、最普遍的方法,所以我给了你支票。但很高兴看到大家都发表了很多好的想法,谢谢:)
    
    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;
    }, {}));
    
    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;
    }, {});