迭代一个对象数组,使用相同的索引对值求和,并返回一个新的对象数组

时间:2018-06-10 14:02:52

标签: javascript arrays node.js object reduce

我有一个对象数组,如下所示:

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的{​​{1}}的{​​{1}}的{​​{1}}相加,这些x具有相同的索引。 array2y也是如此。最终结果应该是包含求和值的新对象数组。

这样的事情:

z

注意:所有数组的长度相同,如果缺少值,则会替换为[ [{totalXOne: 2}, {totalYOne: 4}, {totalZOne: 6}], [{totalXTwo: 2}, {totalYTwo: 4}, {totalZTwo: 6}], [{totalXThree: 2}, {totalYthree: 4}, {totalZThree: 6}], ]

我在MDN上找到了一些不错的东西,但是它汇总了所有0xy值,并且它返回了单个求和值,如下所示:

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并映射它我得到NET | GROSS | TAX数据。

salaryData

据我所知,一切正常,但事实是,。我不知道更好。然后是魔术:

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); 上面更新的代码中的任何其他属性,它就在那里。有什么建议吗?

8 个答案:

答案 0 :(得分:4)

这是一种函数式编程方法,使用中间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; }




解释

为了便于解释,我们就某些条款达成一致:

我们有输入(一个数组),由组成。每个是一个由组成的数组。每个对象组成,每个对象都有一个属性/值对。

输出没有级别,但它有,再次由对象组成,每个都有一个属性/值对。

因此,使用这些条款,我们将通过代码:

由于输出数组中的行数等于任何组中的行数,因此映射第一组的行似乎是一个良好的开端,即像data[0].map

对于输出中的每一行,我们需要进行求和,reduce是该作业的一个很好的候选函数,因此我们调用data.reduce。对于reduce调用的初始值,我已经传递了空Map。目的是用关键和对填充该Map。之后我们可以将Map分解为单独的对象,每个对象只有一个键/和对(但以后就是这样)。

因此reduceMap开头并遍历这些组。我们需要从每个组中取出 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方法创建输出中所需的所有行。

答案 1 :(得分:3)

您可以使用辅助函数获取嵌套对象的值,并将值映射到同一索引。



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




答案 2 :(得分:2)

尝试以下方法:

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

答案 3 :(得分:1)

尝试将这类问题分解为更小的问题并逐步建立起来是一个好主意。这意味着我们不必一次看整件事。

让我们编写一个函数,将数组中的各个元素加在一起:

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

希望这有帮助!

答案 4 :(得分:1)

试试这个简单的小代码snipet:

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

答案 5 :(得分:1)

更短的选择:

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

答案 6 :(得分:1)

你问的内容基本上称为zipWith函数。因此,通用解决方案可以作为;

&#13;
&#13;
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);
&#13;
&#13;
&#13;

答案 7 :(得分:0)

此解决方案返回一个对象,每个键的值都相加。

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 }