【问题标题】:Combine json arrays by key, javascript按键组合json数组,javascript
【发布时间】:2016-06-24 13:14:03
【问题描述】:

我需要组合两个由两个 rest 服务提供的 json 数组。具有相同“id”的条目属于一起。

json1 = [{id:1,name:'aaa'},
     {id:5,name:'ccc'},
     {id:3,name:'bbb'}
   ];

 json2 = [{id:3,parameter1:'x', parameter2:'y', parameter3:'z'},
     {id:1,parameter1:'u', parameter2:'v', parameter3:'w'},
     {id:5,parameter1:'q', parameter2:'w', parameter3:'e'}
    ];

我需要通过以下方式在 javascript 中组合/复制/克隆 json 数组(我在 angular2 中的模型):

json3 = [{id:3,name:'bbb',parameter1:'x', parameter2:'y',   parameter3:'z'},
     {id:1,name:'aaa', parameter1:'u', parameter2:'v', parameter3:'w'},
     {id:5,name:'ccc', parameter1:'q', parameter2:'w', parameter3:'e'}
    ];

有没有办法把它们结合起来?参数名称没有准确定义,需要使用可变参数向量。

我尝试了混合每个循环。在我看来很丑。

【问题讨论】:

标签: javascript arrays json node.js algorithm


【解决方案1】:

the two one-liners 的堆栈 sn-ps

洛达什

const json1 = [{ id: 1, name: 'aaa' }, { id: 3, name: 'bbb' },
  { id: 5, name: 'ccc' }];
const json2 = [{ id: 3, parameters: 'xyz' },
  { id: 5, parameters: 'qwe' }, { id: 1, parameters: 'uvw' }];
console.log('json1 BEFORE:\n' + JSON.stringify(json1));

const result_Lodash =
  _(json1).concat(json2).groupBy('id').map(_.spread(_.assign)).value();
console.log('Result, Lodash:\n' + JSON.stringify(result_Lodash));
console.log('json1 AFTER:\n' + JSON.stringify(json1));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js"></script>

Object.assign

const json1 = [{ id: 1, name: 'aaa' }, { id: 3, name: 'bbb' },
  { id: 5, name: 'ccc' }];
const json2 = [{ id: 3, parameters: 'xyz' },
  { id: 5, parameters: 'qwe' }, { id: 1, parameters: 'uvw' }];
console.log('json2 BEFORE:\n' + JSON.stringify(json2));

const result_Object_assign =
  json2.map(x => Object.assign(x, json1.find(y => y.id === x.id)));
console.log('Object.assign:\n' + JSON.stringify(result_Object_assign));
console.log('json2 AFTER:\n' + JSON.stringify(json2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js"></script>

值得指出的是,对于这两种解决方案,first的内容 /left 数组被改变了。
运行 sn-ps 即可查看。
例如,Object.assign(x, json1.find(y =&gt; y.id === x.id))复制 复合数据到json2中的对象xjson2中的对象是 因此通过.map()更新。
标识符result_Object_assign 实际上只是另一个指针 指向与json2 指向的数组相同的数组——没有新对象 已创建!


Object.assign 不更改输入数组
如果您不想更改任何输入数组,只需创建一个新的 数组,如下图:

const json1 = [{ id: 1, name: 'aaa' }, { id: 3, name: 'bbb' },
  { id: 5, name: 'ccc' }];
const json2 = [{ id: 3, parameters: 'xyz' },
  { id: 5, parameters: 'qwe' }, { id: 1, parameters: 'uvw' }];
console.log('json2 BEFORE:\n' + JSON.stringify(json2));

const result_Object_assign =
  json2.map(x => Object.assign({}, x, json1.find(y => y.id === x.id)));
console.log('Object.assign:\n' + JSON.stringify(result_Object_assign));
console.log('json2 AFTER:\n' + JSON.stringify(json2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js"></script>

【讨论】:

    【解决方案2】:

    JavaScript:

    let json1 = [
      { id: 1, name: 'aaa' },
      { id: 5, name: 'ccc' },
      { id: 3, name: 'bbb' }
    ];
    
    let json2 = [
      { id: 3, parameter1: 'x', parameter2: 'y', parameter3: 'z' },
      { id: 1, parameter1: 'u', parameter2: 'v', parameter3: 'w' },
      { id: 5, parameter1: 'q', parameter2: 'w', parameter3: 'e' }
    ];
    
    let json3 = [];
    
    json1.forEach((j1) => {
      json2.forEach((j2) => {
        if (j1.id === j2.id) {
          json3.push({ ...j1, ...j2 });
        }
      });
    });
    
    console.log(JSON.stringify(json3));
    .as-console-wrapper { top: 0; max-height: 100% !important; }

    【讨论】:

    【解决方案3】:

    如果您想编写它以便可以接收任意数量的数组,而不是 只有 2,您可以使用 arguments,并执行以下操作:

    var json1 = [{id:1,name:'aaa'},{id:5,name:'ccc'},{id:3,name:'bbb'}];
    
    var json2 = [{id:3,parameter1:'x', parameter2:'y', parameter3:'z'},
                 {id:1,parameter1:'u', parameter2:'v', parameter3:'w'},
                 {id:5,parameter1:'q', parameter2:'w', parameter3:'e'}];
    
    function joinObjects() {
      var idMap = {};
      // Iterate over arguments
      for(var i = 0; i < arguments.length; i++) {
        // Iterate over individual argument arrays (aka json1, json2)
        for(var j = 0; j < arguments[i].length; j++) {
          var currentID = arguments[i][j]['id'];
          if(!idMap[currentID]) {
            idMap[currentID] = {};
          }
          // Iterate over properties of objects in arrays (aka id, name, etc.)
          for(key in arguments[i][j]) {
            idMap[currentID][key] = arguments[i][j][key];
          }
        }
      }
      
      // push properties of idMap into an array
      var newArray = [];
      for(property in idMap) {
        newArray.push(idMap[property]);
      }
      return newArray;
    }
    
    var json3 = joinObjects(json1, json2);
    
    console.log(JSON.stringify(json3));
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    Here is a working codepen.

    【讨论】:

      【解决方案4】:

      the concat-reduce answers 的堆栈 sn-ps

      结果 1:使用 reduce 并匹配 id 的
      先将两个数组拼接成一个数组,然后检测合并 具有匹配 id 的对象。最后过滤掉所有null 值。

      结果 2:使用 reduce 和 Object.assign

      const json1 = [{id:1,name:'aaa'},{id:3,name:'bbb'},{id:5,name:'ccc'}];
      const json2 =
        [{id:3,parameters:'xyz'},{id:5,parameters:'qwe'},{id:1,parameters:'uvw'}];
      const json7 = json1.concat(json2);
      console.log(' json7:\n'+JSON.stringify(json7));
      
      const Result_1 =
        json7.reduce((accumulator, obj) => {
          if (!accumulator[obj.id]) {
            accumulator[obj.id] = obj;
          } else {
            for (proprty in obj) { accumulator[obj.id][proprty] = obj[proprty];}
          }
          return accumulator;
        }, []).filter(x => x);
      console.log('\n Result_1:\n' + JSON.stringify(Result_1));
      
      const Result_2 =
        json1.concat(json2).reduce((accumulator, obj) => {
          accumulator[obj.id] = Object.assign({}, accumulator[obj.id], obj);
          return accumulator;
        }, []).filter(x => x);
      console.log('\n Result_2:\n' + JSON.stringify(Result_2));
      .as-console-wrapper { max-height: 100% !important; top: 0; }

      参考资料:

      【讨论】:

        【解决方案5】:

        这是使用object-lib 的通用解决方案。

        优点是您可以完全控制对象的合并方式,这支持嵌套和多个嵌套 groupBys。

        // const objectLib = require('object-lib');
        
        const { Merge } = objectLib;
        
        const json1 = [{ id: 1, name: 'aaa' }, { id: 5, name: 'ccc' }, { id: 3, name: 'bbb' }];
        const json2 = [{ id: 3, parameter1: 'x', parameter2: 'y', parameter3: 'z' }, { id: 1, parameter1: 'u', parameter2: 'v', parameter3: 'w' }, { id: 5, parameter1: 'q', parameter2: 'w', parameter3: 'e' }];
        
        const groupById = Merge({ '[*]': 'id' });
        console.log(groupById(json1, json2));
        // => [ { id: 1, name: 'aaa', parameter1: 'u', parameter2: 'v', parameter3: 'w' }, { id: 5, name: 'ccc', parameter1: 'q', parameter2: 'w', parameter3: 'e' }, { id: 3, name: 'bbb', parameter1: 'x', parameter2: 'y', parameter3: 'z' } ]
        
        const o1 = [{ id: 1, children: [{ type: 'A' }, { type: 'C' }] }, { id: 5 }];
        const o2 = [{ id: 1, children: [{ type: 'A' }, { type: 'B' }] }, { id: 3 }];
        
        console.log(groupById(o1, o2));
        // => [ { id: 1, children: [ { type: 'A' }, { type: 'C' }, { type: 'A' }, { type: 'B' } ] }, { id: 5 }, { id: 3 } ]
        
        const groupByCustom = Merge({
          '[*]': 'id',
          '[*].children[*]': 'type'
        });
        console.log(groupByCustom(o1, o2));
        // => [ { id: 1, children: [ { type: 'A' }, { type: 'C' }, { type: 'B' } ] }, { id: 5 }, { id: 3 } ]
        .as-console-wrapper {max-height: 100% !important; top: 0}
        &lt;script src="https://bundle.run/object-lib@2.0.0"&gt;&lt;/script&gt;

        免责声明:我是object-lib的作者

        【讨论】:

          【解决方案6】:

          let json1 = [
            { id: 1, name: 'aaa' },
            { id: 5, name: 'ccc' },
            { id: 3, name: 'bbb' }
          ];
          
          let json2 = [
            { id: 3, parameter1: 'x', parameter2: 'y', parameter3: 'z' },
            { id: 1, parameter1: 'u', parameter2: 'v', parameter3: 'w' },
            { id: 5, parameter1: 'q', parameter2: 'w', parameter3: 'e' }
          ];
          
          let result = json1.map(obj => {
            let data = json2.find(item => item.id === obj.id);
            return {...obj, ...data}
          });
          
          console.log(result);
          .as-console-wrapper { top: 0; max-height: 100% !important; }

          【讨论】:

          【解决方案7】:

          使用嵌套循环查找对应的元素并将它们合并。

          for (var i = 0; i < json1.length; i++) {
              var id = json1[i].id;
              for (var j = 0; j < json2.length; j++) {
                  if (json2[j].id == id) {
                      for (var key in json2[j]) {
                          json1[i][key] = json2[j][key];
                      }
                      break;
                  }
              }
          }
          

          最后,json1 将包含组合元素。

          上面的代码假定json2 的每个元素都匹配json1 中的某些内容。如果json2 中可能有额外的元素,您将需要一个额外的循环来将它们复制到json1

          【讨论】:

          • 在大数据集上,将较小的数组转换为稀疏数组(使用id作为索引)并切割嵌套循环会更有效。
          • 是的,我平时就是这样写的,这次我决定写速写版。
          【解决方案8】:

          使用 forEachfilter 我们可以解决需求。

          vehicleArray1 = [{id:1, name: "a"},{id:2, name: "b"},{id:3, name:"c"}];
          vehicleArray2 = [{id:1, type: "two wheeler"},{id:2, type: "four wheeler"},{id:3, type:"six wheeler"}];
          var outArr = [];
          vehicleArray1.forEach(function(value) {
              var existing = vehicleArray2.filter(function(v, i) {
                  return (v.id == value.id);
              });
              if (existing.length) {
                  value.type = existing[0].type;
                  outArr.push(value)
              } else {
                  value.type = '';
                  outArr.push(value);
              }
          });
          console.log(outArr)

          【讨论】:

            【解决方案9】:

            ES2015 georg 的回答效果很好;

                json1 = [
                {id:1, test: 0},
                {id:2, test: 0},
                {id:3, test: 0},
                {id:4, test: 0},
                {id:5, test: 0}
            ];
            
            json2 = [
                {id:1, test: 1},
                {id:3, test: 1},
                {id:5, test: 1}
            ];
            
            json1.map(x => Object.assign(x, json2.find(y => y.id == x.id)));
            

            结果:

            {id:1, test: 1},
            {id:2, test: 0},
            {id:3, test: 1},
            {id:4, test: 0},
            {id:5, test: 1}
            

            【讨论】:

            • 这没有达到问题中的要求。
            【解决方案10】:

            这是一种方法,您首先构建一个以 id(稀疏数组)为键的索引,以检测和组合具有匹配 id 值的对象,然后最终将它们连接回来放入普通数组:

            json3 = json1.concat(json2).reduce(function(index, obj) {
                if (!index[obj.id]) {
                    index[obj.id] = obj;
                } else {
                    for (prop in obj) {
                        index[obj.id][prop] = obj[prop];
                    }
                }
                return index;
            }, []).filter(function(res, obj) {
                return obj;
            });
            

            json1 = [
                {id:1,name:'aaa'},
                {id:5,name:'ccc'},
                {id:3,name:'bbb'}
            ];
            
            json2 = [
                {id:3,parameter1:'x', parameter2:'y', parameter3:'z'},
                {id:1,parameter1:'u', parameter2:'v', parameter3:'w'},
                {id:5,parameter1:'q', parameter2:'w', parameter3:'e'}
            ];
            
            json3 = json1.concat(json2).reduce(function(index, obj) {
                if (!index[obj.id]) {
                    index[obj.id] = obj;
                } else {
                    for (prop in obj) {
                        index[obj.id][prop] = obj[prop];
                    }
                }
                return index;
            }, []).filter(function(res, obj) {
                return obj;
            });
            
            document.write('<pre>', JSON.stringify(json3, null, 4), '</pre>');

            如果您的浏览器支持Object.assign:

            json3 = json1.concat(json2).reduce(function(index, obj) {
                index[obj.id] = Object.assign({}, obj, index[obj.id]);
                return index;
            }, []).filter(function(res, obj) {
                return obj;
            });
            

            【讨论】:

              【解决方案11】:

              两个单行:

              用lodash:

              res = _(json1).concat(json2).groupBy('id').map(_.spread(_.assign)).value();
              

              在 ES2015 中:

              res = json2.map(x => Object.assign(x, json1.find(y => y.id == x.id)));
              

              【讨论】:

              • 我昨晚用 8 行大约 2 小时为此编写了一组函数。我只使用了地图和过滤器。我怎么会忘记还有其他 Array 方法?!支持 Object.assign 虽然也没有想到这一点。使用扩展运算符。你的分类更清晰,更容易阅读。
              • 下划线是否有等价物?
              • 有没有办法合并来自 json2 的新项目,不仅两者都存在?
              • @Joel 我刚刚使用 Ramda 找到了这个 outerJoin 示例:github.com/ramda/ramda/wiki/Cookbook#sql-style-joinsconst joinOuter = R.curry((f1, f2, t1, t2) =&gt; { let o1 = R.indexBy(f1, t1); let o2 = R.indexBy(f2, t2); return R.values(R.mergeWith(R.merge, o1, o2)); }); // usage: joinOuter(R.prop('id'), R.prop('buyer'), people, transactions) // result: // [{ id: 1, name: 'me', buyer: 1, seller: 10 }, // { buyer: 2, seller: 5 }, // { id: 3, name: 'you' }]
              • 我发现 Lodash 解决方案是一个完全外连接,这意味着json1json2 中的每个项目都合并到结果 - 无论该项目是否也在另一个数组中。 Object.assign 解决方案是一个左外连接,这意味着来自json1所有 个项目进入结果,但json2 中的项目仅当它们也存在于json1 中时才加入结果中。有关提供真正的内部连接的解决方案,请参阅this answer
              【解决方案12】:

              这应该为你做。我希望代码本身是有意义的。如果两者都存在,此示例将始终采用 json1 值而不是 json2 值。如果要更改,则需要在最里面的循环中切换对象引用(src[i]obj[j])。

              // Will take src, and merge in the contents of obj.
              // Expects an array of objects for both.
              // Will keep src values in favour of obj values.
              function extend(src, obj) {
                
                // Loop the src, in this case json1
                for (var i = 0; i < src.length; i++) {
                  
                  // For every loop of json1, also loop json2
                  for (var j = 0; j < obj.length; j++) {
                    
                    // If we have matching IDs operate on this pair
                    if (src[i].id == obj[j].id) {
                        
                      // For every key in the object being merged in,
                      // if the key exists in src, ignore new value.
                      // if the doesn't exist in src, take the new value.
                      for (var key in obj[j]) {
                        src[i][key] = src[i].hasOwnProperty(key) ? src[i][key] : obj[j][key];
                      }
                      
                      // We found our matching pair, so break out of the json2 loop
                      break;
                      
                    }
                    
                  }
                  
                }
                
                return src;
              }
              
              // -------------------------------------------
              
              var json1 = [{
                id: 1,
                name: 'aaa'
              },{
                id: 5,
                name: 'ccc'
              },{
                id: 3,
                name: 'bbb'
              }];
              
              var json2 = [{
                id: 3,
                parameter1: 'x', 
                parameter2: 'y', 
                parameter3: 'z'
              },{
                id: 1,
                parameter1: 'u', 
                parameter2: 'v', 
                parameter3: 'w'
              },{
                id: 5,
                parameter1: 'q', 
                parameter2: 'w', 
                parameter3: 'e'
              }];
              
              var json3 = extend(json1, json2);
              
              // ---------------------------------------------
              
              var pre = document.getElementById('out');
              pre.innerHTML = JSON.stringify(json3);
              &lt;pre id="out"&gt;&lt;/pre&gt;

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2015-12-05
                • 2021-12-03
                • 2015-01-20
                • 2021-02-23
                • 1970-01-01
                • 2017-02-25
                • 1970-01-01
                • 2016-12-30
                相关资源
                最近更新 更多