2013-02-19 12 views
5

Ho due array, vecchi e nuovi, che contengono oggetti in ogni posizione. Come faccio a sincronizzare o trovare il delta (vale a dire ciò che è nuovo, aggiornato e cancellata dal nuovo array rispetto alla vecchia serie)JavaScript sincronizza due array (di oggetti)/trova delta

var o = [ 
    {id:1, title:"title 1", type:"foo"}, 
    {id:2, title:"title 2", type:"foo"}, 
    {id:3, title:"title 3", type:"foo"} 
]; 

var n = [ 
    {id:1, title:"title 1", type:"foo"}, 
    {id:2, title:"title updated", type:"foo"}, 
    {id:4, title:"title 4", type:"foo"} 
]; 

Con i dati di cui sopra, utilizzando id come chiave, avremmo trovato che l'elemento con ID = 2 ha un titolo aggiornato, l'elemento con ID = 3 viene eliminato e l'elemento con ID = 4 è nuovo.

C'è una libreria esistente là fuori che ha funzioni utili, o è un caso di loop e inner loop, confrontare ogni riga ... ad esempio.

for(var i=0, l=o.length; i<l; i++) 
{ 
    for(var x=0, ln=n.length; x<ln; x++) 
    { 
     //compare when o[i].id == n[x].id  
    } 
} 

Fare questo tipo di confronto tre volte, per trovare nuovi, aggiornati e cancellati?

+1

Si potrebbe accelerare le cose un po ', se gli ID sono unici e si utilizza un oggetto con l'id come chiavi. – Sirko

+0

Dovresti spiegare qual è l'output? Un oggetto con tre proprietà? '{added: 4], modificato: [2], cancellato: [3]}' –

+0

L'output sarebbe probabilmente il migliore in tre matrici. L'eliminato avrebbe bisogno solo di ID, aggiunti e modificati avrebbe bisogno della piena "riga"/oggetto – Fergal

risposta

12

Non c'è magia per fare ciò che ti serve. È necessario iterare attraverso entrambi gli oggetti alla ricerca di modifiche. Un buon suggerimento è trasformare la tua struttura in mappe per ricerche più veloci.

/** 
* Creates a map out of an array be choosing what property to key by 
* @param {object[]} array Array that will be converted into a map 
* @param {string} prop Name of property to key by 
* @return {object} The mapped array. Example: 
*  mapFromArray([{a:1,b:2}, {a:3,b:4}], 'a') 
*  returns {1: {a:1,b:2}, 3: {a:3,b:4}} 
*/ 
function mapFromArray(array, prop) { 
    var map = {}; 
    for (var i=0; i < array.length; i++) { 
     map[ array[i][prop] ] = array[i]; 
    } 
    return map; 
} 

function isEqual(a, b) { 
    return a.title === b.title && a.type === b.type; 
} 

/** 
* @param {object[]} o old array of objects 
* @param {object[]} n new array of objects 
* @param {object} An object with changes 
*/ 
function getDelta(o, n, comparator) { 
    var delta = { 
     added: [], 
     deleted: [], 
     changed: [] 
    }; 
    var mapO = mapFromArray(o, 'id'); 
    var mapN = mapFromArray(n, 'id');  
    for (var id in mapO) { 
     if (!mapN.hasOwnProperty(id)) { 
      delta.deleted.push(mapO[id]); 
     } else if (!comparator(mapN[id], mapO[id])){ 
      delta.changed.push(mapN[id]); 
     } 
    } 

    for (var id in mapN) { 
     if (!mapO.hasOwnProperty(id)) { 
      delta.added.push(mapN[id]) 
     } 
    } 
    return delta; 
} 

// Call it like 
var delta = getDelta(o,n, isEqual); 

Vedi http://jsfiddle.net/wjdZ6/1/ per un esempio

+0

Grazie, mi fa risparmiare troppo tempo;) – Fergal

+1

@Fergal Questa non è una buona cosa, la cosa migliore per te sarebbe provare da solo, e poi fare domande se non riuscissi a farlo funzionare. Se riesci a farlo funzionare ma vuoi dei suggerimenti, puoi chiedere a http://codereview.stackexchange.com/ –

+1

Beh, stavo scherzando. Ho provato a farlo funzionare per lo più (sono richieste poche righe in più) tenendo d'occhio questa pagina. Ma il modo in cui hai convertito gli array in oggetti ha davvero ridotto le righe di codice (usando hasOwnProperty) – Fergal

0

Questa è la versione dattiloscritta di @Juan Mendes risposta

mapFromArray(array: Array<any>, prop: string): { [index: number]: any } { 
    const map = {}; 
    for (let i = 0; i < array.length; i++) { 
     map[array[i][prop]] = array[i]; 
    } 
    return map; 
    } 

    isEqual(a, b): boolean { 
    return a.title === b.title && a.type === b.type; 
    } 

    getDelta(o: Array<any>, n: Array<any>, comparator: (a, b) => boolean): { added: Array<any>, deleted: Array<any>, changed: Array<any> } { 
    const delta = { 
     added: [], 
     deleted: [], 
     changed: [] 
    }; 
    const mapO = this.mapFromArray(o, 'id'); 
    const mapN = this.mapFromArray(n, 'id'); 
    for (const id in mapO) { 
     if (!mapN.hasOwnProperty(id)) { 
     delta.deleted.push(mapO[id]); 
     } else if (!comparator(mapN[id], mapO[id])) { 
     delta.changed.push(mapN[id]); 
     } 
    } 

    for (const id in mapN) { 
     if (!mapO.hasOwnProperty(id)) { 
     delta.added.push(mapN[id]); 
     } 
    } 
    return delta; 
    }