2014-07-20 29 views
5

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

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

我想合并(数组1,数组2)给我:

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

是否有一个简单的方法来做到这一点?

编辑:几个人已经回答,没有看太紧密我的问题,请注意,我想匹配每个数组中的类似对象,并将他们的属性结合到我的最终数组中。密钥是唯一的,每个数组中只有一个对象具有特定的密钥。

+0

你是用'key'唯一标识对象吗? –

+0

是的,密钥是唯一的 – user3409889

回答

3

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

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

但是,他的解决方案没有解决对象内的对象。为了解决这个问题,我使用递归阅读:恶魔,就像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 
*/ 

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

fiddle

+0

最后一个正确的答案.. :-) – techfoobar

+0

@techfoobar我应该注释的代码。你认为使用'Object.prototype.mergeByKey'会更好吗?我倾向于避免扩展原型,但是idk ... – royhowie

+0

如果它不会被广泛使用(并且例如由您发布的API的消费者),那么保持股票对象原样是我的建议。只是mho,顺便说一句。 – techfoobar

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

这将是最好使用现有的基础设施,如下划线的_.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对象数组,这将它们全部合并成一个单独的对象。

相关问题