How do I merge an array of objects in Javascript?

前端 未结 3 1690
我寻月下人不归
我寻月下人不归 2020-12-20 23:19

Example:

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


        
相关标签:
3条回答
  • 2020-12-20 23:27

    You could do something like this:

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

    You could eliminate the sort if you don't care about the order. Another option is to use an array instead of the map I have used (keyedResult) if you have numeric keys and don't care about the array being sparse (i.e., if the keys are non-consecutive numbers). Here the key would also be the index of the array.

    This solution also runs in O(n).

    fiddle

    0 讨论(0)
  • 2020-12-20 23:33

    I wrote a quick not-so-quick solution. The one problem you might want to consider is whether a property from an object in the second array should override the same property, if present, in the second object it's being compared to.

    Solution 1

    This solution is of complexity O(n²). Solution 2 is much faster; this solution is only for those who don't want to be Sanic the Hedgehog fast.

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

    Test Case

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

    Results

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

    fiddle

    Solution 2

    As Vivin Paliath pointed out in the comments below, my first solution was of O(n²) complexity (read: bad). His answer is very good and provides a solution with a complexity of O(m + n), where m is the size of the first array and n of the second array. In other words, of complexity O(2n).

    However, his solution does not address objects within objects. To solve this, I used recursion—read: the devil, just like 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;
    }
    

    Test Case

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

    Results

    /*
    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
    */
    

    This correctly merges the objects, along with all child objects. This solutions assumes that objects with matching keys have the same hierarchies. It also does not handle the merging of two arrays.

    fiddle

    0 讨论(0)
  • 2020-12-20 23:51

    It would be preferable to use existing infrastructure such as Underscore's _.groupBy and _.extend to handle cases like this, rather than re-inventing the wheel.

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

    The idea here is using _.extend.apply to pass the array of objects grouped under a particular key as arguments to _.extend, which will merge them all into a single object.

    0 讨论(0)
提交回复
热议问题