Jak scalić dwie tablice w JavaScript i zduplikować elementy


1364

Mam dwie tablice JavaScript:

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

Chcę, aby wynik był:

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

Tablica wyjściowa powinna mieć powtarzane słowa usunięte.

Jak scalić dwie tablice w JavaScript, aby uzyskać tylko unikalne elementy z każdej tablicy w tej samej kolejności, w jakiej zostały wstawione do oryginalnych tablic?


1
Zanim opublikujesz nową odpowiedź, zastanów się, że istnieje już ponad 75 odpowiedzi na to pytanie. Upewnij się, że twoja odpowiedź zawiera informacje, których nie ma wśród odpowiedzi istniejących.
janniks

[... nowy zestaw ([... [1, 2, 3], ... [2, 3, 4]]]] wynik [1, 2, 3, 4]
Denis Giffeler

Jeśli chcesz bardziej ogólnego rozwiązania, które obejmuje również głębokie scalanie , zamiast tego spójrz na to pytanie . Niektóre odpowiedzi obejmują również tablice.
Martin Braun

Odpowiedzi:


1661

Aby po prostu scalić tablice (bez usuwania duplikatów)

Zastosowanie wersji ES5 Array.concat:

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

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

Wersja ES6 używa destrukcji

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

Ponieważ nie ma „wbudowanego” sposobu usuwania duplikatów ( ECMA-262 faktycznie ma, Array.forEachco byłoby do tego świetne), musimy to zrobić ręcznie:

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

Następnie, aby go użyć:

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

Pozwoli to również zachować kolejność tablic (tzn. Nie trzeba sortować).

Ponieważ wiele osób denerwuje się powiększaniem prototypów Array.prototypei for inpętlami, oto mniej inwazyjny sposób ich użycia:

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

Dla tych, którzy mają szczęście pracować z przeglądarkami, w których dostępny jest ES5, możesz użyć Object.definePropertytego w następujący sposób:

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

281
Zauważ, że ten algorytm to O (n ^ 2).
Gumbo,

7
Niech [a, b, c]i [x, b, d]być tablice (zakładamy, cytaty). concat daje [a, b, c, x, b, d]. Czy wyjście unikalne () nie byłoby [a, c, x, b, d]. To nie zachowuje porządku, jak sądzę - wierzę, że OP chce[a, b, c, x, d]
Amarghosh

89
OP zaakceptował pierwszą odpowiedź, która skłoniła go do pracy, i wygląda na to, że się podpisała. Wciąż porównujemy się nawzajem, znajdujemy n-naprawiające błędy, poprawiamy wydajność, upewniamy się, że jest kompatybilna wszędzie i tak dalej ... Piękno
przepełnienia stosu

6
Pierwotnie głosowałem za tym, ale zmieniłem zdanie. Przypisanie prototypów do Array.prototype powoduje konsekwencje rozbicia instrukcji „dla… w”. Najlepszym rozwiązaniem jest więc użycie takiej funkcji, ale nie przypisanie jej jako prototypu. Niektórzy ludzie mogą argumentować, że i tak nie należy używać instrukcji „for… in” do iteracji elementów tablicy, ale ludzie często używają ich w ten sposób, więc przynajmniej należy zachować ostrożność przy takim rozwiązaniu.
Code Commander

16
Należy zawsze korzystać for ... inze hasOwnPropertyw takim przypadku metoda prototyp jest w porządku
mulllhausen

600

Za pomocą Underscore.js lub Lo-Dash możesz:

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


70
Lub, być może nawet lepszy niż podkreślenie, lodash kompatybilny z API .
Brian M. Hunt

3
@Ygg Z dokumentów Lodash. „Zwraca nową tablicę unikatowych wartości, w kolejności , które są obecne w co najmniej jednej tablicy”.
Richard Ayotte,

4
Wolę underscore.js. Skończyło się na tym underscore.flatten(), że jest lepszy niż związek, ponieważ wymaga szeregu tablic.
tkacz

8
@weaver _.flatten łączy się, ale nie „usuwa duplikatów”.
GijsjanB,

9
Szybki występ na lodash vs najlepsza odpowiedź: jsperf.com/merge-two-arrays-keeping-only-unique-values
slickplaid

288

Najpierw połącz dwie tablice, następnie odfiltruj tylko unikalne elementy:

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]

Edytować

Zgodnie z sugestią, bardziej rozsądnym rozwiązaniem byłoby odfiltrowanie unikalnych elementów bprzed połączeniem z 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]


5
Oryginalne rozwiązanie ma tę zaletę, że usuwa duplikaty w każdej tablicy źródłowej. Myślę, że to zależy od kontekstu, którego byś użył.
theGecko

Możesz połączyć różne dla obsługi IE6: c = Array.from (new Set (c));
Tobi G.

Jeśli chcę zmienić ana dodawanie b, to czy lepiej będzie zapętlić i użyć wypychania? a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
respekt

1
Przypomnienie o bieżącym użyciu przeglądarki caniuse.com/usage-table dla osób zaniepokojonych IE6.
pmrotule

10
@Andrew: Jeszcze lepiej: 1. var c = [...a, ...b.filter(o => !~a.indexOf(o))]; 2. var c = [...new Set([...a, ...b])];
7vujy0f0hy

203

Jest to rozwiązanie ECMAScript 6 wykorzystujące operator spread i generyczne tablice.

Obecnie działa tylko z przeglądarką Firefox i ewentualnie przeglądarką techniczną przeglądarki Internet Explorer.

Ale jeśli używasz Babel , możesz go mieć teraz.

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

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


14
Należy to dodać do zaakceptowanej odpowiedzi. To rozwiązanie jest o wiele bardziej wydajne i bardziej eleganckie niż to, co jest obecnie możliwe, ale nieuchronnie będziemy w stanie to zrobić (i powinniśmy nadążać w tej dziedzinie).
EmmaGamma

To nie jest dokładnie to samo, co pytanie OP (wydaje się, że jest to bardziej płaska mapa niż cokolwiek innego), ale głosowanie, ponieważ jest niesamowite.
jedd.ahyoung

4
Trudno powiedzieć, że to powinna być zaakceptowana odpowiedź, ponieważ pytanie pochodzi z 2009 roku. Ale tak, to nie tylko bardziej „wydajne”, ale także „eleganckie”
Cezar Augusto

11
Array.frommoże być użyty zamiast operatora rozprzestrzeniania: Array.from(new Set([].concat(...arr)))
Henry Blyth

1
To jest bardzo eleganckie. Niestety Maszynopis jeszcze tego nie obsługuje. stackoverflow.com/questions/33464504/…
Ben Carp

190

ES6

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

LUB

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

LUB

[...new Set([...array1 ,...array2])]; //   => remove duplication

1
Pierwszy / drugi przykład unionw ogóle nie ma + Pierwszy przykład wysadza stos w duże Arrays + 3-ty przykład jest niewiarygodnie wolny i zużywa dużo pamięci, ponieważ Arraytrzeba zbudować dwa s pośrednie + 3 przykład może być użyty tylko unionze znanym liczba Arrays w czasie kompilacji.

więc jak byś to zrobił?
David Noreña,

14
Setjest droga, aby przejść tutaj
philk

3
Zauważ, że dla zestawu nie można deduplikować dwóch obiektów, które mają te same pary wartości klucza, chyba że są to te same odwołania do obiektów.
Jun711,

2
Nie działa z tablicą obiektów, ponieważ scala tylko odwołania do obiektów i nie dba o to, czy same obiekty są równe.
Wilson Biggs,

87

Za pomocą zestawu (ECMAScript 2015) będzie to tak proste:

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


7
Uważam to za „Zaakceptowaną odpowiedź” dotyczącą używania ES6.
mwieczorek

9
@mwieczorek Co powiesz na:const array3 = [...new Set(array1.concat(array2))]
Robby Cornelissen

5
Nie działa, jeśli korzystasz z tablicy obiektów
carkod

1
do łączenia różnych obiektów bez powielania: stackoverflow.com/a/54134237/3131433
Rakibul Haq

38

Oto nieco inne podejście do pętli. Dzięki niektórym optymalizacjom w najnowszej wersji Chrome jest to najszybsza metoda rozwiązania połączenia dwóch tablic (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);
  }
}

while loop: ~ 589k ops / s
filtr: ~ 445k ops / s
lodash: 308k ops / s
dla pętli: 225k ops / s

Komentarz wskazywał, że jedna z moich zmiennych konfiguracyjnych powodowała, że ​​moja pętla wyprzedzała resztę, ponieważ nie musiała inicjować pustej tablicy, aby pisać. Zgadzam się z tym, więc przepisałem test, aby wyrównać szanse, i dodałem jeszcze szybszą opcję.

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

W tym alternatywnym rozwiązaniu połączyłem macierz asocjacyjny jednej odpowiedzi, aby wyeliminować .indexOf() wywołanie w pętli, która spowalniała wiele rzeczy w drugiej pętli, i uwzględniłem niektóre inne optymalizacje, które inni użytkownicy sugerowali również w swoich odpowiedziach .

Najlepsza odpowiedź tutaj z podwójną pętlą dla każdej wartości (i-1) jest nadal znacznie wolniejsza. lodash wciąż ma się dobrze i nadal polecam go każdemu, kto nie ma nic przeciwko dodaniu biblioteki do swojego projektu. Dla tych, którzy nie chcą, moja pętla while jest nadal dobrą odpowiedzią, a odpowiedź filtra ma tutaj bardzo mocne wyniki, bijąc wszystko na moich testach z najnowszym Canary Chrome (44.0.2360) od tego pisania.

Sprawdź odpowiedź Mike'a i odpowiedzi Dan Stocker jeśli chcesz krok to wycięcie w prędkości. To zdecydowanie najszybszy ze wszystkich wyników po przejściu prawie wszystkich realnych odpowiedzi.


W twojej metodologii jest pewna wada: umieszczasz tworzenie array3 w fazie konfiguracji, podczas gdy ten koszt powinien być tylko częścią wyniku twojego rozwiązania opartego na czasie. Po przesunięciu tej 1 linii twoje rozwiązanie spada do prędkości opartej na pętli for. Rozumiem, że tablica może być ponownie użyta, ale być może inne algorytmy również skorzystałyby na tym, że nie musiały deklarować i inicjalizować każdego niezbędnego elementu.
doldt

Zgadzam się z twoją przesłanką @doldt, ale nie zgadzam się z twoimi wynikami. Istnieje podstawowa wada projektowa polegająca na usuwaniu wpisów w oparciu o pętlę, polegająca na tym, że po usunięciu elementów trzeba ponownie sprawdzić długość tablicy, co powoduje wolniejszy czas wykonania. Pętla while działająca wstecz nie ma tych efektów. Oto przykład usuwania tak wielu zmiennych konfiguracyjnych, jak to możliwe bez zbytniej zmiany ich oryginalnej odpowiedzi: jsperf.com/merge-two-arrays-keeping-only-unique-values/19
slickplaid

@slickplaid połączone testy są puste, a kolejna wersja w jsperf zawiesza się w pętli while.
doldt 16.04.115

@doldt Odpowiedziałem na twoje obawy w mojej odpowiedzi i dodałem do niego również odpowiednio zaktualizowany test. Daj mi znać, jeśli zgadzasz się z tymi wynikami, czy nie. Dodałem też kolejny lepszy wynik, używając tablicy asocjacyjnej.
slickplaid

1
@slickplaid Dziękujemy za skonfigurowanie rozszerzonej strony perf. Chyba że czegoś mi brakuje, funkcja „whileLoopAlt2” nie działa? Tworzy nową tablicę zawierającą pierwszą tablicę i drugą tablicę (w odwrotnej kolejności). Aby uniknąć zamieszania, wprowadziłem kolejną wersję, która usuwa zepsutą funkcję. Dodałem również dodatkowy przykład: jsperf.com/merge-two-arrays-keeping-only-unique-values/22
Stephen S

37

Możesz to zrobić po prostu za pomocą ECMAScript 6,

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
  • Użyj operatora rozkładania do konkatenacji tablicy.
  • Użyj Zestawu, aby utworzyć odrębny zestaw elementów.
  • Ponownie użyj operatora rozkładania, aby przekonwertować zestaw na tablicę.

2
Pojawia się błąd: typ „Set <ciąg>” nie jest typem tablicy.
gattsbr

3
A jeśli z jakiegoś powodu nie chcesz użyć operatora spread, istnieje również: Array.from(new Set(array1.concat(array2))).
kba

@gattsbr, z TypeScript w tsconfig.json, możesz dodać "downlevelIteration": truedo 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;
};

Znacznie lepsza funkcja scalania tablic.


4
var test = ['a', 'b', 'c']; console.log(test); wydrukuje ["a", "b", "c", merge: function]
Doubidou

Doskonałe rozwiązanie Zaktualizowałem test jsperf opublikowany powyżej przez @slickplaid ( jsperf.com/merge-two-arrays-keeping-only-unique-values/3 ) i wygląda na to, że jest to najszybszy z nich.
Cobra,

@Cobra Ryzykując, że zabrzmi to małostkowo, działa na Chrome 40.0.2214 (najnowszy z 18.02.15), ta odpowiedź jest o 53% wolniejsza niż moja. OTOH IE11 wydaje się wcale nie zoptymalizowany pod kątem mojej odpowiedzi. :) Chrome mobile wciąż go rozwija. Szczerze mówiąc, jeśli używasz lodash / _, co większość z nas powinna, prawdziwa odpowiedź jest już dość wysoko na tej liście. :)
slickplaid

@ slickplaid Prawda, i to jest nieco szybsze, nawet w porównaniu do lodash / _ one. Prawdopodobnie w pewnym momencie skończę wdrażanie mojej implementacji na coś podobnego do twojego. : D
Cobra,

1
Nie jestem pewien, jakie są koszty metody indexOf (), ale jest to prawdopodobnie najszybsza metoda zgodna z ES5. Nie jest również nic warte, że zmienna długość argumentów nie jest potrzebna. Ta metoda jest łańcuchowa. @ slickplaid Ładowanie biblioteki nigdy nie jest odpowiedzią na pytanie „jak to zrobić w javascript”. z pewnością wiele bibliotek ma funkcję wykonania tego 7-liniowego zadania.
dehart

19

Wrzucam tylko dwa centy.

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

Jest to metoda, której często używam, wykorzystuje obiekt jako tabelę haszowania do sprawdzania duplikatów. Zakładając, że skrót to O (1), to działa on w O (n), gdzie n oznacza a. Długość + b. Długość. Szczerze mówiąc, nie mam pojęcia, w jaki sposób przeglądarka wykonuje skrót, ale działa dobrze na wielu tysiącach punktów danych.


Bardzo ładnie wykonane. Pokonuje całkiem (jeśli nie wszystkie) inne wyniki na tej stronie, wykorzystując tablicę asocjacyjną i utrzymując zapętlanie indexOf i innych operacji. jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid

Twój „skrót” to String()funkcja w javascript. Co może działać dla prymitywnych wartości (aczkolwiek w przypadku kolizji między typami), ale nie jest to dobre dopasowanie do tablic obiektów.
Bergi,

Korzystam z podobnego rozwiązania, zezwalam na przekazywanie funkcji hashCode lub przekazywanie łańcucha w celu zidentyfikowania właściwości w obiekcie, która ma zostać użyta jako klucz skrótu.
Robert Baker

19

Wystarczy omijać zagnieżdżone pętle (O (n ^ 2)) i .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);
}

2
To całkiem niesamowite, szczególnie jeśli robisz struny. Liczby potrzebowałyby dodatkowego kroku, aby je zachować. Ta funkcja znacznie przewyższa wszystkie inne opcje, jeśli nie przeszkadza ci (lub nie obchodzi), że wszystko jest ciągiem po zakończeniu. Dobra robota. Wyniki wydajności tutaj: jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid

18

Uproszczona najlepsza z tej odpowiedzi i zamieniła ją w przyjemną funkcję:

function mergeUnique(arr1, arr2){
    return arr1.concat(arr2.filter(function (item) {
        return arr1.indexOf(item) === -1;
    }));
}

2
Uważam, że jest to o wiele czystsze niż zaakceptowana odpowiedź. Wygląda również na to, że filtr jest obsługiwany w ECMAScript 5.1 +, który jest teraz dość obsługiwany.
Tom Fobear

To powinno być zaakceptowane
wallop

to jest o wiele bardziej zwięzłe.
Mox

15

Dlaczego nie używasz przedmiotu? Wygląda na to, że próbujesz wymodelować zestaw. Nie zachowa to jednak porządku.

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
}

Nie masz na myśli if (!set1.hasOwnProperty(key))?
Gumbo,

2
Dlaczego miałbym to na myśli? Celem tego warunku jest zignorowanie właściwości, które mogą znajdować się w prototypie obiektu.
Nick Retallack

12

Najlepszym rozwiązaniem...

Możesz sprawdzić bezpośrednio w konsoli przeglądarki, naciskając ...

Bez duplikatu

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

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

Z duplikatem

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

Jeśli chcesz bez duplikatu, możesz wypróbować lepsze rozwiązanie stąd - kod krzyku .

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

Wypróbuj konsolę przeglądarki Chrome

 f12 > console

Wynik:

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

[1, 2, 3, "prince"]

Nie usuwa duplikatów z tablicy wyjściowej.
Shahar

9

Mój półtora grosza:

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

Nie używa niczego nowego w ES6, czy coś przeoczyłem?
Bergi,

@Bergi: Tak, masz rację. Dziękuję za uwagę. W jakiś sposób bawiłem się tym skryptem i zapewne była jakaś wersja z funkcją ES6, ale teraz zawiera on indeksOf, który istnieje od wieków. Mój błąd, przepraszam.
Hero Qu

8

Możesz to osiągnąć po prostu używając Underscore.js's>> uniq :

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

console.log(array3)

Wydrukuje [„Vijendra”, „Singh”, „Shakya”] .



7

Wiem, że to pytanie nie dotyczy szeregu obiektów, ale tutaj szukają.

dlatego warto dodać przyszłym czytelnikom właściwy sposób łączenia, a następnie usuwania duplikatów

tablica obiektów :

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} ]

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
  }

Implementacja indexOfmetody dla innych przeglądarek pochodzi z MDC


1
Nie mogłem tego znaleźć w w3schools, dlatego to napisałem. w3schools.com/jsref/jsref_obj_array.asp Czy wymaga fromparametru btw?
Amarghosh

Dzięki @Gumbo i @meder - zmienię teraz moje zakładki. Jeszcze nie robię nic poważnego w js i używam w3schools do swobodnego odniesienia (to wszystko, czego kiedykolwiek potrzebowałem) - być może dlatego nie zdawałem sobie z tego sprawy.
Amarghosh

MDC twierdzi, że indexOf wymaga javascript 1.6 Czy bezpiecznie byłoby założyć, że popularne przeglądarki (> = FF2,> IE6 itp.) Będą go obsługiwać?
Amarghosh

4
IE6 nie obsługuje Array.prototype.indexOf, wystarczy wkleić metodę wsparcia podaną przez Mozillę, aby IE nie zgłaszał błędu.
meder omuraliev

zaktualizowane za pomocą indexOf. Wyczyściłem kod, usuwając skomentowaną część. @meder - jeszcze raz dziękuję.
Amarghosh,

6

Nowe rozwiązanie (które wykorzystuje Array.prototype.indexOfi 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 )
};

Stosowanie:

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

Array.prototype.indexOf (dla przeglądarki 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;
  };

@Mender: jeśli zamówienie nie ma znaczenia, to jak to zrobić
Vijjendra,

1
To nie jest standardowa metoda ECMAScript zdefiniowana dla Array.prototype, choć wiem, że możesz ją łatwo zdefiniować dla IE i innych przeglądarek, które jej nie obsługują.
meder omuraliev

Zauważ, że ten algorytm to O (n ^ 2).
Gumbo,

Jaki algorytm jest twoją odpowiedzią?
meder omuraliev

@meder: Mój algorytm jest algorytmem związkowym. Samo połączenie odbywa się w O (n + m), ale sortowanie zajmuje najwyżej O (n · log n + m · log m). Cały algorytm to O (n · log n + m · log m).
Gumbo,

6

Można to zrobić za pomocą Ustaw.

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>


6

Jest tak wiele rozwiązań łączenia dwóch tablic. Można je podzielić na dwie główne kategorie (z wyjątkiem korzystania z bibliotek stron trzecich, takich jak lodash lub underscore.js).

a) połącz dwie tablice i usuń zduplikowane elementy.

b) odfiltruj elementy przed ich połączeniem.

Połącz dwie tablice i usuń zduplikowane elementy

Łączenie

// 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

Zjednoczenie

Istnieje wiele sposobów ujednolicenia tablicy, osobiście sugeruję poniżej dwie metody.

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

Filtruj elementy przed ich połączeniem

Istnieje również wiele sposobów, ale osobiście sugeruję poniższy kod ze względu na jego prostotę.

const merged = array1.concat(array2.filter(secItem => !array1.includes(secItem)));

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]

5
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)

Zaletą tego jest wydajność i że ogólnie rzecz biorąc, pracując z tablicami, łączysz metody takie jak filtr, mapa itp., Więc możesz dodać tę linię, a ona połączy i deduplikuje tablicę2 z tablicą1 bez potrzeby odwoływania się do późniejszych jeden (gdy łączysz metody, których nie masz), przykład:

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

(Nie lubię zanieczyszczać prototypu Array.prototype i to byłby jedyny sposób na respektowanie łańcucha - zdefiniowanie nowej funkcji go zepsuje - więc myślę, że coś takiego jest jedynym sposobem na osiągnięcie tego)


4

Możesz spróbować:

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

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


3

Funkcjonalne podejście z ES2015

Zgodnie z podejściem funkcjonalnym a uniondwóch Arrays jest tylko kompozycją concati filter. Aby zapewnić optymalną wydajność, korzystamy z rodzimego Settypu danych, który jest zoptymalizowany do wyszukiwania właściwości.

W każdym razie kluczowym pytaniem w połączeniu z unionfunkcją jest sposób traktowania duplikatów. Możliwe są następujące permutacje:

Array A      + Array B

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

Dwie pierwsze kombinacje są łatwe w obsłudze dzięki jednej funkcji. Jednak dwa ostatnie są bardziej skomplikowane, ponieważ nie można ich przetwarzać, o ile polegają na Setwyszukiwaniu. Ponieważ przejście na zwykłe Objectwyszukiwanie starych właściwości pociągnęłoby za sobą poważny spadek wydajności, następująca implementacja ignoruje trzecią i czwartą permutację. Trzeba będzie zbudować osobną wersję uniondo ich obsługi.


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

Odtąd implementacja unionnfunkcji, która akceptuje dowolną liczbę tablic (zainspirowana komentarzami naomika), staje się trywialna :

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

Okazuje się, że unionnjest to po prostu foldl(aka Array.prototype.reduce), które przyjmuje unionza swój reduktor. Uwaga: Ponieważ implementacja nie korzysta z dodatkowego akumulatora, zgłosi błąd, gdy zastosujesz go bez argumentów.


1
kilka informacji zwrotnych: zauważyłem to flipi notfnie jestem używany. unionByPrzewiduj także szczegóły implementacji wycieków (wymaga niejawnej wiedzy o Settypie). Przydałoby się coś takiego: union = unionBy (apply)i unionci = unionBy (p => x => p(x.toLowerCase())). W ten sposób użytkownik po prostu wysyła dowolną wartość grupowania p- tylko pomysł ^ _ ^
Dziękuję

zsdeklaracja zmiennej również nie ma var/ letsłowo kluczowe
Dziękujemy

1
oto fragment kodu do wyjaśnienia [gist: unionBy.js ]
Dziękuję

@ anomik Po ponownym przemyśleniu mojego rozwiązania nie jestem już tak pewien, czy jest to właściwy sposób na przekazanie orzeczenia. Wszystko, co zyskujesz, to transformacja każdego elementu drugiej tablicy. Zastanawiam się, czy to podejście rozwiązuje więcej niż tylko problemy z zabawkami.

3

ze względu na to ... oto rozwiązanie z jedną linią:

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

Nie jest szczególnie czytelny, ale może pomóc komuś:

  1. Stosuje funkcję redukcji z początkową wartością akumulatora ustawioną na pustą tablicę.
  2. Funkcja zmniejszania używa concat do dołączania każdej pod-macierzy do tablicy akumulatorów.
  3. Wynik jest przekazywany jako parametr konstruktora, aby utworzyć nowy Set .
  4. Operator rozkładania służy do konwersji Settablicy na tablicę.
  5. sort()Funkcja jest stosowana do nowej tablicy.

2
Zamiast tego reduce()możesz użyćArray.from(set)
Eran Goldin

3

DeDuplicate single lub Scal i DeDuplicate wiele tablic wejściowych. Przykład poniżej.

using ES6 - Ustaw, dla destrukcji

Napisałem tę prostą funkcję, która przyjmuje wiele argumentów tablicowych. Robi się prawie tak samo jak powyższe rozwiązanie, ma tylko bardziej praktyczny przypadek użycia. Ta funkcja nie łączy zduplikowanych wartości tylko w jednej tablicy, dzięki czemu może je usunąć na późniejszym etapie.

DEFINICJA KRÓTKIEJ FUNKCJI (tylko 9 linii)

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

UŻYJ PRZYKŁADU 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]

Po co arr.maptu korzystać ? Używasz go jako foreach, ponieważ wynik jest ignorowany
Antony

Użyłem return Array.from (set.values ​​()); , ponieważ vscode daje błąd przy zwrocie [... zestaw];
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]

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.