Comment fusionner deux tableaux en JavaScript et dédoublonner les éléments

1368

J'ai deux tableaux JavaScript:

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

Je veux que la sortie soit:

var array3 = ["Vijendra","Singh","Shakya"];

Le tableau de sortie doit avoir supprimé les mots répétés.

Comment fusionner deux tableaux en JavaScript pour ne récupérer que les éléments uniques de chaque tableau dans le même ordre qu'ils ont été insérés dans les tableaux d'origine?

Vijjendra
la source
1
Avant de publier une nouvelle réponse, considérez qu'il existe déjà plus de 75 réponses à cette question. Veuillez vous assurer que votre réponse fournit des informations qui ne figurent pas parmi les réponses existantes.
janniks
[... nouveau set ([... [1, 2, 3], ... [2, 3, 4]])] résultat [1, 2, 3, 4]
Denis Giffeler
Si vous voulez une solution plus générique qui couvre également la fusion en profondeur, jetez plutôt un œil à cette question . Certaines réponses couvrent également les tableaux.
Martin Braun

Réponses:

1667

Pour simplement fusionner les tableaux (sans supprimer les doublons)

Utilisation de la version ES5 Array.concat:

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];

console.log(array1.concat(array2));

La version ES6 utilise la déstructuration

const array1 = ["Vijendra","Singh"];
const array2 = ["Singh", "Shakya"];
const array3 = [...array1, ...array2];

Puisqu'il n'y a pas de méthode intégrée pour supprimer les doublons ( ECMA-262 en fait, Array.forEachce qui serait parfait pour cela), nous devons le faire manuellement:

Array.prototype.unique = function() {
    var a = this.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }

    return a;
};

Ensuite, pour l'utiliser:

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
// Merges both arrays and gets unique items
var array3 = array1.concat(array2).unique(); 

Cela préservera également l'ordre des tableaux (c'est-à-dire, aucun tri n'est nécessaire).

Étant donné que de nombreuses personnes sont ennuyées par l'augmentation de prototype Array.prototypeet les for inboucles, voici une façon moins invasive de l'utiliser:

function arrayUnique(array) {
    var a = array.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }

    return a;
}

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
    // Merges both arrays and gets unique items
var array3 = arrayUnique(array1.concat(array2));

Pour ceux qui ont la chance de travailler avec des navigateurs où ES5 est disponible, vous pouvez utiliser Object.definePropertycomme ceci:

Object.defineProperty(Array.prototype, 'unique', {
    enumerable: false,
    configurable: false,
    writable: false,
    value: function() {
        var a = this.concat();
        for(var i=0; i<a.length; ++i) {
            for(var j=i+1; j<a.length; ++j) {
                if(a[i] === a[j])
                    a.splice(j--, 1);
            }
        }

        return a;
    }
});
LiraNuna
la source
281
Notez que cet algorithme est O (n ^ 2).
Gumbo
7
Soit [a, b, c]et [x, b, d]soyez les tableaux (supposez les guillemets). concat donne [a, b, c, x, b, d]. La sortie de l'unique () ne serait-elle pas [a, c, x, b, d]. Cela ne préserve pas l'ordre, je pense - je crois que OP veut[a, b, c, x, d]
Amarghosh
89
OP a accepté la première réponse qui l'a fait travailler et a signé, semble-t-il. Nous comparons toujours les solutions des autres, trouvant-n-fixant les défauts, améliorant les performances, assurant leur compatibilité partout et ainsi de suite ... La beauté de stackoverflow :-)
Amarghosh
6
Au départ, j'ai voté contre, mais j'ai changé d'avis. L'affectation de prototypes à Array.prototype a pour conséquence de casser les instructions "for ... in". La meilleure solution est donc probablement d'utiliser une fonction comme celle-ci mais de ne pas l'attribuer comme prototype. Certaines personnes peuvent affirmer que les instructions "for ... in" ne doivent pas être utilisées pour itérer les éléments du tableau de toute façon, mais les gens les utilisent souvent de cette façon, donc au moins cette solution doit être utilisée avec prudence.
Code Commander
16
vous devez toujours utiliser for ... inavec hasOwnPropertyauquel cas la méthode prototype est très bien
mulllhausen
601

Avec Underscore.js ou Lo-Dash, vous pouvez faire:

console.log(_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

http://underscorejs.org/#union

http://lodash.com/docs#union

GijsjanB
la source
70
Ou, peut-être encore mieux que le soulignement, le lodash compatible API .
Brian M. Hunt
3
@Ygg À partir des documents lodash. "Renvoie un nouveau tableau de valeurs uniques, dans l'ordre , qui sont présentes dans un ou plusieurs tableaux."
Richard Ayotte
4
Je préfère underscore.js. Ce que j'ai fini par utiliser underscore.flatten(), c'est mieux que l'union en ce qu'il prend un tableau de tableaux.
tisserand
8
@weaver _.flatten fusionne, mais ne «dédouble pas».
GijsjanB
9
Performances rapides sur lodash vs la meilleure réponse: jsperf.com/merge-two-arrays-keeping-only-unique-values
slickplaid
288

Concaténez d'abord les deux tableaux, puis filtrez uniquement les éléments uniques:

var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b)
var d = c.filter((item, pos) => c.indexOf(item) === pos)

console.log(d) // d is [1, 2, 3, 101, 10]

Éditer

Comme suggéré, une solution plus performante serait de filtrer les éléments uniques bavant de les concaténer avec a:

var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b.filter((item) => a.indexOf(item) < 0))

console.log(c) // c is [1, 2, 3, 101, 10]

simo
la source
5
La solution originale présente l'avantage de supprimer les dupes dans chaque tableau source. Je suppose que cela dépend de votre contexte que vous utiliseriez.
theGecko
Vous pouvez fusionner différents pour le support IE6: c = Array.from (nouveau Set (c));
Tobi G.18
Si je veux vraiment changer apour ajouter b, sera-t-il alors préférable de parcourir et d'utiliser push? a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
impressionnant
1
Juste un rappel de l'utilisation actuelle du navigateur caniuse.com/usage-table pour les personnes soucieuses d'IE6.
pmrotule
10
@Andrew: Encore mieux: 1. var c = [...a, ...b.filter(o => !~a.indexOf(o))]; 2. var c = [...new Set([...a, ...b])];
7vujy0f0hy
203

Il s'agit d'une solution ECMAScript 6 utilisant l' opérateur générique et les génériques de tableau.

Actuellement, cela ne fonctionne qu'avec Firefox, et éventuellement avec la prévisualisation technique d'Internet Explorer.

Mais si vous utilisez Babel , vous pouvez l'avoir maintenant.

const input = [
  [1, 2, 3],
  [101, 2, 1, 10],
  [2, 1]
];
const mergeDedupe = (arr) => {
  return [...new Set([].concat(...arr))];
}

console.log('output', mergeDedupe(input));

Adria
la source
14
Cela devrait être ajouté à la réponse acceptée. Cette solution est beaucoup plus efficace et beaucoup plus élégante que ce qui est actuellement possible, mais c'est ce que nous serons inévitablement en mesure de faire (et que nous devrions faire pour rester dans ce domaine).
EmmaGamma
Ce n'est pas la même chose que la question de l'OP (cela semble être plus une flatmap qu'autre chose) mais un vote positif car c'est génial.
jedd.ahyoung
4
Difficile de dire que cela devrait être la réponse acceptée puisque la question est de 2009. Mais oui, ce n'est pas seulement plus "performant" mais aussi "élégant"
Cezar Augusto
11
Array.frompeut être utilisé à la place de l'opérateur d'épandage: Array.from(new Set([].concat(...arr)))
Henry Blyth
1
C'est très élégant. Malheureusement, Typescript ne le supporte pas encore. stackoverflow.com/questions/33464504/…
Ben Carp
190

ES6

array1.push(...array2) // => don't remove duplication 

OU

[...array1,...array2] //   =>  don't remove duplication 

OU

[...new Set([...array1 ,...array2])]; //   => remove duplication
Abdennour TOUMI
la source
1
Le 1er / 2e exemple n'est pas uniondu tout + le 1er exemple fait exploser la pile pour les grands Arrays + le 3e exemple est incroyablement lent et consomme beaucoup de mémoire, car deux intermédiaires Arraydoivent être construits + le 3e exemple ne peut être utilisé unionqu'avec un nombre de Arrays au moment de la compilation.
alors comment feriez-vous?
David Noreña
14
Setest le chemin à parcourir ici
philk
3
Notez que pour set ne peut pas dédupliquer deux objets qui ont les mêmes paires de valeurs de clé à moins qu'il ne s'agisse des mêmes références d'objet.
Jun711
2
Ne fonctionne pas avec un tableau d'objets, car il fusionnera uniquement les références d'objets et ne se souciera pas si les objets eux-mêmes sont égaux.
Wilson Biggs
87

À l'aide d'un ensemble (ECMAScript 2015), ce sera aussi simple que cela:

const array1 = ["Vijendra", "Singh"];
const array2 = ["Singh", "Shakya"];
console.log(Array.from(new Set(array1.concat(array2))));

Benny Neugebauer
la source
7
Je considère cela comme la "réponse acceptée" pour utiliser ES6.
mwieczorek
9
@mwieczorek Que diriez-vous:const array3 = [...new Set(array1.concat(array2))]
Robby Cornelissen
5
Cela ne fonctionne pas si vous utilisez un tableau d'objets
carkod
1
pour fusionner différents objets sans dupliquer: stackoverflow.com/a/54134237/3131433
Rakibul Haq
38

Voici une version légèrement différente de la boucle. Avec certaines des optimisations de la dernière version de Chrome, c'est la méthode la plus rapide pour résoudre l'union des deux tableaux (Chrome 38.0.2111).

http://jsperf.com/merge-two-arrays-keeping-only-unique-values

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [];

var arr = array1.concat(array2),
  len = arr.length;

while (len--) {
  var itm = arr[len];
  if (array3.indexOf(itm) === -1) {
    array3.unshift(itm);
  }
}

en boucle: ~ 589 k ops / s
filtre: ~ 445 k ops / s
lodash: 308k ops / s
pour les boucles: 225k ops / s

Un commentaire a souligné qu'une de mes variables de configuration faisait que ma boucle devançait le reste, car elle n'avait pas à initialiser un tableau vide pour écrire. Je suis d'accord avec cela, j'ai donc réécrit le test pour uniformiser les règles du jeu et inclus une option encore plus rapide.

http://jsperf.com/merge-two-arrays-keeping-only-unique-values/52

let whileLoopAlt = function (array1, array2) {
    const array3 = array1.slice(0);
    let len1 = array1.length;
    let len2 = array2.length;
    const assoc = {};

    while (len1--) {
        assoc[array1[len1]] = null;
    }

    while (len2--) {
        let itm = array2[len2];

        if (assoc[itm] === undefined) { // Eliminate the indexOf call
            array3.push(itm);
            assoc[itm] = null;
        }
    }

    return array3;
};

Dans cette solution alternative, j'ai combiné la solution de tableau associatif d'une réponse pour éliminer l' .indexOf()appel dans la boucle qui ralentissait beaucoup les choses avec une deuxième boucle, et j'ai inclus certaines des autres optimisations que d'autres utilisateurs ont suggérées dans leurs réponses. .

La meilleure réponse ici avec la double boucle sur chaque valeur (i-1) est encore beaucoup plus lente. lodash se porte toujours bien, et je le recommanderais toujours à tous ceux qui ne veulent pas ajouter une bibliothèque à leur projet. Pour ceux qui ne le souhaitent pas, ma boucle while est toujours une bonne réponse et la réponse du filtre a une très bonne indication ici, battant tout sur mes tests avec le dernier Canary Chrome (44.0.2360) au moment de la rédaction de cet article.

Découvrez la réponse de Mike et la réponse de Dan Stocker si vous voulez à l' étape d'un cran dans la vitesse. Ce sont de loin les résultats les plus rapides de tous les résultats après avoir examiné presque toutes les réponses viables.

slickplaid
la source
Il y a une faille dans votre méthodologie: vous mettez la création de array3 dans la phase de configuration, alors que ce coût ne devrait faire partie que du score de votre solution basée sur le temps. Avec cette ligne déplacée , votre solution tombe à la vitesse de celle basée sur la boucle for. Je comprends que le tableau peut être réutilisé, mais peut-être que les autres algorithmes pourraient également bénéficier de ne pas avoir à déclarer et à initialiser tous les blocs de construction nécessaires.
doldt
Je suis d'accord avec votre prémisse @doldt, mais en désaccord avec vos résultats. Il existe un défaut de conception fondamental avec la suppression des entrées basée sur la boucle, dans la mesure où vous devez revérifier la longueur du tableau après avoir supprimé les éléments, ce qui entraîne un temps d'exécution plus lent. Une boucle while travaillant à l'envers n'a pas ces effets. Voici un exemple avec la suppression d'autant de variables de configuration que je peux sans trop changer leur réponse d'origine: jsperf.com/merge-two-arrays-keeping-only-unique-values/19
slickplaid
@slickplaid les tests liés sont vides et la prochaine révision de jsperf se bloque dans la boucle while.
doldt
@doldt J'ai répondu à vos préoccupations dans ma réponse et y ai également ajouté un test mis à jour. Faites-moi savoir si vous êtes d'accord ou non avec ces résultats. De plus, j'ai ajouté un autre meilleur résultat en utilisant un tableau associatif.
slickplaid
1
@slickplaid Merci d'avoir configuré la page de perf étendue. A moins que je manque quelque chose, la fonction "whileLoopAlt2" ne fonctionne pas? Il crée un nouveau tableau contenant le premier tableau et le deuxième tableau (dans l'ordre inverse). Pour éviter toute confusion, j'ai fait une autre révision qui supprime la fonction cassée. J'ai également ajouté un exemple supplémentaire: jsperf.com/merge-two-arrays-keeping-only-unique-values/22
Stephen S
37

Vous pouvez le faire simplement avec ECMAScript 6,

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
  • Utilisez l' opérateur d'étalement pour concaténer le tableau.
  • Utilisez Set pour créer un ensemble distinct d'éléments.
  • Utilisez à nouveau l'opérateur d'étalement pour convertir l'ensemble en un tableau.
Rajaprabhu Aravindasamy
la source
2
J'obtiens une erreur: le type «Set <string>» n'est pas un type de tableau.
gattsbr
3
Et si pour une raison quelconque ne voulez pas utiliser l'opérateur de diffusion, il y a aussi: Array.from(new Set(array1.concat(array2))).
kba
@gattsbr, avec TypeScript dans tsconfig.json, vous pouvez ajouter "downlevelIteration": trueà compilerOptions.
VincentPerrin.com
19
Array.prototype.merge = function(/* variable number of arrays */){
    for(var i = 0; i < arguments.length; i++){
        var array = arguments[i];
        for(var j = 0; j < array.length; j++){
            if(this.indexOf(array[j]) === -1) {
                this.push(array[j]);
            }
        }
    }
    return this;
};

Une bien meilleure fonction de fusion de tableaux.

GAgnew
la source
4
var test = ['a', 'b', 'c']; console.log(test); imprimera ["a", "b", "c", merge: function]
Doubidou
Excellente solution. J'ai mis à jour le test jsperf publié ci-dessus par @slickplaid ( jsperf.com/merge-two-arrays-keeping-only-unique-values/3 ) et il semble que ce soit le plus rapide d'entre eux.
Cobra
@Cobra Au risque de paraître mesquin, fonctionnant sur Chrome 40.0.2214 (au plus tard le 18/02/15), cette réponse est 53% plus lente que la mienne. OTOH IE11 ne semble pas du tout optimisé pour ma réponse. :) Chrome mobile est toujours d'actualité. Honnêtement, si vous utilisez lodash / _, ce que la plupart d'entre nous devraient, la vraie réponse est déjà assez haut dans cette liste. :)
slickplaid
@slickplaid True, et c'est un peu plus rapide, même comparé à celui de lodash / _. Je finirai probablement par changer mon implémentation à un moment ou à un autre pour quelque chose de similaire au vôtre. : D
Cobra
1
Je ne sais pas quels sont les coûts de la méthode indexOf () mais c'est probablement la méthode compatible ES5 la plus rapide. Ne vaut également rien que la longueur variable des arguments n'est pas nécessaire. Cette méthode est chaînable. @slickplaid Le chargement d'une bibliothèque n'est jamais une réponse à une question "comment faire en javascript". de nombreuses bibliothèques ont sûrement une fonction pour faire ce travail en 7 lignes.
dehart
19

Je jette juste mes deux cents.

function mergeStringArrays(a, b){
    var hash = {};
    var ret = [];

    for(var i=0; i < a.length; i++){
        var e = a[i];
        if (!hash[e]){
            hash[e] = true;
            ret.push(e);
        }
    }

    for(var i=0; i < b.length; i++){
        var e = b[i];
        if (!hash[e]){
            hash[e] = true;
            ret.push(e);
        }
    }

    return ret;
}

C'est une méthode que j'utilise beaucoup, elle utilise un objet comme table de hachage pour effectuer la vérification des doublons. En supposant que le hachage est O (1), cela s'exécute dans O (n) où n est a.length + b.length. Honnêtement, je n'ai aucune idée de la façon dont le navigateur effectue le hachage, mais il fonctionne bien sur plusieurs milliers de points de données.

Mike
la source
Très bien fait. Met en évidence tout (sinon tous) les autres résultats de cette page en tirant parti du tableau associatif et en évitant le bouclage d'indexOf et d'autres opérations. jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid
Votre "hachage" est la String()fonction en javascript. Ce qui pourrait fonctionner pour les valeurs primitives (bien qu'avec des collisions entre types), mais ce n'est pas un bon choix pour les tableaux d'objets.
Bergi
J'utilise une solution similaire, j'autorise le passage d'une fonction hashCode ou le passage d'une chaîne pour identifier une propriété dans l'objet à utiliser comme clé de hachage.
Robert Baker
19

Évitez simplement les boucles imbriquées (O (n ^ 2)) et .indexOf()(+ O (n)).

function merge(a, b) {
    var hash = {}, i;
    for (i=0; i<a.length; i++) {
        hash[a[i]]=true;
    } 
    for (i=0; i<b.length; i++) {
        hash[b[i]]=true;
    } 
    return Object.keys(hash);
}
Dan Stocker
la source
2
C'est assez étonnant, surtout si vous faites des cordes. Les nombres auraient besoin d'une étape supplémentaire pour les conserver tels quels. Cette fonction bat considérablement toutes les autres options si cela ne vous dérange pas (ou ne vous souciez pas) que tout soit une chaîne après avoir terminé. Bon travail. Résultats des performances ici: jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid
18

Le meilleur de cette réponse simplifié et transformé en une belle fonction:

function mergeUnique(arr1, arr2){
    return arr1.concat(arr2.filter(function (item) {
        return arr1.indexOf(item) === -1;
    }));
}
Andrew
la source
2
Je crois que c'est beaucoup plus propre que la réponse acceptée. De plus, il semble que le filtre soit pris en charge dans ECMAScript 5.1 +, qui est maintenant assez pris en charge.
Tom Fobear
Cela aurait dû être accepté
wallop
c'est tellement plus succinct.
Mox
15

Pourquoi n'utilisez-vous pas un objet? On dirait que vous essayez de modéliser un ensemble. Cependant, cela ne préservera pas la commande.

var set1 = {"Vijendra":true, "Singh":true}
var set2 = {"Singh":true,  "Shakya":true}

// Merge second object into first
function merge(set1, set2){
  for (var key in set2){
    if (set2.hasOwnProperty(key))
      set1[key] = set2[key]
  }
  return set1
}

merge(set1, set2)

// Create set from array
function setify(array){
  var result = {}
  for (var item in array){
    if (array.hasOwnProperty(item))
      result[array[item]] = true
  }
  return result
}
Nick Retallack
la source
Tu ne veux pas dire if (!set1.hasOwnProperty(key))?
Gumbo
2
Pourquoi est-ce que je veux dire ça? Le but de cette condition est d'ignorer les propriétés qui peuvent se trouver dans le prototype de l'objet.
Nick Retallack
12

La meilleure solution...

Vous pouvez vérifier directement dans la console du navigateur en appuyant sur ...

Sans doublon

a = [1, 2, 3];
b = [3, 2, 1, "prince"];

a.concat(b.filter(function(el) {
    return a.indexOf(el) === -1;
}));

Avec doublon

["prince", "asish", 5].concat(["ravi", 4])

Si vous voulez sans doublon, vous pouvez essayer une meilleure solution à partir d'ici - Shouting Code .

[1, 2, 3].concat([3, 2, 1, "prince"].filter(function(el) {
    return [1, 2, 3].indexOf(el) === -1;
}));

Essayez sur la console du navigateur Chrome

 f12 > console

Production:

["prince", "asish", 5, "ravi", 4]

[1, 2, 3, "prince"]
Zigri2612
la source
Il ne supprime pas les doublons du tableau de sortie.
Shahar
9

Mon centime et demi:

Array.prototype.concat_n_dedupe = function(other_array) {
  return this
    .concat(other_array) // add second
    .reduce(function(uniques, item) { // dedupe all
      if (uniques.indexOf(item) == -1) {
        uniques.push(item);
      }
      return uniques;
    }, []);
};

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var result = array1.concat_n_dedupe(array2);

console.log(result);
Hero Qu
la source
Il n'utilise rien de nouveau dans ES6, ai-je raté quelque chose?
Bergi
@Bergi: Oui, vous avez raison. Merci de noter. D'une manière ou d'une autre, je jouais avec ce script et il y avait probablement une version avec la fonction ES6, mais maintenant il contient indexOf qui existe depuis des siècles. Mon erreur, désolé.
Hero Qu
8

Vous pouvez y parvenir en utilisant simplement Underscore.js => uniq :

array3 = _.uniq(array1.concat(array2))

console.log(array3)

Il imprimera ["Vijendra", "Singh", "Shakya"] .

Mohideen bin Mohammed
la source
7

Pour ES6, une seule ligne:

a = [1, 2, 3, 4]
b = [4, 5]
[...new Set(a.concat(b))]  // [1, 2, 3, 4, 5]
user1079877
la source
7

Je sais que cette question ne concerne pas les tableaux d'objets, mais les chercheurs se retrouvent ici.

il vaut donc la peine d'ajouter pour les futurs lecteurs une méthode de fusion ES6 appropriée, puis de supprimer les doublons

tableau d'objets :

var arr1 = [ {a: 1}, {a: 2}, {a: 3} ];
var arr2 = [ {a: 1}, {a: 2}, {a: 4} ];

var arr3 = arr1.concat(arr2.filter( ({a}) => !arr1.find(f => f.a == a) ));

// [ {a: 1}, {a: 2}, {a: 3}, {a: 4} ]
Stavm
la source
6
//Array.indexOf was introduced in javascript 1.6 (ECMA-262) 
//We need to implement it explicitly for other browsers, 
if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt, from)
  {
    var len = this.length >>> 0;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}
//now, on to the problem

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var merged = array1.concat(array2);
var t;
for(i = 0; i < merged.length; i++)
  if((t = merged.indexOf(i + 1, merged[i])) != -1)
  {
    merged.splice(t, 1);
    i--;//in case of multiple occurrences
  }

L'implémentation de la indexOfméthode pour les autres navigateurs provient de MDC

Amarghosh
la source
1
Je ne pouvais pas le trouver dans w3schools, c'est pourquoi je l'ai écrit. w3schools.com/jsref/jsref_obj_array.asp Faut -il un fromparamètre btw?
Amarghosh
Merci @Gumbo et @meder - je vais changer mes favoris maintenant. Je n'ai encore rien fait de sérieux en js et j'utilise w3schools comme référence occasionnelle (c'est tout ce dont j'ai jamais eu besoin) - c'est peut-être pour cela que je ne m'en suis pas rendu compte.
Amarghosh
MDC dit que indexOf nécessite javascript 1.6 Serait-il sûr de supposer que les navigateurs courants (> = FF2,> IE6, etc.) le prendraient en charge?
Amarghosh
4
IE6 ne prend pas en charge Array.prototype.indexOf, collez simplement la méthode de support fournie par Mozilla pour qu'IE ne génère pas d'erreur.
meder omuraliev
mis à jour en utilisant indexOf. Nettoyé le code en supprimant la partie commentée. @meder - merci encore.
Amarghosh
6

Nouvelle solution (qui utilise Array.prototype.indexOfet Array.prototype.concat):

Array.prototype.uniqueMerge = function( a ) {
    for ( var nonDuplicates = [], i = 0, l = a.length; i<l; ++i ) {
        if ( this.indexOf( a[i] ) === -1 ) {
            nonDuplicates.push( a[i] );
        }
    }
    return this.concat( nonDuplicates )
};

Usage:

>>> ['Vijendra', 'Singh'].uniqueMerge(['Singh', 'Shakya'])
["Vijendra", "Singh", "Shakya"]

Array.prototype.indexOf (pour Internet Explorer):

Array.prototype.indexOf = Array.prototype.indexOf || function(elt)
  {
    var len = this.length >>> 0;

    var from = Number(arguments[1]) || 0;
    from = (from < 0) ? Math.ceil(from): Math.floor(from); 
    if (from < 0)from += len;

    for (; from < len; from++)
    {
      if (from in this && this[from] === elt)return from;
    }
    return -1;
  };
meder omuraliev
la source
@Mender: si l'ordre n'a pas d'importance alors comment je fais ça
Vijjendra
1
Ce n'est pas une méthode ECMAScript standard définie pour Array.prototype, bien que je sache que vous pouvez facilement la définir pour IE et d'autres navigateurs qui ne la prennent pas en charge.
meder omuraliev
Notez que cet algorithme est O (n ^ 2).
Gumbo
Quel algorithme est votre réponse?
meder omuraliev
@meder: Mon algorithme est un algorithme d'union. L'union elle-même se fait en O (n + m), mais le tri prend au plus O (n · log n + m · log m). Donc, tout l'algorithme est O (n · log n + m · log m).
Gumbo
6

Cela peut être fait en utilisant Set.

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var array3 = array1.concat(array2);
var tempSet = new Set(array3);
array3 = Array.from(tempSet);

//show output
document.body.querySelector("div").innerHTML = JSON.stringify(array3);
<div style="width:100%;height:4rem;line-height:4rem;background-color:steelblue;color:#DDD;text-align:center;font-family:Calibri" > 
  temp text 
</div>

Karan Singla
la source
6

Il existe de nombreuses solutions pour fusionner deux tableaux. Ils peuvent être divisés en deux catégories principales (à l'exception de l'utilisation de bibliothèques tierces comme lodash ou underscore.js).

a) combinez deux tableaux et supprimez les éléments en double.

b) filtrer les éléments avant de les combiner.

Combinez deux tableaux et supprimez les éléments en double

Combiner

// mutable operation(array1 is the combined array)
array1.push(...array2);
array1.unshift(...array2);

// immutable operation
const combined = array1.concat(array2);
const combined = [...array1, ...array2];    // ES6

Unificateur

Il existe de nombreuses façons d'unifier un tableau, je suggère personnellement ci-dessous deux méthodes.

// a little bit tricky
const merged = combined.filter((item, index) => combined.indexOf(item) === index);
const merged = [...new Set(combined)];

Filtrer les éléments avant de les combiner

Il existe également de nombreuses façons, mais je suggère personnellement le code ci-dessous en raison de sa simplicité.

const merged = array1.concat(array2.filter(secItem => !array1.includes(secItem)));
TopW3
la source
5
Array.prototype.add = function(b){
    var a = this.concat();                // clone current object
    if(!b.push || !b.length) return a;    // if b is not an array, or empty, then return a unchanged
    if(!a.length) return b.concat();      // if original is empty, return b

    // go through all the elements of b
    for(var i = 0; i < b.length; i++){
        // if b's value is not in a, then add it
        if(a.indexOf(b[i]) == -1) a.push(b[i]);
    }
    return a;
}

// Example:
console.log([1,2,3].add([3, 4, 5])); // will output [1, 2, 3, 4, 5]
Lajos Meszaros
la source
5
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)

La bonne chose à propos de celui-ci est la performance et que vous en général, lorsque vous travaillez avec des tableaux, vous enchaînez des méthodes comme le filtre, la carte, etc. afin que vous puissiez ajouter cette ligne et elle concaténera et dédupliquera array2 avec array1 sans avoir besoin d'une référence à la dernière. une (lorsque vous enchaînez des méthodes que vous n'avez pas), par exemple:

someSource()
.reduce(...)
.filter(...)
.map(...) 
// and now you want to concat array2 and deduplicate:
.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)
// and keep chaining stuff
.map(...)
.find(...)
// etc

(Je n'aime pas polluer Array.prototype et ce serait le seul moyen de respecter la chaîne - la définition d'une nouvelle fonction la brisera - donc je pense que quelque chose comme ça est le seul moyen d'y parvenir)

cancerbero
la source
4

Vous pouvez essayer ceci:

const union = (a, b) => Array.from(new Set([...a, ...b]));

console.log(union(["neymar","messi"], ["ronaldo","neymar"]));

mitesh7172
la source
3

Une approche fonctionnelle avec ES2015

Suivant l'approche fonctionnelle, a unionde deux Arrays n'est que la composition de concatet filter. Afin de fournir des performances optimales, nous avons recours au Settype de données natif , qui est optimisé pour les recherches de propriétés.

Quoi qu'il en soit, la question clé associée à une unionfonction est de savoir comment traiter les doublons. Les permutations suivantes sont possibles:

Array A      + Array B

[unique]     + [unique]
[duplicated] + [unique]
[unique]     + [duplicated]
[duplicated] + [duplicated]

Les deux premières permutations sont faciles à manipuler avec une seule fonction. Cependant, les deux derniers sont plus compliqués, car vous ne pouvez pas les traiter tant que vous comptez sur les Setrecherches. Étant donné que le passage à Objectdes recherches de propriétés anciennes simples entraînerait une sérieuse performance, l'implémentation suivante ignore simplement les troisième et quatrième permutations. Vous devrez créer une version distincte de unionpour les prendre en charge.


// small, reusable auxiliary functions

const comp = f => g => x => f(g(x));
const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// de-duplication

const dedupe = comp(afrom) (createSet);


// the actual union function

const union = xs => ys => {
  const zs = createSet(xs);  
  return concat(xs) (
    filter(x => zs.has(x)
     ? false
     : zs.add(x)
  ) (ys));
}


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,4,5,6,6];


// here we go

console.log( "unique/unique", union(dedupe(xs)) (ys) );
console.log( "duplicated/unique", union(xs) (ys) );

À partir de là, il devient trivial d'implémenter une unionnfonction, qui accepte n'importe quel nombre de tableaux (inspiré des commentaires de naomik):

// small, reusable auxiliary functions

const uncurry = f => (a, b) => f(a) (b);
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);

const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// union and unionn

const union = xs => ys => {
  const zs = createSet(xs);  
  return concat(xs) (
    filter(x => zs.has(x)
     ? false
     : zs.add(x)
  ) (ys));
}

const unionn = (head, ...tail) => foldl(union) (head) (tail);


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,4,5,6,6];
const zs = [0,1,2,3,4,5,6,7,8,9];


// here we go

console.log( unionn(xs, ys, zs) );

Il s'avère que unionnc'est juste foldl(aka Array.prototype.reduce), qui prend unioncomme réducteur. Remarque: Étant donné que l'implémentation n'utilise pas d'accumulateur supplémentaire, elle générera une erreur lorsque vous l'appliquerez sans arguments.


la source
1
quelques commentaires: je l'ai remarqué flipet ne notfsont pas utilisés. unionByPrécisez également les détails d'implémentation des fuites (nécessite une connaissance implicite du Settype). Ce serait bien si vous pouviez faire quelque chose comme ça: union = unionBy (apply)et unionci = unionBy (p => x => p(x.toLowerCase())). De cette façon, l'utilisateur envoie simplement quelle que soit la valeur de regroupement à p- juste une idée ^ _ ^
Merci
zsla déclaration de variable manque également var/ letmot
Merci
1
voici un extrait de code pour clarifier [gist: unionBy.js ]
Merci
@naomik Après avoir repensé ma solution pendant un certain temps, je ne sais plus si c'est la bonne façon de passer le prédicat. Tout ce que vous gagnez est une transformation de chaque élément du deuxième tableau. Je me demande si cette approche ne résout pas seulement les problèmes de jouets.
3

pour le plaisir ... voici une solution monoligne:

const x = [...new Set([['C', 'B'],['B', 'A']].reduce( (a, e) => a.concat(e), []))].sort()
// ['A', 'B', 'C']

Pas particulièrement lisible, mais cela peut aider quelqu'un:

  1. Applique une fonction de réduction avec la valeur initiale de l'accumulateur définie sur un tableau vide.
  2. La fonction de réduction utilise concat pour ajouter chaque sous-tableau au tableau d'accumulateurs.
  3. Le résultat de ceci est passé en tant que paramètre constructeur pour créer un nouveau Set .
  4. L'opérateur d'étalement est utilisé pour convertir le Seten un tableau.
  5. La sort()fonction est appliquée au nouveau tableau.
Mark Tyers
la source
2
Aussi, au lieu de reduce()vous pouvez utiliserArray.from(set)
Eran Goldin
3

DeDuplicate single or Merge and DeDuplicate multiple array inputs. Exemple ci-dessous.

utilisation d'ES6 - Définir, pour, déstructurer

J'ai écrit cette fonction simple qui prend plusieurs arguments de tableau. Fait à peu près la même chose que la solution ci-dessus, il a juste un cas d'utilisation plus pratique. Cette fonction ne concatène pas les valeurs en double dans un seul tableau afin de pouvoir les supprimer ultérieurement.

DÉFINITION DE LA FONCTION COURTE (seulement 9 lignes)

/**
* This function merging only arrays unique values. It does not merges arrays in to array with duplicate values at any stage.
*
* @params ...args Function accept multiple array input (merges them to single array with no duplicates)
* it also can be used to filter duplicates in single array
*/
function arrayDeDuplicate(...args){
   let set = new Set(); // init Set object (available as of ES6)
   for(let arr of args){ // for of loops through values
      arr.map((value) => { // map adds each value to Set object
         set.add(value); // set.add method adds only unique values
      });
   }
   return [...set]; // destructuring set object back to array object
   // alternativly we culd use:  return Array.from(set);
}

UTILISER L'EXEMPLE CODEPEN :

// SCENARIO 
let a = [1,2,3,4,5,6];
let b = [4,5,6,7,8,9,10,10,10];
let c = [43,23,1,2,3];
let d = ['a','b','c','d'];
let e = ['b','c','d','e'];

// USEAGE
let uniqueArrayAll = arrayDeDuplicate(a, b, c, d, e);
let uniqueArraySingle = arrayDeDuplicate(b);

// OUTPUT
console.log(uniqueArrayAll); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 43, 23, "a", "b", "c", "d", "e"]
console.log(uniqueArraySingle); // [4, 5, 6, 7, 8, 9, 10]
DevWL
la source
Pourquoi utiliser arr.mapici? Vous l'utilisez comme un foreach, car le résultat est ignoré
Antony
J'ai utilisé return Array.from (set.values ​​()); , car vscode donne une erreur pour le retour [... set];
makkasi
3
var arr1 = [1, 3, 5, 6];
var arr2 = [3, 6, 10, 11, 12];
arr1.concat(arr2.filter(ele => !arr1.includes(ele)));
console.log(arr1);

output :- [1, 3, 5, 6, 10, 11, 12]
Bharti Ladumor
la source
3

var array1 = ["one","two"];
var array2 = ["two", "three"];
var collectionOfTwoArrays = [...array1, ...array2];    
var uniqueList = array => [...new Set(array)];
console.log('Collection :');
console.log(collectionOfTwoArrays);    
console.log('Collection without duplicates :');
console.log(uniqueList(collectionOfTwoArrays));

Shiva
la source