如何在Javascript中合并对象数组?

时间:2014-07-20 03:52:53

标签: javascript

示例:

var array1 = [ {'key':1, 'property1': 'x'}, {'key':2, 'property1': 'y'} ]
var array2 = [ {'key':2, 'property2': 'a'}, {'key':1, 'property2': 'b'} ]

我想合并(array1,array2)给我:

[
    {'key':1, 'property1': 'x', 'property2' : 'b'},
    {'key':2, 'property1': 'y', 'property2' : 'a'}
]

有一种简单的方法吗?

编辑:有几个人在没有仔细查看我的问题的情况下回答,请注意我想匹配每个数组中的类似对象并将它们的属性组合到我的最终数组中。键是唯一的,每个阵列中只有一个对象具有特定的键。

3 个答案:

答案 0 :(得分:3)

我写了一个 quick 不那么快的解决方案。您可能要考虑的一个问题是,第二个数组中的对象的属性是否应覆盖与其进行比较的第二个对象中的相同属性(如果存在)。

解决方案1 ​​

此解决方案具有复杂性O(n²)。解决方案2更快;此解决方案仅适用于那些不想快速Sanic the Hedgehog的人。

<强>的JavaScript

var mergeByKey = function (arr1, arr2, key) {
// key is the key that the function merges based on
    arr1.forEach(function (d, i) {
        var prop = d[key];
        // since keys are unique, compare based on this key's value
        arr2.forEach(function (f) {
            if (prop == f[key]) {    // if true, the objects share keys
                for (var x in f) {   // loop through each key in the 2nd object
                    if (!(x in d))   // if the key is not in the 1st object
                        arr1[i][x] = f[x];    // add it to the first object
                // this is the part you might want to change for matching properties
                // which object overrides the other?
                }
            }
        })
    })
    return arr1;
}

测试用例

var arr = [ {'key':1, 'property1': 'x'},
            {'key':2, 'property1': 'y'} ],
    arr2= [ {'key':2, 'property2': 'a'},
            {'key':1, 'property2': 'b'} ];

console.log(mergeByKey(arr, arr2, "key"));

<强>结果

/* returns:
    Object
    key: 1
    property1: "x"
    property2: "b"
    __proto__: Object
and 
    Object
    key: 2
    property1: "y"
    property2: "a"
    __proto__: Object
*/

fiddle

解决方案2

正如Vivin Paliath在下面的评论中指出的那样,我的第一个解决方案是O(n²)复杂度(读:坏)。他的答案非常好,并提供了复杂度为O(m + n)的解决方案,其中m是第一个数组的大小,第二个数组是n。换句话说,具有复杂性O(2n)

但是,他的解决方案并未解决对象中的对象。为了解决这个问题,我使用了recursion-read:devil,就像O(n²)

<强>的JavaScript

var mergeByKey = function (arr1, arr2, key) {
    var holder = [],
        storedKeys = {},
        i = 0; j = 0; l1 = arr1.length, l2 = arr2.length;

    var merge = function (obj, ref) {
        for (var x in obj) {
            if (!(x in ref || x instanceof Object)) {
                ref[x] = obj[x];
            } else {
                merge(obj[x], ref[x]);
            }
        }
        storedKeys[obj.key] = ref;
    }
    for (; i < l1; i++) {
        merge(arr1[i], storedKeys[arr1[i].key] || {});
    }
    for (; j < l2; j++) {
        merge(arr2[j], storedKeys[arr2[j].key] || {});
    }

    delete storedKeys[undefined];

    for (var obj in storedKeys)
        holder.push(storedKeys[obj]);

    return holder;
}

测试用例

var arr1 = [
    {
        "key" : 1,
        "prop1" : "x",
        "test" : {
            "one": 1,
            "test2": {
                "maybe" : false,
                "test3": { "nothing" : true }
            }
        }
    },
    {
        "key" : 2,
        "prop1": "y",
        "test" : { "one": 1 }
    }],
    arr2 = [
        {
            "key" : 1,
            "prop2" : "y",
            "test" : { "two" : 2 }
        },
        {
            "key" : 2,
            "prop2" : "z",
            "test" : { "two": 2 }
        }];
console.log(mergeByKey(arr1, arr2, "key"));

<强>结果

/*
Object
    key: 1
    prop1: "x"
    prop2: "y"
    test: Object
        one: 1
        test2: Object
            maybe: false
            test3: Object
                nothing: true
                __proto__: Object
            __proto__: Object
        two: 2
        __proto__: Object
    __proto__: Object
Object
    key: 2
    prop1: "y"
    prop2: "z"
    test: Object
        one: 1
        two: 2
        __proto__: Object
    __proto__: Object
*/

这正确地合并了对象以及所有子对象。此解决方案假定具有匹配keys的对象具有相同的层次结构。它也不处理两个数组的合并。

fiddle

答案 1 :(得分:2)

你可以这样做:

function merge(array1, array2) {
    var keyedResult = {};

    function _merge(element) {
        if(!keyedResult[element.key]) {
            keyedResult[element.key] = {};
        }

        var entry = keyedResult[element.key];
        for(var property in element) if(element.hasOwnProperty(property)) {
            if(property !== "key") {
                entry[property] = element[property];
            }                
        }

        entry["key"] = element.key;
    }

    array1.forEach(_merge);
    array2.forEach(_merge);

    var result = [];

    for(var key in keyedResult) if(keyedResult.hasOwnProperty(key)) {
        result.push(keyedResult[key]);
    }

    return result.sort(function(a, b) {
        return a.key - b.key;
    });
}

如果您不关心订单,可以取消排序。另一个选择是使用数组而不是我使用的地图(keyedResult)如果你有数字键并且不关心数组稀疏(即,如果键是非连续数字) 。这里的键也是数组的索引。

此解决方案也在O(n)中运行。

<强> fiddle

答案 2 :(得分:2)

最好使用现有的基础设施,例如Underscore的_.groupBy_.extend来处理这类案件,而不是重新发明轮子。

function merge(array1, array2) {

  // merge the arrays
  // [ {'key':1, 'property1': 'x'}, {'key':2, 'property1': 'y'}, {'key':2, 'property2': 'a'}, {'key':1, 'property2': 'b'} ]
  var merged_array = array1.concat(array2);

  // Use _.groupBy to create an object indexed by key of relevant array entries
  // {1: [{ }, { }], 2: [{ }, { }]}
  var keyed_objects = _.groupBy(merged_array, 'key');

  // for each entry in keyed_objects, merge objects
  return Object.keys(keyed_objects).map(function(key) {
    return _.extend.apply({}, keyed_objects[key]);
  });

}

这里的想法是使用_.extend.apply将在特定键下分组的对象数组作为参数传递给_.extend,这会将它们全部合并到一个对象中。