【问题标题】:Group array of objects by common keys, merging nested property into array [duplicate]通过公共键对对象数组进行分组,将嵌套属性合并到数组中[重复]
【发布时间】:2020-11-12 04:50:47
【问题描述】:

假设我有这个数组:

[
    {"type": "A",   "status": "CREATED",        "name": "Jack"}, 
    {"type": "A",   "status": "CREATED",        "name": "John"}, 
    {"type": "A",   "status": "UPDATED",        "name": "Alex"}, 
    {"type": "B",   "status": "UPDATED",        "name": "Jane"}
]

我希望有一个按“type”和“status”分组的新数组,并将名称放在一个列表中。 这是所需的输出:

[
    {"type": "A",   "status": "CREATED",        "name-list": ["Jack", "John"]}, 
    {"type": "A",   "status": "UPDATED",        "name-list": ["Alex"]}, 
    {"type": "B",   "status": "UPDATED",        "name-list": ["Jane"]}
]

如您所见,数组的第一个对象包含 2 个名称,因为它们属于相同的“type”和“status”。 (name-list只是一个例子,也可以保留name,重要的是它应该是一个字符串/对象数组)

我必须在 html 页面中表示这些数据,因此需要能够通过 JavaScript 循环它们。

【问题讨论】:

  • 如果性能对您的应用很重要,您可以查看the benchmark

标签: javascript arrays


【解决方案1】:

您可以使用Array.prototype.reduce() 遍历您的源数组,构建Map,将typestatus 组合为键和各自的对象,并合并name 作为值。然后从Map中提取Map.prototype.values()

const src = [{"type":"A","status":"CREATED","name":"Jack"},{"type":"A","status":"CREATED","name":"John"},{"type":"A","status":"UPDATED","name":"Alex"},{"type":"B","status":"UPDATED","name":"Jane"}],

     result = [...src
        .reduce((r, o) => {
          const key = o.type+'\ud8ff'+o.status,
                match = r.get(key)
          match ? match.name.push(o.name) : r.set(key, {...o, name: [o.name]})
          return r
        }, new Map())
        .values()
      ]
      
console.log(result)
.as-console-wrapper{min-height:100%;}

【讨论】:

  • 太酷了,如果我在每个对象中有一个额外的整数参数,我想在分组名称时求和?
  • @andQlimax : 就是这么简单,就像that
【解决方案2】:

我会尝试使用.reduce().find()

const data = [
    {"type": "A",   "status": "CREATED",        "name": "Jack"}, 
    {"type": "A",   "status": "CREATED",        "name": "John"}, 
    {"type": "A",   "status": "UPDATED",        "name": "Alex"}, 
    {"type": "B",   "status": "UPDATED",        "name": "Jane"}
];

const result = data.reduce((a, { type, status, name}) => {
  const found = a.find(e => e.type === type && e.status === status);
  
  if (found) {
    found['name-list'].push(name);
  } else {
    a.push({
      type,
      status,
      ['name-list']: [name]
    });
  }
  
  return a;
}, []);

console.log(result);

【讨论】:

  • 进行嵌套查找(.find()'s inside .reduce() 可能会降低性能,如果源数组足够大。
  • @YevgenGorbunkov 同意,​​检查了你的答案,这很聪明,也赞成。
【解决方案3】:

const arr = [
    {"type": "A",   "status": "CREATED",        "name": "Jack"}, 
    {"type": "A",   "status": "CREATED",        "name": "John"}, 
    {"type": "A",   "status": "UPDATED",        "name": "Alex"}, 
    {"type": "B",   "status": "UPDATED",        "name": "Jane"}
]

const res = arr.reduce((acc, cur) => {
  
  acc.forEach((obj, idx) => {
      if(obj.type == cur.type && obj.status == cur.status){
          acc[idx]["name-list"].push(cur.name)
      }
  })
  
  if(!acc.some(obj => obj["name-list"].includes(cur.name))){
      acc.push({type: cur.type, status: cur.status, "name-list": [cur.name]})
  }
  
  return acc
},[])

console.log(res)

【讨论】:

    【解决方案4】:

    这是你想要的:

    const ar = [
        { "type": "A", "status": "CREATED", "name": "Jack" },
        { "type": "A", "status": "CREATED", "name": "John" },
        { "type": "A", "status": "UPDATED", "name": "Alex" },
        { "type": "B", "status": "UPDATED", "name": "Jane" }
    ];
    const res = Array.from(ar.reduce((a, c) => {
        if (a.has(c.type + c.status)) {
            a.get(c.type + c.status)['name-list'].push(c.name);
        } else {
            c['name-list'] = [c.name];
            delete c.name;
            a.set(c.type + c.status, c)
        }
        return a;
    }, new Map()).values());
    
    console.log(res);

    【讨论】:

    • 使用 typestatus 连接作为 Map 键有点危险,因为您可能会意外地将对象与 {type: 'a', status: 'bc'..}{type: 'ab', status: 'c'..} 合并。最好使用一些 safe 字符作为分隔符。例如,您可以参考my ansewr
    【解决方案5】:

    使用reduceObject.values

    let arr = [
        {"type": "A",   "status": "CREATED",        "name": "Jack"}, 
        {"type": "A",   "status": "CREATED",        "name": "John"}, 
        {"type": "A",   "status": "UPDATED",        "name": "Alex"}, 
        {"type": "B",   "status": "UPDATED",        "name": "Jane"}
    ];
    
    arr = Object.values(
        arr.reduce((acc, cur) => {
            if(acc[`${cur.type}-${cur.status}`]) acc[`${cur.type}-${cur.status}`]["name-list"].push(cur.name)
    
            else {
                acc[`${cur.type}-${cur.status}`] = {type:cur.type, status:cur.status, "name-list":[cur.name]}
            }    
            return acc;
        }, {})
    );
    
    console.log(arr)

    【讨论】:

    • 使用连字符作为分隔符有点像你的风险,会意外地合并带有{type: 'a-b', status:'c..}和`{type:'a',status:'b-c'..}的对象。您应该使用更安全的分隔符,例如,您可以参考my answer
    猜你喜欢
    • 2023-02-07
    • 1970-01-01
    • 2016-09-10
    • 1970-01-01
    • 2018-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多