2015-06-14 11 views
9

Sto cercando di capire una soluzione per la simmetrica differenza utilizzando JavaScript che compie i seguenti obiettivi:Cercando di risolvere differenza simmetrica utilizzando Javascript

  • accetta un numero imprecisato di array come argomenti
  • conserva la L'ordine originale dei numeri negli array
  • non rimuove i duplicati dei numeri nei singoli array
  • rimuove i duplicati che si verificano tra gli array

Così, ad esempio, se l'ingresso è ([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]), la soluzione sarebbe, [1, 1, 6, 5, 4].

Sto cercando di risolvere questo come una sfida data da una comunità di codifica online . Le istruzioni esatta dello Stato sfida ,

creare una funzione che prende due o più array e restituisce un array della differenza simmetrica degli array forniti.

Il termine matematico differenza simmetrica si riferisce agli elementi in due set che si trovano nel primo o nel secondo set, ma non in entrambi.

Anche se la mia soluzione qui di seguito trova i numeri che sono unico per ogni array, elimina tutti i numeri che si verificano più di una volta e non impedisce l'ordine dei numeri.

La mia domanda è molto simile a quella richiesta a finding symmetric difference/unique elements in multiple arrays in javascript. Tuttavia, la soluzione non conserva l'ordine originale dei numeri e non conserva i duplicati di numeri univoci che si verificano in matrici singole.

function sym(args){ 
    var arr = []; 
    var result = []; 
    var units; 
    var index = {}; 
    for(var i in arguments){ 
     units = arguments[i]; 

    for(var j = 0; j < units.length; j++){ 
     arr.push(units[j]); 
     } 
    } 

    arr.forEach(function(a){ 
     if(!index[a]){ 
      index[a] = 0; 
     } 
      index[a]++; 

    }); 

     for(var l in index){ 
      if(index[l] === 1){ 
       result.push(+l); 
      } 
     } 

    return result; 
} 
symsym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]); // => Desired answer: [1, 1, 6. 5. 4] 

risposta

10

Ecco una versione che utilizza l'oggetto Set per effettuare una ricerca più rapida. Ecco la logica di base:

  1. Ogni array viene passato come argomento in un oggetto Set separato (per facilitare la ricerca rapida).
  2. Quindi, itera ogni passaggio nell'array e lo confronta con gli altri oggetti Set (quelli non creati dall'array che viene ripetuto).
  3. Se l'elemento non viene trovato in nessuno degli altri Set, viene aggiunto al risultato.

Così, si comincia con il primo array [1, 1, 2, 6]. Poiché 1 non è stato trovato in nessuno degli altri array, ciascuno dei primi due valori 1 viene aggiunto al risultato. Quindi 2 si trova nel secondo set in modo che non venga aggiunto al risultato. Quindi 6 non si trova in nessuno degli altri due set, quindi viene aggiunto al risultato. Lo stesso processo si ripete per il secondo array [2, 3, 5] dove 2 e 3 si trovano in altri Set, ma 5 non è così 5 viene aggiunto al risultato.E, per l'ultimo array, solo 4 non è stato trovato negli altri Set. Quindi, il risultato finale è [1,1,6,5,4].

Gli oggetti Set vengono utilizzati per comodità e prestazioni. Si potrebbe usare .indexOf() per cercarli in ogni array o uno potrebbe fare la propria ricerca Set-like con un oggetto semplice se non si vuole fare affidamento sull'oggetto Set. C'è anche un polyfill parziale per l'oggetto Set che funzionerebbe qui in this answer.

function symDiff() { 
 
    var sets = [], result = []; 
 
    // make copy of arguments into an array 
 
    var args = Array.prototype.slice.call(arguments, 0); 
 
    // put each array into a set for easy lookup 
 
    args.forEach(function(arr) { 
 
     sets.push(new Set(arr)); 
 
    }); 
 
    // now see which elements in each array are unique 
 
    // e.g. not contained in the other sets 
 
    args.forEach(function(array, arrayIndex) { 
 
     // iterate each item in the array 
 
     array.forEach(function(item) { 
 
      var found = false; 
 
      // iterate each set (use a plain for loop so it's easier to break) 
 
      for (var setIndex = 0; setIndex < sets.length; setIndex++) { 
 
       // skip the set from our own array 
 
       if (setIndex !== arrayIndex) { 
 
        if (sets[setIndex].has(item)) { 
 
         // if the set has this item 
 
         found = true; 
 
         break; 
 
        } 
 
       } 
 
      } 
 
      if (!found) { 
 
       result.push(item); 
 
      } 
 
     }); 
 
    }); 
 
    return result; 
 
} 
 

 
var r = symDiff([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]); 
 
log(r); 
 

 
function log(x) { 
 
    var d = document.createElement("div"); 
 
    d.textContent = JSON.stringify(x); 
 
    document.body.appendChild(d); 
 
}

Una parte fondamentale di questo codice è come si confronta un determinato elemento per i set delle altre matrici. Semplicemente scorre l'elenco degli oggetti Set, ma salta l'oggetto Set che ha lo stesso indice nell'array della matrice che viene iterata. Questo salta il Set fatto da questo array in modo che sia solo alla ricerca di elementi che esistono in altri array. Ciò gli consente di conservare i duplicati che si verificano in un solo array.


Ecco una versione che utilizza l'oggetto Set se è presente, ma inserisce una sostituzione Teeny se non (così questo funziona nei browser più vecchi):

function symDiff() { 
 
    var sets = [], result = [], LocalSet; 
 
    if (typeof Set === "function") { 
 
     try { 
 
      // test to see if constructor supports iterable arg 
 
      var temp = new Set([1,2,3]); 
 
      if (temp.size === 3) { 
 
       LocalSet = Set; 
 
      } 
 
     } catch(e) {} 
 
    } 
 
    if (!LocalSet) { 
 
     // use teeny polyfill for Set 
 
     LocalSet = function(arr) { 
 
      this.has = function(item) { 
 
       return arr.indexOf(item) !== -1; 
 
      } 
 
     } 
 
    } 
 
    // make copy of arguments into an array 
 
    var args = Array.prototype.slice.call(arguments, 0); 
 
    // put each array into a set for easy lookup 
 
    args.forEach(function(arr) { 
 
     sets.push(new LocalSet(arr)); 
 
    }); 
 
    // now see which elements in each array are unique 
 
    // e.g. not contained in the other sets 
 
    args.forEach(function(array, arrayIndex) { 
 
     // iterate each item in the array 
 
     array.forEach(function(item) { 
 
      var found = false; 
 
      // iterate each set (use a plain for loop so it's easier to break) 
 
      for (var setIndex = 0; setIndex < sets.length; setIndex++) { 
 
       // skip the set from our own array 
 
       if (setIndex !== arrayIndex) { 
 
        if (sets[setIndex].has(item)) { 
 
         // if the set has this item 
 
         found = true; 
 
         break; 
 
        } 
 
       } 
 
      } 
 
      if (!found) { 
 
       result.push(item); 
 
      } 
 
     }); 
 
    }); 
 
    return result; 
 
} 
 

 

 
var r = symDiff([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]); 
 
log(r); 
 

 
function log(x) { 
 
    var d = document.createElement("div"); 
 
    d.textContent = JSON.stringify(x); 
 
    document.body.appendChild(d); 
 
}

+0

Grazie! Questo mi ha lasciato perplesso. Non potrei, per la vita di me, capire come fare l'iterazione e il confronto di elementi che la tua soluzione mostra in modo così chiaro. Grazie molto! – davisec52

+0

@ davisec52 - Ho appena notato che anche l'altra soluzione alla quale ti sei collegato era mia. Immagino che mi piacciano questi tipi di problemi. – jfriend00

+1

Ho aggiunto una versione del codice che funziona in IE (torna a IE9). Nella saggezza infinita di IE (anche IE11), anche se supporta l'oggetto 'Set', non supporta il passaggio di una matrice al costruttore. Quindi, la seconda versione del codice sopra test per la mancanza dell'oggetto 'Set' o IE mal funzionante e utilizza una sostituzione teeny polyfill che fa proprio ciò di cui ha bisogno questa funzione. – jfriend00

14

Come con tutti i problemi, è meglio iniziare a scrivere un algoritmo:

versioni Concatena delle schiere, dove ogni array viene filtrata per contenere quegli elementi che non matrice diverso da quello attuale contiene

Poi basta scriverlo in JS:

function sym() { 
    var arrays = [].slice.apply(arguments); 

    return [].concat.apply([],    // concatenate 
    arrays.map(       // versions of the arrays 
     function(array, i) {     // where each array 
     return array.filter(    // is filtered to contain 
      function(elt) {     // those elements which 
      return !arrays.some(   // no array 
       function(a, j) {    // 
       return i !== j    // other than the current one 
        && a.indexOf(elt) >= 0 // contains 
       ; 
       } 
      ); 
      } 
     ); 
     } 
    ) 
); 
} 

non commentato versione , scritto più brevemente con ES6:

function sym(...arrays) { 
    return [].concat(arrays . 
    map((array, i) => array . 
     filter(elt => !arrays . 
     some((a, j) => i !== j && a.indexOf(elt) >= 0)))); 
} 
+0

Grazie. La tua soluzione è anche molto utile mostrandomi come usare i metodi Javascript per lavorare con gli array. – davisec52

1

Questo è il codice JS utilizzando le funzioni di ordine superiore

function sym(args) { 
 
     var output; 
 
     output = [].slice.apply(arguments).reduce(function(previous, current) { 
 
     current.filter(function(value, index, self) { //for unique 
 
      return self.indexOf(value) === index; 
 
     }).map(function(element) { //pushing array 
 
      var loc = previous.indexOf(element); 
 
      a = [loc !== -1 ? previous.splice(loc, 1) : previous.push(element)]; 
 
     }); 
 
     return previous; 
 
     }, []); 
 
     document.write(output); 
 
     return output; 
 
    } 
 

 
    sym([1, 2, 3], [5, 2, 1, 4]);

E sarebbe tornare l'output come: [3,5,4]

+0

Potresti stampare il risultato quando esegui lo snippet di codice, per favore? –

1

Soluzione javascript pura.

function diff(arr1, arr2) { 
var arr3= []; 
    for(var i = 0; i < arr1.length; i++){ 
    var unique = true; 
    for(var j=0; j < arr2.length; j++){ 
      if(arr1[i] == arr2[j]){ 
       unique = false; 
       break; 
      } 
    } 
    if(unique){ 
    arr3.push(arr1[i]);} 
    } 
return arr3; 
} 

function symDiff(arr1, arr2){ 
    return diff(arr1,arr2).concat(diff(arr2,arr1)); 
} 

symDiff([1, "calf", 3, "piglet"], [7, "filly"]) 
//[1, "calf", 3, "piglet", 7, "filly"] 
1

La mia soluzione corta. Alla fine, ho rimosso i duplicati per filtro().

function sym() { 
    var args = Array.prototype.slice.call(arguments); 
    var almost = args.reduce(function(a,b){ 
    return b.filter(function(i) {return a.indexOf(i) < 0;}) 
    .concat(a.filter(function(i){return b.indexOf(i)<0;})); 
    }); 
    return almost.filter(function(el, pos){return almost.indexOf(el) == pos;}); 
} 

sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]); 

//Result: [4,5,1] 
5

Mi sono imbattuto in questa domanda nella mia ricerca della stessa sfida di codifica su FCC. Sono stato in grado di risolverlo utilizzando i loop for e while, ma ho avuto alcuni problemi risolvendo usando lo Array.reduce() raccomandato.Dopo aver imparato una tonnellata su .reduce e altri metodi di array, ho pensato di condividere anche le mie soluzioni.

Questo è il primo modo in cui l'ho risolto, senza utilizzare .reduce.

function sym() { 
    var arrays = [].slice.call(arguments); 

    function diff(arr1, arr2) { 
    var arr = []; 

    arr1.forEach(function(v) { 
     if (!~arr2.indexOf(v) && !~arr.indexOf(v)) { 
     arr.push(v); 
     } 
    }); 

    arr2.forEach(function(v) { 
     if (!~arr1.indexOf(v) && !~arr.indexOf(v)) { 
     arr.push(v); 
     } 
    }); 
    return arr; 
    } 

    var result = diff(arrays.shift(), arrays.shift()); 

    while (arrays.length > 0) { 
    result = diff(result, arrays.shift()); 
    } 

    return result; 
} 

Dopo aver appreso e provando varie combinazioni di metodo, sono arrivato fino a questo che penso sia abbastanza concisa e leggibile.

function sym() { 
    var arrays = [].slice.call(arguments); 

    function diff(arr1, arr2) { 
    return arr1.filter(function (v) { 
     return !~arr2.indexOf(v); 
    }); 
    } 

    return arrays.reduce(function (accArr, curArr) { 
    return [].concat(diff(accArr, curArr), diff(curArr, accArr)) 
    .filter(function (v, i, self) { return self.indexOf(v) === i; }); 
    }); 

} 

Quell'ultima .filter linea ho pensato è stato abbastanza freddo per dedup un array. L'ho trovato here, ma l'ho modificato per utilizzare il terzo parametro di callback anziché l'array denominato a causa del concatenamento del metodo.

Questa sfida è stata molto divertente!

0

questo funziona per me:

function sym() { 
 
    var args = [].slice.call(arguments); 
 
    
 
    var getSym = function(arr1, arr2) { 
 
    return arr1.filter(function(each, idx) { 
 
     return arr2.indexOf(each) === -1 && arr1.indexOf(each, idx + 1) === -1; 
 
    }).concat(arr2.filter(function(each, idx) { 
 
     return arr1.indexOf(each) === -1 && arr2.indexOf(each, idx + 1) === -1; 
 
    })); 
 
    }; 
 
    
 
    var result = getSym(args[0], args[1]); 
 
    var len = args.length - 1, i = 2; 
 
    while (--len) { 
 
    result = [].concat(getSym(result, args[i])); 
 
    i++; 
 
    } 
 
    
 
    return result; 
 
} 
 

 
console.info(sym([1, 1, 2, 5], [2, 2, 3, 5], [6, 8], [7, 8], [9]));

0

alternativa: utilizzare la ricerca all'interno di una mappa, invece di una matrice

function sym(...vs){ 
    var has = {}; 
    //flatten values 
    vs.reduce((a,b)=>a.concat(b)). 
     //if element does not exist add it (value==1) 
     //or mark it as multiply found value > 1 
     forEach(value=>{has[value] = (has[value]||0)+1}); 
    return Object.keys(has).filter(x=>has[x]==1).map(x=>parseInt(x,10)); 
} 
console.log(sym([1, 2, 3], [5, 2, 1, 4],[5,7], [5]));//[3,4,7]) 
0

function sym(args) { 
 
    var initialArray = Array.prototype.slice.call(arguments); 
 
    var combinedTotalArray = initialArray.reduce(symDiff); 
 

 
    
 
    // Iterate each element in array, find values not present in other array and push values in combinedDualArray if value is not there already 
 
    // Repeat for the other array (change roles) 
 
    function symDiff(arrayOne, arrayTwo){ 
 
    var combinedDualArray = []; 
 
    arrayOne.forEach(function(el, i){ 
 
     if(!arrayTwo.includes(el) && !combinedDualArray.includes(el)){ 
 
     combinedDualArray.push(el); 
 
     } 
 
    }); 
 
     
 
    arrayTwo.forEach(function(el, i){ 
 
     if(!arrayOne.includes(el) && !combinedDualArray.includes(el)){ 
 
     combinedDualArray.push(el); 
 
     } 
 
    }); 
 
    combinedDualArray.sort(); 
 
    return combinedDualArray; 
 
    } 
 
    
 
    return combinedTotalArray; 
 
} 
 

 
console.log(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]));

Problemi correlati