【问题标题】:Merge Array of Objects by Property using Lodash使用 Lodash 按属性合并对象数组
【发布时间】:2016-12-31 20:09:49
【问题描述】:

我有两个对象数组,它们代表具有标签和值的电子邮件地址:

var original = [
  {
    label: 'private',
    value: 'private@johndoe.com'
  },
  {
    label: 'work',
    value: 'work@johndoe.com'
  }
];

var update = [
  {
    label: 'private',
    value: 'me@johndoe.com'
  },
  {
    label: 'school',
    value: 'schhol@johndoe.com'
  }
];

现在我想通过label 字段来比较和合并两个数组,这样结果会如下所示:

var result = [
  {
    label: 'private',
    value: 'me@johndoe.com'
  },
  {
    label: 'work',
    value: 'work@johndoe.com'
  },
  {
    label: 'school',
    value: 'schol@johndoe.com'
  }
]

我该怎么做?使用lodash?

【问题讨论】:

标签: javascript arrays merge lodash


【解决方案1】:

_.unionBy():
此方法与 _.union 类似,不同之处在于它接受 iteratee,它为每个数组的每个元素调用以生成计算唯一性的标准。 结果值从出现值的第一个数组中选择

var original = [
  { label: 'private', value: 'private@johndoe.com' },
  { label: 'work', value: 'work@johndoe.com' }
];

var update = [
  { label: 'private', value: 'me@johndoe.com' },
  { label: 'school', value: 'schol@johndoe.com' }
];

var result = _.unionBy(update, original, "label");

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

【讨论】:

  • 很干净
  • @Andreas 小后续问题:我怎样才能达到相同的结果,但是当数组中的对象包含另一个对象作为值时?例如:[ { label: 'private', value: {propa: 'some text', propb: 12}} ] 我希望结果合并,这样如果标签相同,则原始值的属性与新值的属性合并。
  • 如果两个数组中的属性名不同怎么办?
  • 如果我有 20 个或更多阵列怎么办?我找不到在循环中动态地将数组作为参数传递的方法。有什么想法吗?
【解决方案2】:

将列表转换为以label 为键的对象,通过_.assign 合并它们,然后将其转换回数组。它甚至会在大多数浏览器上保留项目的顺序。

var original = [
  {
    label: 'private',
    value: 'private@johndoe.com'
  },
  {
    label: 'work',
    value: 'work@johndoe.com'
  }
];

var update = [
  {
    label: 'private',
    value: 'me@johndoe.com'
  },
  {
    label: 'school',
    value: 'schol@johndoe.com'
  }
];

console.log(
  _.map(
    _.assign(
      _.mapKeys(original, v => v.label),
      _.mapKeys(update, v => v.label)
    )
  )
);


// or remove more duplicated code using spread

console.log(
  _.map(
    _.assign(
      ...[original, update].map(
        coll => _.mapKeys(coll, v => v.label)
      )
    )
  )
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.js"></script>

【讨论】:

  • 完美运行,但使用 _.unionBy() 的解决方案似乎更干净一些。谢谢:-)
  • 是的,看到它的时候我都快哭了。太简单了,感觉lodash什么都有了:)
  • 是的 ;-) 有时你只是失去了概述。
【解决方案3】:

可能有点晚了,但是我看到的所有解决方案都没有正确连接两个数组,它们使用其中一个数组循环,并且第二个数组中的任何多余元素都不会被添加(假设这是是必需的)。

正确的方法是对两个数组进行排序并在两个数组中向前移动,合并匹配的元素并从两个数组中添加缺失的元素。 请在下面找到完整的解决方案。这也需要 O(n+m) 这是你能得到的最好的(没有排序本身的计算成本)。在我的代码中,我已经从数据库中排序了数据。

function mergeObjectsBasedOnKey(array1, array2, compareFn, mergeFn, alreadySorted) {
  var array1Index = 0;
  var array2Index = 0;

  const merged = [];

  if (!alreadySorted) {
    array1.sort(compareFn);
    array2.sort(compareFn);
  }

  while (array1Index < array1.length && array2Index < array2.length) {
    var comparedValue = compareFn(array1[array1Index], array2[array2Index]);
    if (comparedValue === 0) {
      merged.push(mergeFn(array1[array1Index], array2[array2Index]));
      array1Index++;
      array2Index++;
    } else if (comparedValue < 0) {
      merged.push(mergeFn(array1[array1Index]));
      array1Index++;
    } else {
      merged.push(mergeFn(array2[array2Index]));
      array2Index++;
    }
  }
  while (array1Index < array1.length) {
    merged.push(mergeFn(array1[array1Index]));
    array1Index++;
  }
  while (array2Index < array2.length) {
    merged.push(mergeFn(array2[array2Index]));
    array2Index++;
  }
  return merged;
}


const array1 = [{
    "id": 10,
    isArray1: true
  },
  {
    "id": 11,
    isArray1: true
  },
  {
    "id": 12,
    isArray1: true
  },
];
const array2 = [{
    "id": 8,
    isArray2: true
  },
  {
    "id": 11,
    isArray2: true
  },
  {
    "id": 15,
    isArray2: true
  },
];

const result = mergeObjectsBasedOnKey(array1, array2, function(a, b) {
  return a.id - b.id;
}, function(a, b) {
  if (b) {
    return _.merge(a, b);
  }
  return _.merge(a, {
    isArray1: true,
    isArray2: true
  });
});

console.log(result);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"&gt;&lt;/script&gt;

结果是:

[ { id: 8, isArray2: true, isArray1: true },
  { id: 10, isArray1: true, isArray2: true },
  { id: 11, isArray1: true, isArray2: true },
  { id: 12, isArray1: true, isArray2: true },
  { id: 15, isArray2: true, isArray1: true } ]

【讨论】:

    【解决方案4】:

    我知道这不是所要求的,但以防万一有人偶然发现此页面,这是您在 ramda 中执行此操作的方法:

    var original = [
      { label: 'private', value: 'private@johndoe.com' },
      { label: 'work', value: 'work@johndoe.com' }
    ];
    
    var updated = [
      { label: 'private', value: 'me@johndoe.com' },
      { label: 'school', value: 'schol@johndoe.com' }
    ];
    
    unionWith(eqBy(prop('label')), updated, original);
    

    【讨论】:

      【解决方案5】:

      如果您使用的 lodash 3.x 没有 _.unionBy(),您可以结合使用 _.union()_.uniq() 以获得相同的结果。

      var original = [
        { label: 'private', value: 'private@johndoe.com' },
        { label: 'work', value: 'work@johndoe.com' }
      ];
      
      var update = [
        { label: 'private', value: 'me@johndoe.com' },
        { label: 'school', value: 'schol@johndoe.com' }
      ];
      
      var result = _.uniq(_.union(update, original), "label"); 
      
      console.log(result);
      

      【讨论】:

        【解决方案6】:

        这是使用 Lodash 合并两个对象的另一种方法:

        let a = [{
            content: 'aaa',
            name: 'bbb2'
          },
          {
            content: 'aad',
            name: 'ccd'
          }
        ];
        
        let b = [{
            content: 'aaa',
            name: 'bbb'
          },
          {
            content: 'aad1',
            name: 'ccd1'
          }
        ];
        
        let c = [...a, ...b];
        
        let d = _.uniq(c, function(data) {
          return data.content;
        })
        
        console.log(d);
        &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"&gt;&lt;/script&gt;

        【讨论】:

          【解决方案7】:

          也许有点晚了,但是我看到的所有解决方案都没有正确连接两个数组,它们使用其中一个数组循环,并且第二个数组中的任何多余元素都不会被添加(假设这是是必需的)。

          我也有同样的观察,所以我自己整理了一些东西。这适用于我的用例,如果“标签”字段的值匹配,则合并每个对象:

          const dataSetHashes = dataSets.map(dataSet => _.keyBy(dataSet, 'label'))
          const resultHash = _.merge(
            {},
            ...dataSetLookups
          )
          const result = Object.values(resultLookup)
          

          【讨论】:

            猜你喜欢
            • 2017-03-12
            • 2018-08-28
            • 2019-05-07
            • 1970-01-01
            • 1970-01-01
            • 2019-05-30
            • 2015-12-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多