web-dev-qa-db-fra.com

Essayer de résoudre les différences symétriques avec Javascript

J'essaie de trouver une solution de différence symétrique à l'aide de javascript qui remplisse les objectifs suivants:

  • accepte un nombre non spécifié de tableaux comme arguments
  • conserve l'ordre d'origine des nombres dans les tableaux
  • ne supprime pas les doublons de nombres dans des tableaux simples
  • supprime les doublons apparaissant sur les tableaux

Ainsi, par exemple, si l’entrée est ([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]), la solution serait, [1, 1, 6, 5 , 4].

J'essaie de résoudre ce problème comme un défi lancé par une communauté de codage en ligne. Les instructions exactes de l'état de défi,

Créez une fonction qui prend deux ou plusieurs tableaux et renvoie un tableau de la différence symétrique des tableaux fournis.

Le terme mathématique différence symétrique fait référence aux éléments de deux ensembles figurant dans le premier ou le deuxième ensemble, mais pas dans les deux.

Bien que ma solution ci-dessous trouve les nombres propres à chaque tableau, elle élimine tous les nombres apparaissant plus d'une fois et ne conserve pas l'ordre des nombres.

Ma question est très proche de celle posée à trouver une différence symétrique/des éléments uniques dans plusieurs tableaux en javascript . Cependant, la solution ne conserve pas l'ordre d'origine des nombres ni les doublons de numéros uniques apparaissant dans des tableaux uniques.

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]
12
davisec52

Voici une version qui utilise l'objet Set pour accélérer la recherche. Voici la logique de base:

  1. Il place chaque tableau passé en tant qu'argument dans un objet Set séparé (pour faciliter la recherche rapide).
  2. Ensuite, il itère chaque tableau passé et le compare aux autres objets Set (ceux qui ne sont pas créés à partir du tableau itéré).
  3. Si l'élément ne figure dans aucun des autres ensembles, il est ajouté au résultat.

Donc, ça commence par le premier tableau [1, 1, 2, 6]. Étant donné que 1 ne figure dans aucune des autres matrices, chacune des deux premières valeurs 1 est ajoutée au résultat. Ensuite, 2 est trouvé dans le deuxième ensemble afin qu'il ne soit pas ajouté au résultat. Alors, 6 ne figure dans aucune des deux autres séries et est donc ajouté au résultat. Le même processus se répète pour le deuxième tableau [2, 3, 5]2 et 3 sont trouvés dans d'autres ensembles, mais 5 n'est pas le même, 5 est ajouté au résultat. Et, pour le dernier tableau, seul 4 n'est pas trouvé dans les autres ensembles. Donc, le résultat final est [1,1,6,5,4].

Les objets Set sont utilisés pour des raisons de commodité et de performances. Vous pouvez utiliser .indexOf() pour les rechercher dans chaque tableau ou créer votre propre recherche de type Set avec un objet brut si vous ne voulez pas vous fier à l'objet Set. Il y a aussi un polyfill partiel pour l'objet Set qui fonctionnerait ici dans cette réponse .

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

Un élément clé de ce code est la façon dont il compare un élément donné aux ensembles des autres tableaux. Il parcourt simplement la liste des objets Set, mais ignore l'objet Set qui possède le même index dans le tableau que le tableau itéré. Cela ignore l'ensemble créé à partir de ce tableau afin qu'il recherche uniquement les éléments existant dans d'autres tableaux. Cela lui permet de conserver les doublons qui se produisent dans un seul tableau.


Voici une version qui utilise l'objet Set s'il est présent, mais insère un remplacement minime si ce n'est pas le cas (cela fonctionnera donc dans les navigateurs plus anciens):

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

Comme pour tous les problèmes, il est préférable de commencer à écrire un algorithme:

Concaténez les versions des tableaux, où chaque tableau est filtré pour contenir les éléments qu'aucun autre tableau que celui en cours ne contient

Ensuite, écrivez cela dans 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
                ;
              }
            );
          }
        );
      }
    )
  );
}

Version non commentée, écrite plus succinctement avec ES6:

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

Je suis tombé sur cette question dans mes recherches sur le même défi de codage pour FAC. J'ai pu le résoudre en utilisant les boucles for et while, mais j'ai eu quelques difficultés à résoudre en utilisant la fonction Array.reduce() recommandée. Après avoir beaucoup appris sur .reduce et d'autres méthodes sur les tableaux, j'ai décidé de partager mes solutions.

C'est la première fois que je l'ai résolu, sans utiliser .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;
}

Après avoir appris et essayé diverses combinaisons de méthodes, j'ai trouvé ce que je pense assez succinct et lisible.

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

}

Cette dernière ligne .filter qui, à mon avis, était assez cool pour dédoubler un tableau. Je l'ai trouvé ici , mais je l'ai modifié pour utiliser le troisième paramètre de rappel à la place du tableau nommé en raison de l'enchaînement des méthodes.

Ce défi était très amusant!

7
jpl1079

Utilisez simplement _.xor ou copiez le code lodash.

2
kornieff

Une autre solution simple mais lisible:

 
/*
This filters arr1 and arr2 from elements which are in both arrays
and returns concatenated results from filtering.
*/
function symDiffArray(arr1, arr2) {
  return arr1.filter(elem => !arr2.includes(elem))
             .concat(arr2.filter(elem => !arr1.includes(elem)));
}

/*
Add and use this if you want to filter more than two arrays at a time.
*/
function symDiffArrays(...arrays) {
  return arrays.reduce(symDiffArray, []);
}

console.log(symDiffArray([1, 3], ['Saluton', 3])); // [1, 'Saluton']
console.log(symDiffArrays([1, 3], [2, 3], [2, 8, 5])); // [1, 8, 5]

Fonctions utilisées: Array.prototype.filter () | Array.prototype.reduce () | Array.prototype.includes ()

1
pmal

Ceci est le code JS utilisant des fonctions d'ordre supérieur

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

Et cela rendrait la sortie sous la forme: [3,5,4]

1
Angshuman Gupta

Ma solution courte. À la fin, j'ai supprimé les doublons par filter ().

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]
1
Tim Reznich

Solution javascript pure.

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
vinayakj
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]));
0
Vincent Tang

Créez une carte avec un nombre de toutes les valeurs uniques (sur les tableaux). Puis concattez tous les tableaux et filtrez les valeurs non uniques à l’aide de la carte.

const symsym = (...args) => {
  // create a Map from the unique value of each array
  const m = args.reduce((r, a) => {
    // get unique values of array, and add to Map
    new Set(a).forEach((n) => r.set(n, (r.get(n) || 0) + 1));
    
    return r;
  }, new Map());
  
  // combine all arrays
  return [].concat(...args)
    // remove all items that appear more than once in the map
    .filter((n) => m.get(n) === 1); 
};

console.log(symsym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4])); // => Desired answer: [1, 1, 6, 5, 4]
0
Ori Drori
const exclude = (a, b) => a.filter(v => !b.includes(v))

const symDiff = (first, ...rest) => rest.reduce((acc, x) => [
  ...exclude(acc, x), 
  ...exclude(x, acc)
], first)    

/* - - - */
console.log(symDiff([1, 3], ['Saluton', 3]))    // [1, 'Saluton']
console.log(symDiff([1, 3], [2, 3], [2, 8, 5])) // [1, 8, 5]
0
kornieff

Hé si quelqu'un est intéressé c'est ma solution:

function sym (...args) {
  let fileteredArgs = [];
  let symDiff = [];
  args.map(arrayEl =>
    fileteredArgs.Push(arrayEl.filter((el, key) =>
      arrayEl.indexOf(el) === key
      )
    )
  );

  fileteredArgs.map(elArr => {
    elArr.map(el => {
      let index = symDiff.indexOf(el);
      if (index === -1) {
        symDiff.Push(el);
      } else {
        symDiff.splice(index, 1);
      }
    });
  });

  return (symDiff);
}

console.log(sym([1, 2, 3, 3], [5, 2, 1, 4]));
0
user1137342
function sym(arr1, arr2, ...rest) {

  //creating a array which has unique numbers from both the arrays
  const union = [...new Set([...arr1,...arr2])];

  // finding the Symmetric Difference between those two arrays
  const diff= union.filter((num)=>!(arr1.includes(num)&&arr2.includes(num)))

  //if there are more than 2 arrays
  if(rest.length){
    // recurrsively call till rest become 0 
    // i.e.  diff of 1,2 will be the first parameter so every recurrsive call will reduce     //  the arrays till diff between all of them are calculated.

    return sym(diff, rest[0], ...rest.slice(1))
  }
  return diff
}
0
Karan Jariwala

Alternative: utiliser la recherche dans une carte au lieu d'un tableau

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
user753642

Cela fonctionne pour moi:

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
Jonatas Walker