В JavaScript есть элегантный способ объединить два объекта и суммировать какие-либо общие свойства? - PullRequest
2 голосов
/ 18 октября 2019

Я дедуплирую большой массив Объектов, многие из которых имеют некоторые общие свойства. Все свойства являются целыми числами.

Тривиально легко циклически перемещаться по клавишам и объединяться вручную, но я не могу не чувствовать, что есть какая-то комбинация Object.assign и map и reduce, котораямог бы сделать это одной строкой. По мере того как язык становится более зрелым, кажется, что он стоит впереди кривой.

РЕДАКТИРОВАТЬ: В качестве примера:

let A = {
  "a": 10,
  "e": 2,
  "g": 14
}

let B = {
  "b": 3,
  "e": 15,
  "f": 1,
  "g": 2,
  "h": 11
}

let C = Object.magicMerge(A, B) 

/*
{
  "a": 10,
  "e": 17,
  "g": 16
  "b": 3,
  "f": 1,
  "h": 11
}
*/

Ответы [ 5 ]

2 голосов
/ 18 октября 2019

В vanilla JS на самом деле нет никаких ярлыков, вы должны явно перебирать каждый объект и каждое свойство и каким-то образом суммировать их. Одним из вариантов является группировка с reduce:

const arr = [{
  foo: 1,
  bar: 2
}, {
  bar: 5,
  baz: 7
}, {
  baz: 10,
  buzz: 10
}];

const combined = arr.reduce((a, obj) =>
  Object.entries(obj).reduce((a, [key, val]) => {
    a[key] = (a[key] || 0) + val;
    return a;
  }, a)
, {});

console.log(combined);

Я предпочитаю reduce, потому что созданная внешняя переменная combined не мутирует в своей области видимости, но вы можете использовать for..of вместо этого, если хотите:

const arr = [{
  foo: 1,
  bar: 2
}, {
  bar: 5,
  baz: 7
}, {
  baz: 10,
  buzz: 10
}];

const combined = {};
for (const obj of arr) {
  for (const [key, val] of Object.entries(obj)) {
    combined[key] = (combined[key] || 0) + val;
  }
}

console.log(combined);
1 голос
/ 18 октября 2019

Вот что-то без циклов / уменьшение:

let C = Object.fromEntries(
    Object.keys(A)
        .concat(Object.keys(B))
        .map(k => [k,
            (A[k] || 0) + (B[k] || 0)
        ])
)

А вот универсальная функция для любого количества объектов:

let mergeSum = (...objs) => Object.fromEntries(
    Array.from(
        new Set(objs.flatMap(Object.keys)),
        k => [k,
            objs
                .map(o => o[k] || 0)
                .reduce((a, b) => a + b)
        ]))

C = mergeSum(A, B)

или даже более универсальная:

let mergeReduce = (objs, fn, init) => Object.fromEntries(
    Array.from(
        new Set(objs.flatMap(Object.keys)),
        k => [k, objs.map(o => o[k]).reduce(fn, init)]
    ));

// for example:

sumOfProps = mergeReduce([A, B],
    (a, b) => (a || 0) + (b || 0))

listOfProps = mergeReduce([A, B],
    (a, b) => b ? a.concat(b) : a,
    [])
0 голосов
/ 18 октября 2019

Я бы сделал это следующим образом:

const a = { "a": 10, "e": 2, "g": 14 };
const b = { "b": 3, "e": 15, "f": 1, "g": 2, "h": 11 };

const sum = [...Object.entries(a), ...Object.entries(b)]
  .reduce(
    (acc, [key, val]) => ({ ...acc, [key]: (acc[key] || 0) + val }),
    {}
  );
console.log("sum:", sum);
Один лайнер, как вы и просили :) На мой взгляд, это самый элегантный способ сделать это.
0 голосов
/ 18 октября 2019

Я хотел бы сделать:

function objTotals(array){
  const r = {};
  array.forEach(o => {
    for(let i in o){
      if(!(i in r))r[i] = 0;
    }
  });
  for(let i in r){
    array.forEach(o => {
      if(i in o)r[i]+=o[i];
    });
  }
  return r;
}
const a = [{a:0, b:2, c:5, d:1, e:2}, {a:1, b:3}, {a:5, b:7, c:4}, {a:2, b:1, c:9, d:11}];
console.log(objTotals(a));
0 голосов
/ 18 октября 2019

Да, мне кажется, вам нужно объединить ключи, а затем объединить объекты. Пробовал для более простого решения, но этот (хотя и дольше, чем мне нравится) делает работу, и для любого количества объектов.

const object1 = {
  a: 4,
  c: 2.2,
  d: 43,
  g: -18 
}, object2 = {
  b: -22.4,
  c: 14,
  d: -42,
  f: 13.3
};

// This will take any number of objects, and create
//  a single array of unique keys.
const mergeKeys = (objects)=>{
  let keysArr = objects.reduce((acc, object)=>{
    acc.push(...Object.keys(object));
    acc = [...new Set(acc.sort() )];
    return acc;
  }, []);
  return keysArr;
}

/***
 * this first gets the unique keys, then creates a
 *   merged object. Each object is checked for each
 *   property, and if it exists, we combine them.
 *   if not, we simply keep the current value.
 *
 ***/
const combineAnyNumberOfObjects = (...objects)=>{
  const keys = mergeKeys(objects),
        returnObj = {};
  keys.forEach(key=>{
    returnObj[key]=0;
    objects.forEach(object=>{
      returnObj[key] = !!object[key] ? returnObj[key]+object[key] : returnObj[key]
    })
  })

  return returnObj;
}

console.log(combineAnyNumberOfObjects(object1, object2));

Как вы сказали, тривиально легко объединить и вручную добавить каждую опору. Ответ в одну строку выше? Красивый. ;)

...