合并具有相同属性的数组对象

Merging array objects with same properies

我正在做我的一个前端项目,我有一种情况,我必须根据某些条件 merge/add 数组中存在的对象。条件是

所以我的输入是

[
  {
    label: 'label-1',
    published: 1,
    draft: 2,
    id: 'some1'
  },
  {
    label: 'label-1',
    published: 2,
    status: 0,
    draft: 1,
    id: 'some4'
  },
  {
    label: 'label-2',
    published: 1,
    draft: 14,
    id: 'some2'
  },
  {
    label: 'label-2',
    published: 12,
    status: 0,
    draft: 14,
    id: 'some3'
  }
]

和预期

    [
      {
        label: 'label-1',
        published: 3,
        draft: 4,
        status: 0
      },
{
        label: 'label-2',
        published: 13,
        draft: 28,
        status: 0
      }
    ]

目前我正在使用以下代码来实现相同的目的,但发现它不够整洁。有什么办法可以轻松实现。

function mapData(data) {
    let groupData = _.groupBy(data, 'label');
    let stackBarData = [];
    Object.keys(groupData).forEach((key) => {
      if (groupData[key] && groupData[key].length > 0) {
        let temp = Array.from(groupData[key]).reduce((a, b) => {
          for (let property in b) {
            if (b.hasOwnProperty(property)) {
              if (property !== 'label' && property !== 'id' && property !== 'Others') {
                a[property] = (a[property] || 0) + b[property];
              } else {
                a[property] = b[property];
              }
            }
          }
          return a;
        }, {});
        stackBarData.push(temp);
      }
    });
    return stackBarData;
  }

请帮忙。

这是一个纯 ES6 函数,它收集数字对象值,并根据唯一标签将它们相加(这似乎是您所做的):

function mapData(data) {
    const grouped = new Map(data.map( ({label}) => [label, { label }] ));
    for (let obj of data) {
        let target = grouped.get(obj.label);
        for (let [key, val] of Object.entries(obj)) {
            if (typeof val === 'number') {
                target[key] = (target[key] || 0) + val;
            }
        }
    }
    return [...grouped.values()];
}

// Sample data
const data = [{label: 'label-1',published: 1,draft: 2,id: 'some1'},{label: 'label-1',published: 2,status: 0,draft: 1,id: 'some4'},{label: 'label-2',published: 1,draft: 14,id: 'some2'},{label: 'label-2',published: 12,status: 0,draft: 14,id: 'some3'}];

console.log(mapData(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }

如果您有想要排除的数字属性,那么最好有一组您感兴趣的显式属性:

const props = new Set(['status', 'published', 'draft']);
// ... etc
//
if (props.has(key)) { 
    target[key] = (target[key] || 0) + val;
}
// ...

Lodash

_.groupBy() by the label, _.map() the groups, and merge each group using _.mergeWith(), and _.omit() id。合并组时,如果当前值为数字,则将当前值和新值相加,如果不是 return undefined - 如果自定义程序 return 未定义,则合并由方法处理.

const arr = [{"label":"label-1","published":1,"draft":2,"id":"some1"},{"label":"label-1","published":2,"status":0,"draft":1,"id":"some4"},{"label":"label-2","published":1,"draft":14,"id":"some2"},{"label":"label-2","published":12,"status":0,"draft":14,"id":"some3"}]

const result = _(arr)
  .groupBy('label')
  .map((g) => _.omit(_.mergeWith({}, ...g, (objValue, srcValue) => _.isNumber(objValue) ? objValue + srcValue : undefined), 'id'))
  .value()
  
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

ES6

Array.reduce(). On each iteration check if the accumulator (the Map) has the label, and if not add an empty object with the label as the key. Iterate the current object keys with Array.forEach(), ignore id, and sum the numeric values. To get an array spread the Map.values()迭代数组:

const arr = [{"label":"label-1","published":1,"draft":2,"id":"some1"},{"label":"label-1","published":2,"status":0,"draft":1,"id":"some4"},{"label":"label-2","published":1,"draft":14,"id":"some2"},{"label":"label-2","published":12,"status":0,"draft":14,"id":"some3"}]

const result = [...arr.reduce((m, o) => {
  m.has(o.label) || m.set(o.label, {})
  
  const obj = m.get(o.label)
  
  Object.keys(o).forEach((k) => {
    if(k === 'id') return
    
    obj[k] = typeof o[k] === 'number' ? (obj[k] || 0) + o[k] : o[k]
  })
  
  return m
}, new Map()).values()]
  
console.log(result)