Общие значения в массиве массивов — Lodash

#javascript #arrays #lodash

#javascript #массивы #Lodash

Вопрос:

У меня есть массив, который выглядит следующим образом:

 const myArray = [
  [
    {id: 1, name: 'Liam'},
    {id: 2, name: 'Oliver'},
    {id: 3, name: 'Jake'},
  ],
  [
    {id: 1, name: 'Liam'},
    {id: 2, name: 'Oliver'},
    {id: 4, name: 'Joe'},
  ],
]
 

Мне нужно найти общие элементы по идентификатору и вернуть их в массив, который будет выглядеть примерно так:

 [
  {id: 1, name: 'Liam'},
  {id: 2, name: 'Oliver'},
]
 

Если нет никакого способа сделать это с помощью Lodash, просто JS тоже может работать.
Обратите внимание, что я не знаю, сколько из этих массивов у меня будет внутри, поэтому он должен работать для любого числа.

Ответ №1:

Вы можете использовать Lodash _.intersectionBy() . Вам нужно будет распространить myArray , потому _intersectionBy() что ожидайте массивы в качестве аргументов, а не один массив массива:

 const myArray = [[{"id":1,"name":"Liam"},{"id":2,"name":"Oliver"},{"id":3,"name":"Jake"}],[{"id":1,"name":"Liam"},{"id":2,"name":"Oliver"},{"id":4,"name":"Joe"}]]

const result = _.intersectionBy(...myArray, 'id')

console.log(result) 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script> 

Комментарии:

1. Я уже пробовал это, но не распространял ‘myArray’, поэтому это дало мне неверные результаты. -.- Решаемая, спасибо!

2. Пожалуйста. Добавлена информация о распространении.

Ответ №2:

Ванильное решение может быть таким же простым, как filter() вызов первого элемента массива, проверяющий every() , содержит ли последующий элемент some() элементы, которые совпадают.

 const [srcElement, ...compArray] = [...myArray];

const intersection = srcElement.filter(o => (
  compArray.every(arr => arr.some(p => p.id === o.id)))
);

console.log(intersection) 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 
 <script>
const myArray = [
  [{ id: 1, name: 'Liam' }, { id: 2, name: 'Oliver' }, { id: 3, name: 'Jake' }],
  [{ id: 1, name: 'Liam' }, { id: 2, name: 'Oliver' }, { id: 4, name: 'Joe' }],
  [{ id: 1, name: 'Liam' }, { id: 2, name: 'Oliver' }, { id: 5, name: 'Dean' }, { id: 6, name: 'Mara' }]
]
</script> 

Ответ №3:

Используйте вложенные forEach циклы и Set . Просмотрите каждый подмассив и найдите общие элементы на данный момент.

 const intersection = ([firstArr, ...restArr]) => {
  let common = new Set(firstArr.map(({ id }) => id));
  restArr.forEach((arr) => {
    const newCommon = new Set();
    arr.forEach(({ id }) => common.has(id) amp;amp; newCommon.add(id));
    common = newCommon;
  });
  return firstArr.filter(({ id }) => common.has(id));
};

const myArray = [
  [
    { id: 1, name: "Liam" },
    { id: 2, name: "Oliver" },
    { id: 3, name: "Jake" },
  ],
  [
    { id: 1, name: "Liam" },
    { id: 2, name: "Oliver" },
    { id: 4, name: "Joe" },
  ],
    [
    { id: 2, name: "Oliver" },
    { id: 4, name: "Joe" },
  ],
];

console.log(intersection(myArray)); 

Ответ №4:

В настоящее время vanilla ES довольно мощен для функциональной работы с коллекциями даже без помощи библиотек утилит. Вы можете использовать обычные Array методы, чтобы получить чисто JS-решение. Я создал два примера с использованием чистого JS. Конечно, может быть и больше подходов. И если вы уже используете Lodash в своем приложении, вероятно, было бы лучше просто использовать его высокоуровневую реализацию в форме _.intersectionBy() , предложенной выше, чтобы уменьшить сложность кода.

 const myArray = [
  [
    {id: 1, name: 'Liam'},
    {id: 2, name: 'Oliver'},
    {id: 3, name: 'Jake'},
  ],
  [
    {id: 1, name: 'Liam'},
    {id: 2, name: 'Oliver'},
    {id: 4, name: 'Joe'},
  ],
];

// Regular functional filter-reduce
const reducer = (accum, x) => {
  return accum.findIndex(y => x.id == y.id) < 0
    ? [...accum, x]
    : accum;
};

const resultFilterReduce = myArray
  .flat()
  .filter(x => myArray.every(y => y.findIndex(obj => obj.id === x.id) > -1))
  .reduce(reducer, []);

console.log(resultFilterReduce);

// Filter-reduce with using of "HashMap" to remove duplicates
const resultWithHashMap = Object.values(
  myArray
    .flat()
    .filter(x => myArray.every(y => y.findIndex(obj => obj.id === x.id) > -1))
    .reduce((accum, x) => {
      accum[x.id] = x;
      return accum;
    }, {})
  );

console.log(resultWithHashMap);
 

Комментарии:

1. Они фактически возвращают плоский массив уникальных (по идентификатору) объектов, как и ожидалось в примере в вопросе.

2. Ожидаемый результат операции — это пересечение [{id: 1, имя: ‘Liam’}, {id: 2, имя: ‘Oliver’}] (элементы, которые появляются хотя бы один раз в каждом массиве). Вы возвращаете объединение [{id: 1, имя: ‘Liam’ }, {id: 2, имя: ‘Oliver’ }, {id: 3, имя: ‘Jake’ }, {id: 4, имя: ‘Joe’ }] (все элементы, которые появляются ввсе массивы).

3. @pilchard Вчера я был невнимателен, когда создал тупой ответ «найти уникальный». Спасибо, что осветили для меня проблему с реальной темой. Я отредактировал свой ответ, чтобы решить проблему в вопросе с помощью тех подходов с уменьшением фильтра и «хэш-карты», о которых я думал раньше, но правильно. 🙂