Javascript 根据一个键合并两个对象数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46849286/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:38:33  来源:igfitidea点击:

Merge two array of objects based on a key

javascript

提问by Adel

I have two arrays:

我有两个数组:

Array 1:

数组 1:

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

and array 2:

和数组 2:

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

I need to merge these two arrays based on idand get this:

我需要根据以下条件合并这两个数组id并得到:

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

How can I do this without iterating trough Object.keys?

我怎样才能在不迭代低谷的情况下做到这一点Object.keys

采纳答案by Rajaprabhu Aravindasamy

You can do it like this -

你可以这样做 -

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);



Use below code if arr1and arr2are in a different order:

如果arr1arr2的顺序不同,请使用以下代码:

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 merged = [];

for(let i=0; i<arr1.length; i++) {
  merged.push({
   ...arr1[i], 
   ...(arr2.find((itmInner) => itmInner.id === arr1[i].id))}
  );
}

console.log(merged);

Use this if arr1and arr2are in a same order

如果arr1arr2顺序相同,请使用此选项

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 merged = [];

for(let i=0; i<arr1.length; i++) {
  merged.push({
   ...arr1[i], 
   ...arr2[i]
  });
}

console.log(merged);

回答by Trevor Joseph

You can do this in one line

您可以在一行中完成此操作

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));

  1. Map over array1
  2. Search through array2 for array1.id
  3. If you find it ...spread the result of array2 into array1
  1. 映射到 array1
  2. 在 array2 中搜索 array1.id
  3. 如果你找到它......将array2的结果传播到array1

The final array will only contain id's that match from both arrays

最终的数组将只包含与两个数组匹配的 id

回答by Nina Scholz

You could use an arbitrary count of arrays and map on the same index new objects.

您可以使用任意数量的数组并映射到相同索引的新对象。

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; }

回答by Narcoleptic Snowman

Here's an O(n) solution using reduce and Object.assign

这是使用 reduce 和 Object.assign 的 O(n) 解决方案

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
            },
            {}
        )
    )

Each list gets reduced to a single object where the keys are ids and the values are the objects. If there's a value at the given key already, it gets object.assign called on it and the current record.

每个列表都被简化为单个对象,其中键是 id,值是对象。如果在给定的键上已经有一个值,它会调用 object.assign 和当前记录。

Here's the generic O(n*m) solution, where n is the number of records and m is the number of keys. This will only work for valid object keys. You can convert any value to base64 and use that if you need to.

这是通用的 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 )

This is essentially the same as the joinById method, except that it keeps an index object to identify records to join. The records are stored in an array and the index stores the position of the record for the given key set and the number of lists it's been found in.

这与 joinById 方法基本相同,只是它保留一个索引对象来标识要加入的记录。记录存储在数组中,索引存储给定键集的记录位置以及在其中找到的列表数。

Each time the same key set is encountered, the node is found in the tree, the element at it's index is updated, and the number of times it's been found is incremented.

每次遇到相同的键集时,都会在树中找到节点,更新其索引处的元素,并增加找到它的次数。

finally, the idx object is removed from the array with the slice, any elements that weren't found in each set are removed. This makes it an inner join, you could remove this filter and have a full outer join.

最后,idx 对象从带有切片的数组中删除,在每个集合中找不到的任何元素都将被删除。这使其成为内连接,您可以删除此过滤器并拥有完整的外连接。

finally each element is mapped to it's value, and you have the merged array.

最后,每个元素都映射到它的值,并且您有合并的数组。

回答by Kris Burke

To merge the two arrays on id, assuming the arrays are equal length:

要合并 上的两个数组id,假设数组长度相等:

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

回答by Renzo Calla

You can use array methods

您可以使用数组方法

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"}

回答by jsbisht

This solution is applicable even when you have different size of array being merged. Also, even if the keys on which match is happening has a different name.

即使您合并了不同大小的数组,此解决方案也适用。此外,即使发生匹配的键具有不同的名称。

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

Now to merge these use a Map as follows:

现在合并这些使用 Map 如下:

const map = new Map();
arr1.forEach(item => map.set(item.id, item));
arr2.forEach(item => map.set(item.nameId, {...map.get(item.nameId), ...item}));
const mergedArr = Array.from(map.values());

This should result in:

这应该导致:

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

回答by loretoparisi

You can recursively merge them into one as follows:

您可以递归地将它们合二为一,如下所示:

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))

回答by IanEdington

Non of these solutions worked for my case:

这些解决方案都不适用于我的案例:

  • missing objects can exist in either array
  • runtime complexity of O(n)
  • 缺少的对象可以存在于任一数组中
  • O(n) 的运行时复杂度

notes:

笔记:

  • I used lodash but it's easy to replace with something else
  • Also used Typescript (just remove/ignore the types)
  • 我使用了 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;
}

回答by Thadeus Ajayi

I was able to achieve this with a nested mapping of the two arrays and updating the initial array:

我能够通过两个数组的嵌套映射并更新初始数组来实现这一点:

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