J'essaie de trouver une solution de différence symétrique à l'aide de javascript qui remplisse les objectifs suivants:
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]
Voici une version qui utilise l'objet Set
pour accélérer la recherche. Voici la logique de base:
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]
où 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);
}
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))));
}
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!
Utilisez simplement _.xor ou copiez le code lodash.
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 ()
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]
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]
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"]
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]));
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]
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]
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]));
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
}
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])
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]));