繁体   English   中英

根据键合并两个对象数组

[英]Merge two array of objects based on a key

我有两个数组:

阵列 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进行迭代,我该如何做到这一点?

你可以这样做 -

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


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

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

您可以在一行中执行此操作

 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. 在array1上映射
  2. 在 array2 中搜索 array1.id
  3. 如果你找到它......将array2的结果传播到array1

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

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

使用 Map 合并两个数组,如下所示:

 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" } ]; 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()); console.log(JSON.stringify(mergedArr));
 .as-console-wrapper { max-height: 100% !important; top: 0; }

运行堆栈片段以查看结果:

[
  {
    "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"
  }
]

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

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

要将此函数用于 OP 的情况,请将要加入的数组传递给 joinById (注意列表是一个休息参数)。

let joined = joinById(list1, list2)

每个列表都被简化为一个对象,其中键是 id,值是对象。 如果给定键已经有值,它会调用 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 对象从带有切片的数组中删除,并且在每个集合中未找到的任何元素都将被删除。 这使其成为内部联接,您可以删除此过滤器并进行完整的外部联接。

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

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

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

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

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

如果您有 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);

我们可以在这里使用 lodash。 _.merge 按您的预期工作。 它与现有的公共密钥一起使用。

_.merge(array1, array2)

您可以使用数组方法

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

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

  • 丢失的对象可以存在于任一数组中
  • 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;
}

这是单线(数组中元素的顺序并不重要,假设存在 1 对 1 的关系):

var newArray = array1.map(x=>Object.assign(x, array2.find(y=>y.id==x.id)))

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

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

无论您可以合并它的顺序如何,

 function merge(array,key){ let map = {}; array.forEach(val=>{ if(map[val[key]]){ map[val[key]] = {...map[val[key]],...val}; }else{ map[val[key]] = val; } }) return Object.keys(map).map(val=>map[val]); } let b = [ { id: "abdc4051", name: "ab" }, { id: "abdc4052", name: "abc" } ]; let a = [ { id: "abdc4051", date: "2017-01-24" }, { id: "abdc4052", date: "2017-01-22" } ]; console.log(merge( [...a,...b], 'id'));

我遍历第一个数组并在第二个数组上使用.find方法来查找id相等的匹配项并返回结果。

 const a = [{ id: "abdc4051", date: "2017-01-24" },{ id: "abdc4052", date: "2017-01-22" }]; const b = [{ id: "abdc4051", name: "ab" },{ id: "abdc4052", name: "abc" }]; console.log(a.map(itm => ({...itm, ...b.find(elm => elm.id == itm.id)})));

如果两个数组都具有不相交项的方法。

 const firstArray = [ { id: 1, name: "Alex", salutation: "Mr." }, { id: 2, name: "Maria", salutation: "Ms." }, ]; const secondArray = [ { id: 2, address: "Larch Retreat 31", postcode: "123452" }, { id: 3, address: "Lycroft Close 12D", postcode: "123009" }, ]; const mergeArr = (arr1, arr2) => { const obj = {}; arr1.forEach(item => { obj[item.id] = item; }); arr2.forEach(item => { obj[item.id] ? (obj[item.id] = { ...obj[item.id], ...item }) : (obj[item.id] = item); }); return Object.values(obj); }; const output = mergeArr(firstArray, secondArray); console.log(output);

好吧......假设两个数组的长度相同,我可能会做这样的事情:

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

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

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

有很多可用的解决方案,但是,我们可以简单地使用for循环和if条件来获取合并数组。

const firstArray = [
  { id: 1, name: "Alex", salutation: "Mr." },
  { id: 2, name: "Maria", salutation: "Ms." },
];

const secondArray = [
  { id: 1, address: "Larch Retreat 31", postcode: "123452" },
  { id: 2, address: "Lycroft Close 12D", postcode: "123009" },
];

let mergedArray: any = [];

for (const arr1 of firstArray) {
  for (arr2 doc of secondArray) {
    if (arr1.id === arr2.id) {
      mergedArray.push({ ...arr1, ...arr2 });
    }
  }
}

console.log(mergedArray)

Python 3 解决方案,适用于登陆此页面并希望找到一个的人

def merge(studentDetails, studentMark, merge_key):
    student_details = {}
    student_marks = {}
    for sd, sm in zip(studentDetails, studentMark):
        key = sd.pop(merge_key)
        student_details[key] = sd

        key = sm.pop(merge_key)
        student_marks[key] = sm

    res = []
    for id, val in student_details.items():
        # Merge three dictionary together
        temp = {**{"studentId": id}, **val, **student_marks[id]}
        res.append(temp)
    return res


if __name__ == '__main__':
    # Test Case 1
    studentDetails = [
        {"studentId": 1, "studentName": 'Sathish', "gender": 'Male', "age": 15},
        {"studentId": 2, "studentName": 'kumar', "gender": 'Male', "age": 16},
        {"studentId": 3, "studentName": 'Roja', "gender": 'Female', "age": 15},
        {"studentId": 4, "studentName": 'Nayanthara', "gender": 'Female', "age": 16},
    ]
    studentMark = [
        {"studentId": 1, "mark1": 80, "mark2": 90, "mark3": 100},
        {"studentId": 2, "mark1": 80, "mark2": 90, "mark3": 100},
        {"studentId": 3, "mark1": 80, "mark2": 90, "mark3": 100},
        {"studentId": 4, "mark1": 80, "mark2": 90, "mark3": 100},
    ]

    # Test Case 2
    array1 = [
        {"id": "abdc4051", "date": "2017-01-24"},
        {"id": "abdc4052", "date": "2017-01-22"}
    ]
    array2 = [
        {"id": "abdc4051", "name": "ab"},
        {"id": "abdc4052", "name": "abc"}
    ]

    output = merge(studentDetails, studentMark, merge_key="studentId")
    [print(a) for a in output]

    output = merge(array1, array2, merge_key="id")
    [print(a) for a in output]

输出

{'studentId': 1, 'studentName': 'Sathish', 'gender': 'Male', 'age': 15, 'mark1': 80, 'mark2': 90, 'mark3': 100}
{'studentId': 2, 'studentName': 'kumar', 'gender': 'Male', 'age': 16, 'mark1': 80, 'mark2': 90, 'mark3': 100}
{'studentId': 3, 'studentName': 'Roja', 'gender': 'Female', 'age': 15, 'mark1': 80, 'mark2': 90, 'mark3': 100}
{'studentId': 4, 'studentName': 'Nayanthara', 'gender': 'Female', 'age': 16, 'mark1': 80, 'mark2': 90, 'mark3': 100}
{'studentId': 'abdc4051', 'date': '2017-01-24', 'name': 'ab'}
{'studentId': 'abdc4052', 'date': '2017-01-22', 'name': 'abc'}

这是将最佳答案(jsbisht)转换为接受密钥为 arguments 的 function。

const mergeArraysByKeyMatch = (array1, array2, key1, key2) => {
  const map = new Map();
  array1.forEach((item) => map.set(item[key1], item));
  array2.forEach((item) =>
    map.set(item[key2], { ...map.get(item[key2]), ...item })
  );
  const merged = Array.from(map.values());

  return merged;
};

一个Typescript O(n+m)(可以归类为O(n))解; 没有lodash:

// RequireAtLeastOne from https://stackoverflow.com/questions/40510611/typescript-interface-require-one-of-two-properties-to-exist/49725198#49725198
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<
  T,
  Exclude<keyof T, Keys>
> &
  {
    [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>;
  }[Keys];

export const mergeDualArraysOnKey = <
  K extends PropertyKey,
  T extends RequireAtLeastOne<{ [f in PropertyKey]?: unknown }, K>
>(
  key: K,
  ...lists: [T[], T[]]
): T[] => {
  const lookup: { [key in string]: number } = {};
  return lists[0].concat(lists[1]).reduce((acc: T[], value: T, i: number) => {
    const lookupKey = `${value[key]}`;
    if (lookup.hasOwnProperty(lookupKey)) {
      acc[lookup[lookupKey]] = Object.assign({}, acc[lookup[lookupKey]], value);
    } else {
      acc.push(value);
      lookup[lookupKey] = acc.length - 1;
    }
    return acc;
  }, []);
};

首先连接两个 arrays 然后遍历新创建的数组。 它使用查找表(对象)将项目的索引存储在具有相同键的最终合并数组中并就地合并对象。

如果这需要扩展以处理更多 arrays,可以使用循环或递归作为包装 function:

const mergeArrays = <
  K extends PropertyKey,
  T extends RequireAtLeastOne<{ [f in PropertyKey]?: unknown }, K>
>(
  key: K,
  ...lists: T[][]
): T[] => {
  if (lists.length === 1) {
    return lists[0];
  }
  const l1 = lists.pop() || [];
  const l2 = lists.pop() || [];
  return mergeArrays(key, mergeDualArraysOnKey(key, l1, l2), ...lists);
};

用法是:

const arr1 = [
  { id: "abdc4052", date: "2017-01-22" },
  { id: "abdc4052", location: "US" },
  { id: "abdc4051", date: "2017-01-24" },
  { id: "abdc4053", date: "2017-01-24" },
  { id: "abdc4054", date: "2017-01-24" },
  { id: "abdc4055", location: "US" },
];

const arr2 = [
  { id: "abdc4052", date: "2017-01-22" },
  { id: "abdc4052", name: "abc" },
  { id: "abdc4055", date: "2017-01-24" },
  { id: "abdc4055", date: "2017-01-24", name: "abcd" },
];

const arr3 = [{ id: "abdc4056", location: "US" }];

const arr4 = [
  { id: "abdc4056", name: "abcde" },
  { id: "abdc4051", name: "ab--ab" },
];

mergeArrays<
    "id",
    {
      id: string;
      date?: string;
      location?: string;
      name?: string;
    }
  >("id", arr1, arr2, arr3, arr4)

根据您的示例,您可以这样做:

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

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

const mergeArrays = () => {
  arrayOne.forEach((item, i) => {
    const matchedFound = arrayTwo.findIndex(a => a.id === item.id);
    arrayOne[i] = {
      ...item,
      ...matchedFound,
    }
  });
};

mergeArrays();

console.log(arrayOne);

这是一个版本,当您有一个对象和一个数组并且您想要合并它们并给数组一个键值以便它很好地适合对象时。

 var fileData = [ { "id" : "1", "filename" : "myfile1", "score" : 33.1 }, { "id" : "2", "filename" : "myfile2", "score" : 31.4 }, { "id" : "3", "filename" : "myfile3", "score" : 36.3 }, { "id" : "4", "filename" : "myfile4", "score" : 23.9 } ]; var fileQuality = [0.23456543,0.13413131,0.1941344,0.7854522]; var newOjbect = fileData.map((item, i) => Object.assign({}, item, {fileQuality:fileQuality[i]})); console.log(newOjbect);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM