2014-07-20 10 views
5

Esempio:Come unire una matrice di oggetti in Javascript?

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

voglio unire (array1, array2) per darmi:

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

C'è un modo semplice per fare questo?

EDIT: molte persone hanno risposto senza guardare troppo da vicino il mio problema, si prega di notare che voglio abbinare oggetti simili in ogni matrice e combinare le loro proprietà nel mio array finale. Le chiavi sono uniche e ci sarà sempre al massimo un solo oggetto con una particolare chiave in ogni array.

+0

Stai identificando in modo univoco gli oggetti con 'chiave'? –

+0

sì, le chiavi sono univoche – user3409889

risposta

3

ho scritto un rapido soluzione non-così-rapido . L'unico problema che si potrebbe prendere in considerazione è se una proprietà da un oggetto nel secondo array dovrebbe ignorare la stessa proprietà, se presente, per il secondo oggetto è essere paragonato a.

Soluzione 1

Questa soluzione è di complessità O(n²). La soluzione 2 è molto più veloce; questa soluzione è solo per coloro che non vogliono essere Sanic the Hedgehog veloce.

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

banco di prova

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

Risultati

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

fiddle

Soluzione 2

Come Vivin Paliath sottolineato nei commenti qui sotto, la mia prima soluzione era di O(n²) complessità (leggi: cattivo). La sua risposta è molto buono e fornisce una soluzione con una complessità di O(m + n), dove m è la dimensione della prima matrice e n della seconda matrice. In altre parole, di complessità O(2n).

Tuttavia, la sua soluzione non affronta gli oggetti all'interno di oggetti. Per risolvere questo problema, ho usato la ricorsione lettura: il diavolo, proprio come 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; 
} 

banco di prova

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

Risultati

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

Questa correttamente m adatta gli oggetti, insieme a tutti gli oggetti figli. Questa soluzione presuppone che gli oggetti con corrispondenza di keys abbiano le stesse gerarchie.Inoltre, non gestisce la fusione di due array.

fiddle

+0

Finalmente una risposta corretta .. :-) – techfoobar

+0

@techfoobar Probabilmente dovrei annotare il codice. Pensi che sarebbe meglio usare 'Object.prototype.mergeByKey'? Tendo ad evitare di estendere il prototipo, ma idk ... – royhowie

+0

Se non verrà utilizzato ampiamente (e dai consumatori di un'API che si sta pubblicando ad esempio), quindi mantenere gli oggetti di magazzino così come sono è quello che consiglierei . Solo mho, btw. – techfoobar

2

Si potrebbe fare qualcosa di simile:

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

Si potrebbe eliminare il genere, se non vi interessa circa l'ordine. Un'altra opzione consiste nell'utilizzare una matrice anziché la mappa che ho usato (keyedResult) se si dispone di tasti numerici e non si preoccupa della matrice sparsa (ad esempio, se le chiavi sono numeri non consecutivi). Qui la chiave sarebbe anche l'indice dell'array.

Questa soluzione viene eseguita anche in O(n).

fiddle

2

Sarebbe preferibile utilizzare infrastruttura esistente, come di _.groupBy sottolineatura e _.extend per gestire casi come questo, piuttosto che ri-inventare la ruota.

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

} 

L'idea qui utilizza _.extend.apply passare la matrice di oggetti raggruppati in una determinata tonalità argomenti _.extend, che li si fondono in un unico oggetto.

Problemi correlati