Фильтровать массив на основе другого массива и комбинировать - PullRequest
0 голосов
/ 03 января 2019

С учетом двух массивов:

const inputOne = [
 {id: "valueA", prop: 123},
 {id: "valueB", prop: 456}
]

const inputTwo = [
 {id: "valueA", other: 54},
 {id: "valueB", other: 98},
 {id: "valueC", other: 11}
]

Я пытаюсь отфильтровать inputTwo на основе inputOne id, а затем объединить свойства, найденные в обоих.

Желаемый вывод:

combinedAndFiltered = [
 {id: "valueA", other: 54, prop: 123},
 {id: "valueB", other: 98, prop: 456}
]

Я пробовал различные комбинации map, filter и / или reduce, но почему-то не могу понять это.

Ответы [ 8 ]

0 голосов
/ 03 января 2019

В этом решении предполагается, что будет только два набора, а свойство id уникально в каждом наборе.

Вы можете создать пересечение из двух наборов объединенных объектов на основедля общего свойства (id) с помощью:

  • Выполните итерацию первого набора, а затем найдите второй набор для текущего элемента первого набора.
  • Если совпадение найдено, объедините два объекта в новый объект, затем добавьте этот объект в набор аккумуляторов.
  • Возвращает аккумулятор

Этот метод возвращает новый набор, содержащий объединенные объекты, найденные в обоих наборах.Если объект отсутствует в любом наборе, он не будет включен в вывод.

const inputOne = [ {id: "valueA", prop: 123}, {id: "valueB", prop: 456}, {id: "valueD", prop: 789} ]
const inputTwo = [ {id: "valueA", other: 54}, {id: "valueB", other: 98}, {id: "valueC", other: 11} ]

function intersectAndMerge(a, b) {
  const accumulator = []
  for(let { id, ...props } of a) {
    const match = b.find(e =>  e.id === id)
    if(match !== undefined) {
      accumulator.push({ ...match, ...props })
    }
  }
  return accumulator
}

console.log(intersectAndMerge(inputOne, inputTwo))

Это также можно сделать с помощью цикла сокращения, но я считаю его менее читабельным:

const inputOne = [ {id: "valueA", prop: 123}, {id: "valueB", prop: 456}, {id: "valueD", prop: 789} ]
const inputTwo = [ {id: "valueA", other: 54}, {id: "valueB", other: 98}, {id: "valueC", other: 11} ]

function intersectAndMerge(a, b) {
  return a.reduce((accumulator, { id, ...props }) => {
    const match = b.find(e =>  e.id === id)
    if(match !== undefined) {
      accumulator.push({ ...match, ...props })
    }
    return accumulator
  }, [])
}

console.log(intersectAndMerge(inputOne, inputTwo))
0 голосов
/ 03 января 2019

Итак, вы можете сначала построить объект и использовать его для получения других значений, перебирая другой массив с .map

const inputOne = [
 {id: "valueA", prop: 123},
 {id: "valueB", prop: 456}
],
 inputTwo = [
 {id: "valueA", other: 54},
 {id: "valueB", other: 98},
 {id: "valueC", other: 11}
],
ids = {};

inputTwo.forEach(function (o) {
    ids[o.id] = o;
});

var res = inputOne.map(function (o) {
    return {
        other: ids[o.id].other,
        prop: o.prop,
        id: o.id
    };
});

console.log(res)
0 голосов
/ 03 января 2019

Я думаю, что почти все решения будут представлять собой разновидность двойной итерации, будь то цикл, карта или фильтр.Другой способ, отличный от перечисленных выше, - использовать библиотеку, подобную lodash. Этот ответ уже дает довольно хороший пример функции unionBy .

0 голосов
/ 03 января 2019

Это должно сработать (при условии, что inputOne действует как источник);

const inputOne = [
 {id: "valueA", prop: 123},
 {id: "valueB", prop: 456}
]

const inputTwo = [
 {id: "valueA", other: 54},
 {id: "valueB", other: 98},
 {id: "valueC", other: 11}
]

const mergeArrays = (first, second) => {
  return first.map((firstItem) => {
    const obj = second.find((secondItem) => secondItem.id === firstItem.id);
    return {...firstItem, ...obj};
  });
};

const combinedArrays = mergeArrays(inputOne, inputTwo);

console.log(combinedArrays);
0 голосов
/ 03 января 2019

Преобразуйте 1-й массив в Map , используя Array.reduce(), затем уменьшите 2-й массив, и, если объект найден на карте, получите объект с карты, объедините объекты и добавьте в аккумулятор:

const combine = (arr1, arr2) => {
  const arr1Map = arr1.reduce((m, o) => m.set(o.id, o), new Map)
  
  return arr2.reduce((r, o) => arr1Map.has(o.id) ? 
    [...r, { ...o, ...arr1Map.get(o.id) }] : r
  , [])
}

const inputOne = [{id: "valueA", prop: 123},{id: "valueB", prop: 456}]

const inputTwo = [{id: "valueA", other: 54},{id: "valueB", other: 98},{id: "valueC", other: 11}]

const result = combine(inputOne, inputTwo)

console.log(result)
0 голосов
/ 03 января 2019

Вы можете использовать карту и фильтр, чтобы решить эту проблему.Если я не ошибаюсь в том, что вы хотите, это может быть полезно

let result = inputOne.map((item) => {
    let leftItems = inputTwo.filter((item2) => {
        return item2.id == item.id
    }) 
    if(leftItems.length > 0) {
        return {id: item.id, other: leftItems[0].other, prop: item.prop}
        // or return {...item, ...leftItems[0]}
    }
}).filter(item => !! item)
0 голосов
/ 03 января 2019

Простое решение с использованием for:

// declare an empty array:
let resArr = []

for(i=0; i<inputOne.length; i++) {
  for(j=0; j<inputTwo.length; j++) {
    if(inputOne[i].id === inputTwo[j].id) {
      resArr.push({ ...inputOne[i], ...inputTwo[j] })
    }
  }
}
0 голосов
/ 03 января 2019
inputOne.map((a, i) => ({...a, ...inputTwo[i]}))

Предполагается, что inputOne является более коротким, вы можете использовать if, чтобы убедиться, что оно истинно во всех случаях.

EDIT: В действительно функциональном вопросе можно было бы использовать что-то вроде zip в haskell, но без дальнейших библиотек или самостоятельной реализации вы, я боюсь, застрянете с чем-то подобным.

РЕДАКТИРОВАТЬ 2:

inputOne.map((a, i) => ({...a, ...(inputTwo.filter(x => x.id === a.id)[0])}))

Это работает на основе свойства id, недостаточно хорошо читается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...