【问题标题】:Merge arrays from different objects with same key使用相同的键合并来自不同对象的数组
【发布时间】:2023-08-20 08:08:01
【问题描述】:

我有以下代码:

const blueData = {
    "items": [
        {
            "id": 35,
            "revision": 1,
            "updatedAt": "2021-09-10T14:29:54.595012Z",
        },
    ]
}

const redData = {}

const greenData = {
    "items": [
        {
            "id": 36,
            "revision": 1,
            "updatedAt": "2021-09-10T14:31:07.164368Z",
        }
    ]
}

let colorData = []
colorData = blueData.items ? [colorData, ...blueData.items] : colorData
colorData = redData.items ? [colorData, ...redData.items] : colorData
colorData = greenData.items ? [colorData, ...greenData.items] : colorData

我猜测扩展运算符在这里不是正确的方法,因为我在最终的 colorData 数组中得到了一些额外的数组。我只是想构建一个“项目”数组,其中包含 3 个对象中的所有“项目”。

这是 es6 控制台中该代码的链接:https://es6console.com/ktkhc3j2/

【问题讨论】:

    标签: javascript arrays object merge spread-syntax


    【解决方案1】:

    将您的数据放入一个数组中,然后使用flatMap 解开每个.items

    [greenData, redData, blueData].flatMap(d => d.items ?? [])
    //=> [ {id: 36, revision: 1, updatedAt: '2021-09-10T14:31:07.164368Z'}
    //=> , {id: 35, revision: 1, updatedAt: '2021-09-10T14:29:54.595012Z'}]
    

    如果你喜欢,你可以用一点咖喱来抽象d => d.items ?? [](不是双关语;)

    const take = k => o => o[k] ?? [];
    

    这给了我们:

    [greenData, redData, blueData].flatMap(take('items'))
    

    如果您需要使用不同的键重复此过程,我们甚至可以更进一步:

    const concatBy = fn => xs => xs.flatMap(x => fn(x));
    

    现在感觉就像是在用文字而不是代码来表达你的意图:

    const takeItems = concatBy(take('items'));
    
    takeItems([greenData, redData, blueData]);
    //=> [ {id: 36, revision: 1, updatedAt: '2021-09-10T14:31:07.164368Z'}
    //=> , {id: 35, revision: 1, updatedAt: '2021-09-
    

    让我们构建另一个函数:

    const takeFood = concatBy(take('food'));
    
    takeFood([{food: ['?', '?']}, {food: ['?', '?']}]);
    //=> ['?', '?', '?', '?']
    

    附录

    这只是一种潜在有用的学习材料。我的建议是使用flatMap

    这个:

    [[1, 2], [3, 4]].flatMap(x => x)
    //=> [1, 2, 3, 4]
    

    也可以用reduce表示。稍微冗长一些,但按照它在锡上所说的做:

    [[1, 2], [3, 4]].reduce((xs, x) => xs.concat(x), [])
    //=> [1, 2, 3, 4]
    

    简单地说,你也可以这样做:

    [greenData, redData, blueData].reduce((xs, x) => xs.concat(x.items ?? []), [])
    

    【讨论】:

      【解决方案2】:

      您可以使用Logical OR operator 执行此操作,如果缺少items 字段,您可以提供默认值。

      const blueData = { items: [ { id: 35, revision: 1, updatedAt: '2021-09-10T14:29:54.595012Z', }, ], };
      const redData = {};
      const greenData = { items: [ { id: 36, revision: 1, updatedAt: '2021-09-10T14:31:07.164368Z', }, ], };
      
      const colorData = [
        ...(blueData.items || []),
        ...(redData.items || []),
        ...(greenData.items || []),
      ];
      
      console.log(colorData);

      【讨论】:

        【解决方案3】:

        也许我有点过时,但我会使用concat

        concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat

        const blueData = {
            "items": [
                {
                    "id": 35,
                    "revision": 1,
                    "updatedAt": "2021-09-10T14:29:54.595012Z",
                },
            ]
        }
        
        const redData = {}
        
        const greenData = {
            "items": [
                {
                    "id": 36,
                    "revision": 1,
                    "updatedAt": "2021-09-10T14:31:07.164368Z",
                }
            ]
        }
        
        
        const colorData = [].concat(blueData.items,redData.items,greenData.items).filter(x => x)
        
        console.log(colorData)

        最后一个filter 用于删除未定义的值

        【讨论】:

          【解决方案4】:

          像这样?

          colorData = blueData.items ? [...colorData, ...blueData.items] : colorData
          colorData = redData.items ? [...colorData, ...redData.items] : colorData
          colorData = greenData.items ? [...colorData, ...greenData.items] : colorData
          

          输出:

          [{"id":35,"revision":1,"updatedAt":"2021-09-10T14:29:54.595012Z"}, 
          {"id":36,"revision":1,"updatedAt":"2021-09-10T14:31:07.164368Z"}]
          

          我认为您还需要将扩展​​运算符添加到 colorData 数组,因为如果不是,您将添加 colorData 数组本身,而不是它的项目。

          【讨论】:

          • 这是完美的(只需等待几分钟才能接受您的回答)。有没有更短的写法:colorData = blueData.items ? [...colorData, ...blueData.items] : colorData 如果三元条件为假,那么 colorData 就保持 colorData 不变?
          • 您可以查看Nullish coalescing operator。通过?? 更改||,您专门检查nullundefined 值,而不是包含nullundefined 的任何虚假值;)
          【解决方案5】:

          如果您想要最简单的解决方案,您可以在所有数组之间使用 for 循环进行迭代。创建一个临时数组,用于存储在每个索引上找到的数据。这是最快、最灵活的解决方案。

          var x1 = {
            "items": [
              { "testKey1": "testVal" }
            ] 
          };
          
          var x2 = {
            "items": [
              { "testKey2.0": "testVal2" },
              { "testKey2.1": "testVal2" },
              { "testKey2.2": "testVal2" },
            ] 
          };
          
          var x3 = {
            "items": [
              { "testKey3.0": "testVal3" },
              { "testKey3.1": "testVal3" }
            ] 
          };
          
          function combineArrays(...arrays) {
          
            var tempArray = [];
            for (let index in arrays) {
              let currentArray = arrays[index];
              for (let innerArrayIndex in currentArray) {
                tempArray.push(currentArray[innerArrayIndex]);
              }
            } 
            
            return tempArray;
          }
          
          var result = combineArrays(x1.items, x2.items, x3.items);
          console.log(result);

          使用扩展运算符的解决方案没有考虑到所有对象都将使用浅拷贝进行克隆。 Have a look.

          【讨论】:

          • 我认为OP更多的是针对具体问题和可读性的解决方案,无论如何非常好的文章+口袋妖怪示例! :)
          • The solutions using a spread operator do not take into consideration that all the objects will be cloned using a shallow copy. — 我不知道你为什么这么说;您当前的解决方案根本不会克隆任何东西。不过也不是说错!只是观察到您的解决方案也不是防突变的。
          • @customcommander 如果你想防突变,你可以浅拷贝(克隆)结果数组。在我看来,克隆每个数组然后合并似乎是一种开销。