【问题标题】:How to use Lodash to merge two collections based on a key?如何使用 Lodash 根据一个键合并两个集合?
【发布时间】:2016-05-07 15:04:40
【问题描述】:

我有两个集合,对象有一个公共键“userId”。如下:

var _= require('lodash');

var a = [
  { userId:"p1", item:1},
  { userId:"p2", item:2},
  { userId:"p3", item:4}
];

var b = [
  { userId:"p1", profile:1},
  { userId:"p2", profile:2}
];

我想根据“userId”合并它们来产生:

[ { userId: 'p1', item: 1, profile: 1 },
  { userId: 'p2', item: 2, profile:2 },
  { userId: 'p3', item: 4 } ]

到目前为止我有这些:

var u = _.uniq(_.union(a, b), false, _.property('userId'));

结果:

[ { userId: 'p1', item: 1 },
  { userId: 'p2', item: 2 },
  { userId: 'p3', item: 4 },
  { userId: 'p1', profile: 1 },
  { userId: 'p2', profile: 2 } ]

我现在如何合并它们?

我试过 _.keyBy 但结果是:

{ p1: { userId: 'p1', profile: 1 },
  p2: { userId: 'p2', profile: 2 },
  p3: { userId: 'p3', item: 4 } }

这是错误的。

我应该做的最后一步是什么?

【问题讨论】:

    标签: javascript collections merge lodash


    【解决方案1】:

    投票第二高的答案没有正确合并。如果第二个数组包含唯一属性,则不予考虑。

    这种方法可以进行适当的合并。

    洛达什

    var a = [
      { userId:"p1", item:1},
      { userId:"p2", item:2},
      { userId:"p3", item:4}
    ];
    
    var b = [
      { userId:"p1", profile:1},
      { userId:"p2", profile:2},
      { userId:"p4", profile:4}
    ];
    var merged = _.merge(_.keyBy(a, 'userId'), _.keyBy(b, 'userId'));
    var values = _.values(merged);
    console.log(values);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

    ES6+

    // from https://stackoverflow.com/a/34749873/80766
    const mergeDeep = (target, ...sources) => {
      if (!sources.length) return target;
      const source = sources.shift();
    
      if (target instanceof Object && source instanceof Object) {
        for (const key in source) {
          if (source[key] instanceof Object) {
            if (!target[key]) Object.assign(target, { [key]: {} });
            mergeDeep(target[key], source[key]);
          } else {
            Object.assign(target, { [key]: source[key] });
          }
        }
      }
    
      return mergeDeep(target, ...sources);
    }
    
    const a = [
      { userId:"p1", item:1},
      { userId:"p2", item:2},
      { userId:"p3", item:4}
    ];
    
    const b = [
      { userId:"p1", profile:1},
      { userId:"p2", profile:2},
      { userId:"p4", profile:4}
    ];
    
    
    const aKeyed = a.reduce((acc, cur) => ({ ...acc, [cur.userId]: cur }), {});
    const bKeyed = b.reduce((acc, cur) => ({ ...acc, [cur.userId]: cur }), {});
    const merged = mergeDeep(aKeyed, bKeyed);
    const values = Object.values(merged);
    console.log(values);

    【讨论】:

    • 这可能不适合所有场景,如果userId 字段在每个集合中不是唯一的,则数据将被压缩到单个对象中。
    【解决方案2】:

    没有 lodash 的 ES6+ 版本。

     const array1 = [{ userId: "p1", item: 1 },  { userId: "p2", item: 2 },{ userId: "p3", item: 4 }];
    const array2 = [{ userId: "p1", profile: 1 }, { userId: "p2", profile: 2 }];
    
    
    const result = array1.map(a => ({
      ...a,
      ...array2.find(b => b.userId === a.userId) // _.find(array2, 'skuId') <-- or with lodash 
    }));
    
     document.write('<pre>' + JSON.stringify(result, 0, 2) + '</pre>');   

    【讨论】:

      【解决方案3】:

      Lodash 有一个merge 方法可以作用于对象(具有相同键的对象被合并)。在这个演示中,数组ab首先被转换成对象(其中userId是key),然后合并,结果转换回数组(_.values)(去掉key )。然后_.flatten 是必要的,因为_.values 添加了额外级别的数组。

      var u= _({}) // Start with an empty object
        .merge(
          _(a).groupBy("userId").value(),
          _(b).groupBy("userId").value()
        )
        .values()
        .flatten()
        .value();
      

      【讨论】:

        【解决方案4】:

        您可以使用_.map()_.assign()_.find()

        // Iterate over first array of objects
        _.map(a, function(obj) {
        
            // add the properties from second array matching the userID
            // to the object from first array and return the updated object
            return _.assign(obj, _.find(b, {userId: obj.userId}));
        });
        

        Fiddle Demo

        var a = [{
            userId: "p1",
            item: 1
        }, {
            userId: "p2",
            item: 2
        }, {
            userId: "p3",
            item: 4
        }];
        
        var b = [{
            userId: "p1",
            profile: 1
        }, {
            userId: "p2",
            profile: 2
        }];
        
        var arrResult = _.map(a, function(obj) {
            return _.assign(obj, _.find(b, {
                userId: obj.userId
            }));
        });
        
        console.log(arrResult);
        document.getElementById('result').innerHTML = JSON.stringify(arrResult, 0, 4);
        <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.1.0/lodash.min.js"></script>
        <pre id="result"></pre>

        【讨论】:

        • 只是想一想,如果第二个列表包含唯一的 userId,则不会将其添加到最终列表中。我怀疑这是 OP 不想要的行为。
        • 这些功能现在在最新版本的 JS 中可用。 a.map(obj =&gt; Object.assign(obj, b.find(user =&gt; obj.userId === user.userId)))
        【解决方案5】:

        仅出于完整性考虑:没有任何库的提案。

        function merge(a, b, key) {
        
            function x(a) {
                a.forEach(function (b) {
                    if (!(b[key] in obj)) {
                        obj[b[key]] = obj[b[key]] || {};
                        array.push(obj[b[key]]);
                    }
                    Object.keys(b).forEach(function (k) {
                        obj[b[key]][k] = b[k];
                    });
                });
            }
        
            var array = [],
                obj = {};
        
            x(a);
            x(b);
            return array;
        }
        
        var a = [
                { userId: "p1", item: 1 },
                { userId: "p2", item: 2 },
                { userId: "p3", item: 4 }
            ],
            b = [
                { userId: "p1", profile: 1 },
                { userId: "p2", profile: 2 }
            ],
            c = merge(a, b, 'userId');
        
        document.write('<pre>' + JSON.stringify(c, 0, 4) + '</pre>');

        【讨论】:

          【解决方案6】:

          试试这个demo

          var a = [{
              userId: "p1",
              item: 1
          }, {
              userId: "p2",
              item: 2
          }, {
              userId: "p3",
              item: 4
          }];
          
          var b = [{
              userId: "p1",
              profile: 1
          }, {
              userId: "p2",
              profile: 2
          }];
          
          a.forEach(function (aitem) {
              b.forEach(function (bitem) {
                  if(aitem.userId === bitem.userId) {
                      _.assign(aitem, bitem);
                  }
              });
          });
          
          console.log(a);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-08-02
            • 1970-01-01
            • 2020-10-13
            • 2011-11-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-08-06
            相关资源
            最近更新 更多