Объединить массив объектов по свойству с помощью Lodash

У меня есть два массива объектов, которые представляют адреса электронной почты с меткой и значением:

var original = [
  {
    label: 'private',
    value: '[email protected]'
  },
  {
    label: 'work',
    value: '[email protected]'
  }
];

var update = [
  {
    label: 'private',
    value: '[email protected]'
  },
  {
    label: 'school',
    value: '[email protected]'
  }
];

Теперь я хочу сравнить и объединить два массива с помощью поля label, чтобы результат выглядел так:

var result = [
  {
    label: 'private',
    value: '[email protected]'
  },
  {
    label: 'work',
    value: '[email protected]'
  },
  {
    label: 'school',
    value: '[email protected]'
  }
]

Как я могу это сделать, например, используя lodash?

Ответы

Ответ 1

_.unionBy():
Этот метод похож на _.union, за исключением того, что он принимает итерацию, которая вызывается для каждого элемента каждого массива для генерации критерия, по которому вычисляется уникальность. Значения результатов выбираются из первого массива, в котором происходит значение.

var original = [
  { label: 'private', value: '[email protected]' },
  { label: 'work', value: '[email protected]' }
];

var update = [
  { label: 'private', value: '[email protected]' },
  { label: 'school', value: '[email protected]' }
];

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>

Ответ 2

Преобразуйте списки в объекты с ключом label, объедините их с помощью _.assign и преобразуйте их обратно в массив. Он даже сохранит порядок элементов в большинстве браузеров.

var original = [
  {
    label: 'private',
    value: '[email protected]'
  },
  {
    label: 'work',
    value: '[email protected]'
  }
];

var update = [
  {
    label: 'private',
    value: '[email protected]'
  },
  {
    label: 'school',
    value: '[email protected]'
  }
];

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>

Ответ 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);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Ответ 4

Здесь я создал еще один jsfiddle [https://jsfiddle.net/a0b8f6wg/][1], чтобы объединить два массива объектов, используя Lodash.

Ответ 5

Если вы используете lodash 3.x, где _. unionBy() не существует, вы можете объединить _.union() и _.uniq(), чтобы получить тот же результат.

var original = [
  { label: 'private', value: '[email protected]' },
  { label: 'work', value: '[email protected]' }
];

var update = [
  { label: 'private', value: '[email protected]' },
  { label: 'school', value: '[email protected]' }
];

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

console.log(result);