使用 Lodash 按属性合并对象数组

IT技术 javascript arrays merge lodash
2021-03-19 23:57:42

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

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

6个回答

_.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 Little 后续问题:如何实现相同的结果,但是当数组中的对象包含另一个对象作为值时?例如:[ { label: 'private', value: {propa: 'some text', propb: 12}} ]我希望结果合并,以便如果标签相同,则原始值中的属性与新值中的属性合并。
2021-04-27 23:57:42
如果我有 20 个或更多数组怎么办?我找不到在循环中将数组作为参数动态传递的方法。有任何想法吗?
2021-05-06 23:57:42
那很干净
2021-05-08 23:57:42
如果两个数组中的属性名称不同怎么办?
2021-05-20 23:57:42

将列表转换为以 为键的对象,通过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>

确实如此;-) 有时您只是忽略了概述。
2021-04-27 23:57:42
完美运行,但使用 _.unionBy() 的解决方案似乎更简洁一些。谢谢 :-)
2021-05-15 23:57:42
是啊,我看到的时候几乎高兴得哭了。如此简单,感觉就像 lodash 对一切都有一个功能:)
2021-05-17 23:57:42

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

正确的方法是对两个数组进行排序并在两个数组中向前移动,合并匹配元素并添加两个数组中缺失的元素。请在下面找到完整的解决方案。这也需要 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);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

结果将是:

[ { 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 } ]

我知道这不是我们所要求的,但以防万一有人在此页面上偶然发现这里是您在 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);

如果您正在使用 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);