根据键合并两个对象数组

时间:2017-10-20 12:39:20

标签: javascript

我有两个数组:

数组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迭代的情况下执行此操作?

17 个答案:

答案 0 :(得分:15)

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

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

这应该导致:

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

答案 1 :(得分:13)

您可以一行完成

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

答案 2 :(得分:9)

你可以这样做 -

arr1

如果arr2let 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);的顺序不同,请使用以下代码:

arr1

如果arr2let 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);的订单相同,请使用此

{{1}}

答案 3 :(得分:8)

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

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 :(得分:2)

您可以使用数组方法

&#13;
&#13;
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"}
&#13;
&#13;
&#13;

答案 5 :(得分:2)

我们可以在这里使用lodash。 _.merge可以按预期工作。它可以与存在的通用密钥一起使用。

_.merge(array1, array2)

答案 6 :(得分:2)

这是使用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
            },
            {}
        )
    )

每个列表都简化为单个对象,其中键是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对象与切片一起从数组中删除,在每个集合中未找到的所有元素都将删除。这使它成为内部联接,您可以删除此过滤器并具有完整的外部联接。

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

答案 7 :(得分:1)

我遍历第一个数组并在第二个数组上使用 function updateInputs(data) { var from = data.from; var to = data.to; var rangeFrom, rangeTo; switch (data.input.getAttribute("data-range")) { case "rangeSalary": rangeFrom = $("rangeFromSalary"); rangeTo = $("rangeToSalary"); break; case "rangeAge": rangeFrom = $("rangeFromAge"); rangeTo = $("rangeToAge"); break; // ... } if (rangeFrom && rangeTo) { rangeFrom.prop("value", from); rangeTo.prop("value", to); } table.draw(); } 方法来查找 .find 相等的匹配项并返回结果。

id

答案 8 :(得分:1)

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

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

答案 9 :(得分:1)

您可以按如下方式递归合并它们:

&#13;
&#13;
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))
&#13;
&#13;
&#13;

答案 10 :(得分:1)

无论您合并的顺序如何,

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

答案 11 :(得分:0)

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

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

答案 12 :(得分:0)

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

答案 13 :(得分:0)

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

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

答案 14 :(得分:0)

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

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

答案 15 :(得分:0)

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

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

答案 16 :(得分:-1)

使用开源项目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>