Объединить два массива объектов на основе ключа

У меня два массива:

Массив 1:

[
  { id: "abdc4051", date: "2017-01-24" }, 
  { id: "abdc4052", date: "2017-01-22" }
]

и массив 2:

[
  { id: "abdc4051", name: "ab" },
  { id: "abdc4052", name: "abc" }
]

Мне нужно объединить эти два массива на основе id и получить следующее:

[
  { id: "abdc4051", date: "2017-01-24", name: "ab" },
  { id: "abdc4052", date: "2017-01-22", name: "abc" }
]

Как я могу это сделать без итерации через Object.keys?

Ответ 1

Вы можете сделать это так -

let arr1 = [
    { id: "abdc4051", date: "2017-01-24" },
    { id: "abdc4052", date: "2017-01-22" }
];

let arr2 = [
    { id: "abdc4051", name: "ab" },
    { id: "abdc4052", name: "abc" }
];

let arr3 = arr1.map((item, i) => Object.assign({}, item, arr2[i]));

console.log(arr3);

Ответ 2

Вы можете сделать это в одну строку

let arr1 = [
    { id: "abdc4051", date: "2017-01-24" },
    { id: "abdc4052", date: "2017-01-22" }
];

let arr2 = [
    { id: "abdc4051", name: "ab" },
    { id: "abdc4052", name: "abc" }
];

const mergeById = (a1, a2) =>
    a1.map(itm => ({
        ...a2.find((item) => (item.id === itm.id) && item),
        ...itm
    }));

console.log(mergeById(arr1, arr2));

Ответ 3

Вы можете использовать произвольное количество массивов и отобразить на том же индексе новые объекты.

var array1 = [{ id: "abdc4051", date: "2017-01-24" }, { id: "abdc4052", date: "2017-01-22" }],
    array2 = [{ id: "abdc4051", name: "ab" }, { id: "abdc4052", name: "abc" }],
    result = [array1, array2].reduce((a, b) => a.map((c, i) => Object.assign({}, c, b[i])));
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Ответ 4

Вы можете использовать методы массива

let arrayA=[
{id: "abdc4051", date: "2017-01-24"},
{id: "abdc4052", date: "2017-01-22"}]

let arrayB=[
{id: "abdc4051", name: "ab"},
{id: "abdc4052", name: "abc"}]

let arrayC = [];


function isBiggerThan10(element, index, array) {
  return element > 10;
}

arrayA.forEach(function(element){
  arrayC.push({
  id:element.id,
  date:element.date,
  name:(arrayB.find(e=>e.id===element.id)).name
  });  
});

console.log(arrayC);

//0:{id: "abdc4051", date: "2017-01-24", name: "ab"}
//1:{id: "abdc4052", date: "2017-01-22", name: "abc"}

Ответ 5

Вы можете рекурсивно объединить их в одно:

function mergeRecursive(obj1, obj2) {
    for (var p in obj2) {
        try {
            // Property in destination object set; update its value.
            if (obj2[p].constructor == Object) {
                obj1[p] = this.mergeRecursive(obj1[p], obj2[p]);

            } else {
                obj1[p] = obj2[p];

            }

        } catch (e) {
            obj1[p] = obj2[p];

        }
    }
    return obj1;
}

arr1 = [
    { id: "abdc4051", date: "2017-01-24" },
    { id: "abdc4052", date: "2017-01-22" }
];
arr2 = [
    { id: "abdc4051", name: "ab" },
    { id: "abdc4052", name: "abc" }
];

mergeRecursive(arr1, arr2)
console.log(JSON.stringify(arr1))

Ответ 6

Чтобы объединить два массива по id, предполагая, что массивы равной длины:

arr1.map(item => ({
    ...item,
    ...arr2.find(({ id }) => id === item.id),
}));

Ответ 7

Ни одно из этих решений не сработало для моего случая:

  • отсутствующие объекты могут существовать в любом массиве
  • Сложность выполнения O (n)

примечания:

  • Я использовал lodash, но его легко заменить чем-то другим
  • Также используется Typescript (просто удаляйте/игнорируйте типы)
import { keyBy, values } from 'lodash';

interface IStringTMap<T> {
  [key: string]: T;
}

type IIdentified = {
  id?: string | number;
};

export function mergeArrayById<T extends IIdentified>(
  array1: T[],
  array2: T[]
): T[] {
  const mergedObjectMap: IStringTMap<T> = keyBy(array1, 'id');

  const finalArray: T[] = [];

  for (const object of array2) {
    if (object.id && mergedObjectMap[object.id]) {
      mergedObjectMap[object.id] = {
        ...mergedObjectMap[object.id],
        ...object,
      };
    } else {
      finalArray.push(object);
    }
  }

  values(mergedObjectMap).forEach(object => {
    finalArray.push(object);
  });

  return finalArray;
}

Ответ 8

Ну... если оба массива имеют одинаковую длину, я бы, вероятно, сделал бы что-то вроде этого:

var newArr = []
for (var i = 0; i < array1.length; i++ {
    if (array1[i].id === array2[i].id) {
      newArr.push({id: array1[i].id, date: array1[i].date, name: array2[i].name});
  }
}

Ответ 9

Использовать проект с открытым исходным кодом jinqJs очень просто -

//Use jsJinq.com open source library
var list1= [{Location: 'NY', People: 200}, {Location: 'TX', People: 500}];
var list2= [{Location: 'NY', State: 'New York'}, {Location: 'TX', State: 'Texas'}]

var result = new jinqJs().from(list1).join(list2).on('Location').select();

document.body.innerHTML += '<pre>' + JSON.stringify(result, null, 4) + '</pre>';
<script src="https://rawgit.com/fordth/jinqJs/master/jinqjs.js"></script>

Ответ 10

Я смог добиться этого с помощью вложенного отображения двух массивов и обновления исходного массива:

member.map(mem => {
return memberInfo.map(info => {
    if (info.id === mem.userId) {
        mem.date = info.date;
        return mem;
        }
    }
}

Ответ 11

Здесь решение O (n), использующее Reduce и Object.assign

const joinById = ( ...lists ) =>
    Object.values(
        lists.reduce(
            ( idx, list ) => {
                list.forEach( ( recod ) => {
                    if( idx[ recod.id ] )
                        idx[ recod.id ] = Object.assign( idx[ recod.id ], recod )
                    else
                        idx[ recod.id ] = recod
                } )
                return idx
            },
            {}
        )
    )

Каждый список сводится к одному объекту, где ключи - это идентификаторы, а значения - это объекты. Если в данном ключе уже есть значение, для него вызывается object.assign и текущая запись.

Здесь общее решение O (n * m), где n - количество записей, а m - количество ключей. Это будет работать только для действительных ключей объекта. Вы можете преобразовать любое значение в base64 и использовать его, если вам нужно.

const join = ( keys, ...lists ) =>
    lists.reduce(
        ( res, list ) => {
            list.forEach( ( record ) => {
                let hasNode = keys.reduce(
                    ( idx, key ) => idx && idx[ record[ key ] ],
                    res[ 0 ].tree
                )
                if( hasNode ) {
                    const i = hasNode.i
                    Object.assign( res[ i ].value, record )
                    res[ i ].found++
                } else {
                    let node = keys.reduce( ( idx, key ) => {
                        if( idx[ record[ key ] ] )
                            return idx[ record[ key ] ]
                        else
                            idx[ record[ key ] ] = {}
                        return idx[ record[ key ] ]
                    }, res[ 0 ].tree )
                    node.i = res[ 0 ].i++
                    res[ node.i ] = {
                        found: 1,
                        value: record
                    }
                }
            } )
            return res
        },
        [ { i: 1, tree: {} } ]
         )
         .slice( 1 )
         .filter( node => node.found === lists.length )
         .map( n => n.value )

По сути, это то же самое, что и метод joinById, за исключением того, что он сохраняет индексный объект для идентификации записей, которые нужно объединить. Записи хранятся в массиве, а индекс хранит позицию записи для данного набора ключей и количество списков, в которых он был найден.

Каждый раз, когда встречается один и тот же набор ключей, узел обнаруживается в дереве, элемент с индексом обновляется, и число раз, когда он был найден, увеличивается.

наконец, объект idx удаляется из массива со срезом, все элементы, которые не были найдены в каждом наборе, удаляются. Это делает его внутренним соединением, вы можете удалить этот фильтр и получить full outer join.

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

Ответ 12

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

let arr1 = [
    { id:"1", value:"this", other: "that" },
    { id:"2", value:"this", other: "that" }
];

let arr2 = [
    { id:"2", key:"val2"},
    { id:"1", key:"val1"}
];

вы можете сделать это

const result = arr1.map(item => {
    const obj = arr2.find(o => o.id === item.id);
    return { ...item, ...obj };
  });

console.log(result);