代码之家  ›  专栏  ›  技术社区  ›  Dženis H.

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

  •  6
  • Dženis H.  · 技术社区  · 6 年前

    我有一系列的东西,像这样:

    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 在上面绘制地图我得到了净税收数据。

    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 回复  |  直到 6 年前
        1
  •  3
  •   Nina Scholz    6 年前

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

    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
  •  4
  •   trincot Jakube    6 年前

    这里是一个 函数式程序设计 方法是,使用中级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 . 对于初始值 减少 呼叫我已通过一个空的 地图 . 其目的是用键和对填充该映射。然后,我们可以将该映射分解成单独的对象,每个对象只有那些密钥/和对(但稍后)。

    所以 减少 地图 并在组上迭代。我们需要把 从每个组中查找必须“添加”的对象的行。所以我们坐这排 grp[i] .

    对于该行中的每个对象,我们都可以使用 Object.entries(o) . 实际上,该函数返回一个数组,因此我们用 forEach 知道我们实际上只迭代一次,因为实际上只有一个属性。现在我们有钥匙了( k 和价值 v . 我们处于输入结构的最底层。在这里我们调整蓄能器。

    acc.get(k) 我们可以知道我们已经为某个特定的键积累了什么(例如“X”)。如果我们还没有,它会被初始化为0 || 0 . 然后我们加上当前值 V 把这些钱存回地图上 acc.set(k, ....) . 使用逗号运算符返回 acc 回到 减少 实现(我们可以使用 return 但逗号运算符更简洁)。

    因此映射得到每个键的所有和。用 Array.from 我们可以迭代这些键/和对,并使用回调参数,将这对变成一个适当的小对象(用 { [k]: v } )这个 [k] 符号在ES6中也是一个新事物——它允许对象文本中的动态键名。

    所以… 来自… 返回一个小对象数组,每个对象都有一个和。该数组表示要输出的一行。这个 map 方法创建输出中所需的所有行。

        3
  •  2
  •   amrender singh    6 年前

    请尝试以下操作:

    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);
        4
  •  1
  •   Duncan Thacker    6 年前

    尝试将这类问题分解成更小的问题,并逐渐积累起来,这是一个好主意。这意味着我们不必一蹴而就。

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

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

    希望这有帮助!

        5
  •  1
  •   Numan Ijaz    6 年前

    试试这个简单的小代码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);
    
        6
  •  1
  •   Redu    6 年前

    你所问的基本上就是 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);
        7
  •  1
  •   Slai    6 年前

    短一点的选择:

    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 ') );
        8
  •  0
  •   Flavio    6 年前

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

    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 }