Перебор массива объектов, суммирование значений с одинаковым индексом и возврат нового массива объектов - PullRequest
0 голосов
/ 10 июня 2018

У меня есть массив объектов, что-то вроде этого:

const data = [                 // array1
  [{x: 1}, {y:2}, {z:3}], 
  [{x: 1}, {y:2}, {z:3}],
  [{x: 1}, {y:2}, {z:3}]
],[                            // array2
  [{x: 1}, {y:2}, {z:3}], 
  [{x: 1}, {y:2}, {z:3}],
  [{x: 1}, {y:2}, {z:3}]
]

Необходимо выполнить суммирование x из array1 с x из array2, которые имеюттот же индекс.То же самое касается y и z.Конечным результатом должен быть новый массив объектов, содержащий суммированные значения.

Примерно так:

[
  [{totalXOne: 2}, {totalYOne: 4}, {totalZOne: 6}],
  [{totalXTwo: 2}, {totalYTwo: 4}, {totalZTwo: 6}],
  [{totalXThree: 2}, {totalYthree: 4}, {totalZThree: 6}],
]

Примечание: Все массивы одинаковой длины, и если значение отсутствует, оно будет заменено на 0)

Я нашел что-то хорошее в MDN, но он суммирует все значения x, y, z и возвращает одиночные суммированные значения, например:

let initialValue = 0;
let sum = [{x: 1}, {x:2}, {x:3}].reduce(function(accumulator,currentValue) {
    return accumulator + currentValue.x;
}, initialValue)

Вывод:

[
  [{totalX: 3}, {totalY: 6}, {totalZ: 9}],  // this is not what I need
]

Есть ли способ, которым я могу достичь этого?

ОБНОВЛЕНИЕ

Я получаю JSON из другого источника.Он содержит свойство под названием allEmpsData, сопоставляя его, я получаю необходимые salaryData, и сопоставляя его, я получаю данные NET | GROSS | TAX.

let allReports = [];

    setTimeout(() => {

        allEmpsData.map(x => {
            let reports = {};

            let years = [];
            let months = [];

            let netArr = [];
            let grossArr = [];
            let mealArr = [];
            let taxArr = [];
            let handSalaryArr = [];

            x.salaryData.map(y => {
                years.push(y.year);
                months.push(y.month);
                    netArr.push(y.totalNetSalary);
                    grossArr.push(y.bankGrossSalary);
                    mealArr.push(y.bankHotMeal);
                    taxArr.push(y.bankContributes);
                    handSalaryArr.push(y.handSalary);
                })
                reports.year = years;
                reports.month = months;
                reports.net = netArr;
                reports.gross = grossArr;        
                reports.meal = mealArr;        
                reports.taxesData = taxArr;        
                reports.handSalaryData = handSalaryArr;
                allReports.push(Object.assign([], reports));
        });
    }, 1000);

Как я могу сказать, все работает как надо, но правда в том, что.Я не знаю ничего лучше.Затем здесь происходит волшебство:

setTimeout(() => {
    result = allReports.reduce((r, a) =>
         a.map((b, i) =>
           b.map((o, j) =>
             Object.assign(...Object
              .entries(o)
               .map(([k, v]) => ({ [k]: v + (getV(r, [i, j, k]) || 0) }))
                    )
                )
            ),
            undefined
        );
            console.log(result);
        }, 1500);

... и он возвращает пустой массив в консоли узла, но если я console.log любое другое свойство из обновленного кода выше, он там.Есть предложения?

Ответы [ 8 ]

0 голосов
/ 11 июня 2018

Это решение возвращает один объект с добавлением значения каждого ключа.

const arr1 = [
  [{x: 1}, {y: 2}, {z: 3}],
  [{x: 4}, {y: 6}, {z: null}],
  [{x: 5}, {y: 7}, {z: 9}]
]

const arr2 = [
  [{x: 12}, {y: 20}, {z: 4}],
  [{x: 13}, {y: 21}, {z: 3}],
  [{x: 14}, {y: 22}, {z: 5}]
]

const arr3 = [
  [{x: 2},    {y: 10}, {z: 67}],
  [{x: 3},    {y: 31}, {z: 23}],
  [{x: null}, {y: 25}, {z: null}]
]

function get_keys (arr) {
  let keys = []
  for (let i = 0; i < arr[0].length; i++) {
    let key = Object.keys(arr[0][i])[0]
    keys.push(key)
  }
  return keys
}

function sum_by_key (arrays) {

  let res = {}

  let keys = get_keys(arrays)
  let all_obj = []

  for (let i = 0; i < arrays.length; i++) {
    for (let d = 0; d < arrays[i].length; d++) {
      all_obj.push(arrays[i][d])
    }
  }

  for (let i = 0; i < keys.length; i++) {
    let k = keys[i]
    res[k] = 0
    for (let d = 0; d < all_obj.length; d++) {
      let __k = Object.keys(all_obj[d])[0]
      if (k === __k) {
        res[k] += all_obj[d][__k]
      }
    }
  }

  return res

}

let arrays = [...arr1, ...arr2, ...arr3]
console.log(sum_by_key(arrays)) //=> { x: 54, y: 144, z: 114 }
0 голосов
/ 10 июня 2018

Немного короче альтернатива:

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

var result = data.reduce( (a, b) => a.map((_, i) => 
  Array.from('xyz', (k, j) => [ { [k]: a[i][j][k] + b[i][j][k] } ] ) ) );

console.log( JSON.stringify( result ).replace(/]],/g, ']],\n ') );
0 голосов
/ 11 июня 2018

То, что вы спрашиваете, в основном известно как zipWith функция.Таким образом, общее решение может быть изложено как:

var data    = [[[{x: 1}, {y:2}, {z:3}], 
                [{x: 1}, {y:2}, {z:3}],
                [{x: 1}, {y:2}, {z:3}]],
               [[{x: 1}, {y:2}, {z:3}], 
                [{x: 1}, {y:2}, {z:3}],
                [{x: 1}, {y:2}, {z:3}]]],
    zipWith = (a,b,f) => a.map((e,i) => f(e,b[i])),
    zipper  = (sa,sb) => sa.map((o,i) => Object.keys(o)
                                               .reduce((r,k) => (r[k] = o[k] + sb[i][k], r), {})),
    result  = data.reduce((p,c) => zipWith(p,c,zipper));
console.log(result);
0 голосов
/ 10 июня 2018

Вы можете использовать вспомогательную функцию для получения значения вложенного объекта и сопоставления значений с тем же индексом.

const getV = (o, p) => p.reduce((t, k) => (t || {})[k], o);

var data = [[[{ x: 1 }, { y: 2 }, { z: 3 }], [{ x: 1 }, { y: 2 }, { z: 3 }], [{ x: 1 }, { y: 2 }, { z: 3 }]], [[{ x: 1 }, { y: 2 }, { z: 3 }], [{ x: 1 }, { y: 2 }, { z: 3 }], [{ x: 1 }, { y: 2 }, { z: 3 }]]],
    result = data.reduce((r, a) =>
        a.map((b, i) =>
            b.map((o, j) =>
                Object.assign(...Object
                    .entries(o)
                    .map(([k, v]) => ({ [k]: v + (getV(r, [i, j, k]) || 0) }))
                )
            )
        ),
        undefined
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
0 голосов
/ 10 июня 2018

Попробуйте этот простой и небольшой фрагмент кода:

const data = [                 // array1


[{x: 1}, {y:2}, {z:3}], 
  [{x: 1}, {y:2}, {z:3}],
  [{x: 1}, {y:2}, {z:3}]
],[                            // array2
  [{x: 1}, {y:2}, {z:3}], 
  [{x: 1}, {y:2}, {z:3}],
  [{x: 1}, {y:2}, {z:3}]
]


var array1 = data[0];
var array2 = data[1];
var returnArray = [];

array1.forEach(function (subArray1, index){
var subArray2 = array2[index];
var subReturn = [];
subArray1.forEach(function (obj, i) {
    var variableVal;
    if (i == 0){variableVal = "x";} else if (i == 1) {variableVal = "y";}
    else if (i == 2) {variableVal = "z"}
    var newObj = {};
    newObj[variableVal] = obj[variableVal] + subArray2[i][variableVal];
    subReturn[i] = newObj;
    });
returnArray[index] = subReturn;
});

console.log(returnArray);
0 голосов
/ 10 июня 2018

Вот функциональное программирование способ сделать это, используя промежуточный ES6 Map:

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

const result = data[0].map( (arr, i) => Array.from(data.reduce( (acc, grp) => (
    grp[i].forEach( o =>
        Object.entries(o).forEach( ([k, v]) => acc.set(k, (acc.get(k) || 0) + v)) 
    ), acc
), new Map), ([k, v]) => ({ [k]: v })) ); 

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

Пояснение

Чтобы облегчить объяснение, давайте договоримся о некоторых терминах:

У нас есть вход (массив), состоящий из групп .Каждая группа является массивом, состоящим из строк .Каждая строка состоит из объектов , каждый из которых имеет одну пару свойство / значение.

Выход не имеет группы level, но у него есть строки , снова состоящие из объектов , каждая из которых имеет одну пару свойство / значение.

Итак, используя эти термины, давайте пройдемся по коду:

Поскольку число строк в выходном массиве равно количеству строк в любой из групп, представляется хорошим началом сопоставить строки первой группы, например, как data[0].map.

Для каждой строки в выводе нам нужно составить суммы, и reduce - хорошая функция-кандидат для этой работы, поэтому мы вызываем data.reduce.Для начального значения этого reduce вызова я передал пустой Map.Цель состоит в том, чтобы заполнить эту Карту парами ключ-сумма.Позже мы можем затем разложить эту Карту на отдельные объекты, каждый из которых имеет только одну из этих пар ключ / сумма (но это на потом).

Таким образом, reduce начинается с Map и повторяется погрупп.Нам нужно взять строку i th из каждой группы, чтобы найти объекты, которые нужно «добавить».Итак, мы берем строку grp[i].

Для каждого объекта в этой строке мы получаем имя свойства и значение с Object.entries(o).Фактически, эта функция возвращает массив, поэтому мы выполняем итерацию по ней с forEach, зная, что на самом деле мы будем выполнять итерацию только один раз, поскольку на практике существует только одно свойство.Теперь у нас есть ключ (k) и значение v.Мы находимся на самом глубоком уровне в структуре ввода.Здесь мы настраиваем аккумулятор.

С помощью acc.get(k) мы можем знать, что мы уже накопили для определенного ключа (например, для "x").Если у нас там еще ничего не было, он инициализируется с 0, выполняя || 0.Затем мы добавляем к нему текущее значение v и сохраняем эту сумму обратно на карту с acc.set(k, ....).Используя оператор запятой, мы возвращаем acc обратно в реализацию reduce (мы могли бы использовать return здесь, но оператор запятой является более кратким).

И поэтому Map получает все суммы заключ.С Array.from мы можем перебрать каждую из этих пар ключ / сумма и, используя аргумент обратного вызова, превратить эту пару в правильный маленький объект (с { [k]: v }).Обозначение [k] также является новшеством в ES6 - оно допускает динамические имена ключей в литералах объектов.

Итак ... Array.from возвращает массив небольших объектов, каждый из которых имеет сумму.Этот массив представляет одну строку для вывода.Метод map создает все строки, необходимые для вывода.

0 голосов
/ 10 июня 2018

Попробуйте следующее:

var arr1 = [[{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}]];

var arr2 = [[{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}]];

var map = {
  0 : 'x',
  1 : 'y',
  2 : 'z'
};
var map2 = {
 0 :"One",
 1 :"Two",
 2 : "Three"
};
var result = [];
var obj= {};

for(var i = 0; i < arr1.length; i++){
  total = 0;
  var arr =[];
  for(var j =0; j < arr1[i].length; j++){
    obj["total"+ map[j] + map2[i]] = arr1[i][j][map[j]] + arr2[i][j][map[j]];
     arr.push(obj);
     obj = {};
  }
  result.push(arr);
}
console.log(result);
0 голосов
/ 10 июня 2018

Это хорошая идея - попытаться разбить такого рода проблемы на более мелкие и постепенно наращивать их.Это означает, что нам не нужно смотреть на все это сразу.

Давайте напишем функцию, которая складывает отдельные элементы из массива:

function addElements(element1, element2, key, rowIndex) {
   //for now we keep the keys the same, otherwise multiple additions
   //won't work   
   return {
       [key]: element1[key] + element2[key]
   };
}

Теперь давайте добавим две строкивместе, используя нашу addElements():

function addRows(row1, row2, rowIndex) {
    return ['x', 'y', 'z'].map((key, index) => {
        // "key" will go through "x", "y", and "z" as
        // "index" goes 0, 1, 2
        const element1 = row1[index];
        const element2 = row2[index];
        return addElements(element1, element2, key, rowIndex);
    });
}

Теперь мы можем перебрать все строки в нашей первой матрице и добавить эквивалент из второй матрицы, используя addRows():

function addMatrices(matrix1, matrix2) {
     return matrix1.map((row1, index) => {
         const row2 = matrix2[index];
         return addRows(row1, row2, index);
     });
}

Теперь мы можем превратить это в редуктор:

const EMPTY_MATRIX = { ... }; //define a matrix of all zeroes here
matrices.reduce(addMatrices, EMPTY_MATRIX);

Надеюсь, это поможет!

...