Як об'єднати два масиви в JavaScript і дедублювати елементи


1364

У мене є два масиви JavaScript:

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

Я хочу, щоб результат був:

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

Вихідний масив повинен мати видалені повторні слова.

Як злити два масиви в JavaScript, щоб я отримав лише унікальні елементи з кожного масиву в тому ж порядку, коли вони були вставлені в початковий масив?


1
Перш ніж опублікувати нову відповідь, подумайте, що на це питання вже є 75+ відповідей. Будь ласка, переконайтеся, що ваша відповідь вносить інформацію, яка не входить до наявних відповідей.
janniks

[... новий набір ([... [1, 2, 3], ... [2, 3, 4]])] результат [1, 2, 3, 4]
Денис Гіффелер

Якщо ви хочете більш загальне рішення, яке також охоплює глибоке злиття, погляньте на це питання замість цього. Деякі відповіді охоплюють також масиви.
Мартін Браун

Відповіді:


1661

Просто об'єднати масиви (не видаляючи дублікатів)

Використання версії ES5 Array.concat:

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

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

Версія ES6 використовує руйнування

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

Оскільки не існує "вбудованого" способу видалення дублікатів ( ECMA-262 насправді є, Array.forEachщо було б чудово для цього), ми повинні робити це вручну:

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

Потім, щоб використовувати його:

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

Це також збереже порядок масивів (тобто сортування не потрібно).

Оскільки багато людей дратуються з приводу збільшення прототипу Array.prototypeта for inциклів, ось менш інвазивний спосіб його використання:

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

Для тих, кому пощастило працювати з браузерами, де доступний ES5, ви можете користуватися Object.definePropertyтаким чином:

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
Зауважимо, що цей алгоритм є O (n ^ 2).
Gumbo

7
Нехай [a, b, c]і [x, b, d]бути масивами (припустимо цитати). concat дає [a, b, c, x, b, d]. Не був би вихід унікального () [a, c, x, b, d]. Це не зберігає порядок, я думаю - я вважаю, що ОП хоче[a, b, c, x, d]
Amarghosh

89
ОП прийняв першу відповідь, яка змусила його працювати, і підписався, схоже. Ми все ще порівнюємо рішення один одного, знаходимо виправлення несправностей, вдосконалюємо продуктивність, переконуючись у його сумісності скрізь і так далі ... Краса
stackoverflow

6
Я спочатку проголосував за це, але передумав. Присвоєння прототипів Array.prototype має наслідки розбиття операторів "for ... in". Тож найкращим рішенням є, мабуть, використання такої функції, але не призначити її як прототип. Деякі люди можуть стверджувати, що твердження "за ... в" все одно не повинні використовуватися для ітерації елементів масиву, але люди часто використовують їх таким чином, принаймні, це рішення слід використовувати обережно.
Командир коду

16
Ви повинні завжди використовувати for ... inз hasOwnPropertyв цьому випадку метод прототипу є штраф
mulllhausen

600

За допомогою Underscore.js або Lo-Dash ви можете:

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
Або, можливо, навіть краще, ніж підкреслити, суміжний з API судаш .
Брайан М. Хант

3
@Ygg Із лодашських док. "Повертає новий масив унікальних значень для того , які є в одному або декількох масивах."
Річард Айотт

4
Я віддаю перевагу underscore.js. Що я в кінцевому підсумку використав - це underscore.flatten(), що краще, ніж об’єднання, оскільки воно займає масив масивів.
ткач

8
@weaver _.flatten зливається, але не "повторюється".
GijsjanB

9
Швидка продуктивність перейдіть на lodash vs головну відповідь: jsperf.com/merge-two-arrays-keeping-only-unique-values
slickplaid

288

Спочатку об'єднайте два масиви, після чого відфільтруйте лише унікальні елементи:

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]

Редагувати

Як було запропоновано, більш ефективним рішенням було б відфільтрувати унікальні елементи, bперш ніж спілкуватися з 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
Оригінальне рішення тут має перевагу при видаленні путівки в межах кожного вихідного масиву. Я думаю, це залежить від вашого контексту, який ви б використовували.
theGecko

Ви можете об'єднати різні для IE6-підтримки: c = Array.from (новий комплект (c));
Тобі Г.

Якщо я хочу насправді змінити, aщоб додати b, чи буде тоді краще прокручувати та використовувати push? a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
трепет

1
Лише нагадування про поточне використання браузера caniuse.com/usage-table для людей, які хвилюються щодо IE6.
pmrotule

10
@Andrew: Ще краще: 1. var c = [...a, ...b.filter(o => !~a.indexOf(o))]; 2. var c = [...new Set([...a, ...b])];
7vujy0f0hy

203

Це рішення ECMAScript 6, що використовує дженерики оператора розширення та масиву.

Наразі він працює лише з Firefox та, можливо, з Internet Explorer Technical Preview.

Але якщо ви використовуєте Babel , ви можете мати її зараз.

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
Це слід додати до прийнятої відповіді. Це рішення набагато ефективніше та набагато елегантніше, ніж зараз можливе, але це ми неминуче зможемо зробити (і мусимо зробити, щоб не відставати у цій галузі).
EmmaGamma

Це не так, як питання щодо ОП (це, здається, є більш простою картою, ніж усе), але підсумкове, тому що це приголомшливо.
jedd.ahyoung

4
Важко сказати, що це має бути прийнята відповідь, оскільки це питання з 2009 року. Але так, це не тільки більш "виконавство", але й "елегантно"
Cezar Augusto

11
Array.fromможна використовувати замість оператора розповсюдження: Array.from(new Set([].concat(...arr)))
Генрі Блайт

1
Це дуже елегантно. На жаль, Typescript це ще не підтримує. stackoverflow.com/questions/33464504/…
Бен Карп

190

ES6

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

АБО

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

АБО

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

1
1-й / 2-й приклад взагалі ні, union1-й приклад підірває стек для великих Arrays + 3-й приклад неймовірно повільний і вимагає багато пам'яті, оскільки два проміжних Arrays повинні бути побудовані + 3-й приклад можна використовувати лише для unionвідомого кількість Arrays під час компіляції.

так як би ви це зробили?
Девід Норенья

14
Setце шлях поїхати сюди
Філк

3
Зауважте, що для множини не можна виводити два об'єкти, які мають однакові пари ключових значень, якщо вони не є однаковими посиланнями на об'єкти.
червня 1818 року

2
Не працює з масивом об'єктів, оскільки він об'єднує лише посилання на об'єкти і не хвилює, чи самі об'єкти рівні.
Вілсон Біггс

87

Використовуючи набір (ECMAScript 2015), це буде так само просто:

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


7
Я вважаю це "Прийнятим відповіддю" для використання ES6.
mwieczorek

9
@mwieczorek Як щодо:const array3 = [...new Set(array1.concat(array2))]
Робі Корнеліссен

5
Він не працює, якщо ви використовуєте масив об’єктів
carkod

1
для об'єднання різних об’єктів без дублювання:
stackoverflow.com/a/54134237/3131433

38

Ось дещо інший прийом петлі. З деякими оптимізаціями в останній версії Chrome це найшвидший метод вирішення об'єднання двох масивів (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);
  }
}

цикл: ~ ​​589k ops / s
фільтр: ~ 445k ops / s
lodash: 308k ops / s
для циклів: 225k ops / s

У коментарі вказувалося, що одна з моїх змінних параметрів установки мою петлю витягувала попереду решти, оскільки не потрібно було ініціалізувати порожній масив для запису. Я погоджуюся з цим, тому я переписав тест навіть на ігрове поле і включив ще швидший варіант.

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

У цьому альтернативному рішенні я поєднав асоціативне рішення масиву однієї відповіді, щоб усунути .indexOf()виклик у циклі, який значно уповільнював роботу другим циклом, і включив деякі інші оптимізації, які запропонували інші користувачі у своїх відповідях. .

Верхня відповідь тут з подвійним циклом на кожне значення (i-1) все ще значно повільніше. lodash все ще працює, і я все-таки рекомендую це всім, хто не проти додати бібліотеку до свого проекту. Для тих, хто цього не хоче, мій цикл while все ще є гарною відповіддю, і відповідь фільтра має тут дуже сильний показ, що вибиває всіх на моїх тестах з останньою версією Canary Chrome (44.0.2360) станом на цей текст.

Перевірте відповідь Майка і відповідь Dan Стокера , якщо ви хочете , щоб крок на сходинку вище в швидкості. Це, безумовно, найшвидший з усіх результатів, пройшовши майже всі життєздатні відповіді.


У вашій методології є вада: ви ставите створення array3 у фазу налаштування, при цьому ці витрати повинні бути лише частиною балу вашого рішення на базі часу. З цим переміщенням на 1 рядок ваше рішення падає до швидкості на основі циклу for. Я розумію, що масив може бути використаний повторно, але, можливо, інші алгоритми можуть також отримати користь від того, що не потрібно оголошувати та ініціалізувати кожен необхідний будівельний блок.
doldt

Я погоджуюсь з вашою умовою @doldt, але не згоден з вашими результатами. Існує фундаментальний недолік дизайну з вилученням даних, заснованих на циклі, у тому, що вам доведеться повторно перевірити довжину масиву після видалення елементів, що призводить до уповільнення часу виконання. Хоча цикл, який працює назад, не має цих ефектів. Ось приклад видалення стільки змінних параметрів, скільки я можу, не надто сильно змінюючи оригінальну відповідь: jsperf.com/merge-two-arrays-keeping-only-unique-values/19
slickplaid

@slickplaid пов'язані тести порожні, і наступна редакція на jsperf висить у циклі while.
doldt

@doldt Я відповів на ваші проблеми у своїй відповіді та додав до нього належний оновлений тест. Повідомте мене, чи згодні ви з цими результатами чи ні. Також я додав ще один кращий результат, використовуючи асоціативний масив.
slickplaid

1
@slickplaid Дякуємо за налаштування розширеної сторінки парф. Якщо я щось не пропускаю, функція "whileLoopAlt2" не працює? Він створює новий масив, що містить перший масив і другий масив (у зворотному порядку). Щоб уникнути плутанини, я зробив ще одну редакцію, яка видаляє порушену функцію. Я також додав додатковий приклад: jsperf.com/merge-two-arrays-keeping-only-unique-values/22
Stephen S

37

Ви можете це зробити просто за допомогою ECMAScript 6,

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
  • Використовуйте оператор розповсюдження для об'єднання масиву.
  • Використовуйте Set для створення виразного набору елементів.
  • Знову використовуйте оператор розповсюдження для перетворення набору в масив.

2
Я отримую помилку: Тип "Set <string>" - це не тип масиву.
gattsbr

3
І якщо ви з якихось - то причин не хочете використовувати оператор поширення, є також: Array.from(new Set(array1.concat(array2))).
kba

@gattsbr з машинописом в tsconfig.json, ви можете додати "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;
};

Набагато краща функція злиття масиву.


4
var test = ['a', 'b', 'c']; console.log(test); надрукує ["a", "b", "c", merge: function]
Дубіду

Відмінне рішення. Я оновив тест jsperf, розміщений вище @slickplaid ( jsperf.com/merge-two-arrays-keeping-only-unique-values/3 ), і схоже, що це найшвидший з них.
Кобра

@Cobra Загрожує прозвучати дрібно, працює на Chrome 40.0.2214 (Останнє станом на 18.02.15), ця відповідь на 53% повільніше, ніж у мене. OTOH IE11, схоже, зовсім не оптимізований для моєї відповіді. :) Chrome mobile все ще гойдає. Чесно кажучи, якщо ви використовуєте lodash / _, що мусить більшість із нас, то справжня відповідь у цьому списку вже досить висока. :)
slickplaid

@slickplaid Правда, і це зовсім трохи швидше, навіть порівняно з lodash / _. Я, мабуть, переключу мою реалізацію в той чи інший момент на щось подібне до вашого. : D
Кобра

1
Не знаєте, які коштують метод indexOf (), але це, мабуть, найшвидший метод, сумісний з ES5. Також нічого не варто, що змінна довжина аргументів не потрібна. Цей метод є можливим. @slickplaid Завантаження бібліотеки ніколи не є відповіддю на питання "як це зробити в javascript". безумовно, багато бібліотек мають функцію, щоб виконати цю роботу в 7 рядках.
дегарт

19

Просто кинув у мене два центи.

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

Це метод, який я багато використовую, він використовує об'єкт як таблицю хешлокупінгу, щоб зробити перевірку дублікатів. Якщо припустити, що хеш - O (1), то це працює в O (n), де n - a.length + b.length. Я, чесно кажучи, поняття не маю, як браузер робить хеш, але він працює на багатьох тисячах точок даних.


Дуже красиво зроблено. Вибиває цілком (якщо не всі) інші результати на цій сторінці, використовуючи асоціативний масив і не допускаючи циклічного введення indexOf та інших операцій. jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid

Ваш "хеш" - це String()функція в JavaScript. Що може працювати для примітивних значень (хоч і зіткнення між типами), але це не дуже підходить для масивів об'єктів.
Бергі

Я використовую подібне рішення, я дозволяю передавати функцію хеш-коду або передавати рядок для ідентифікації властивості в об'єкті для використання як хеш-ключ.
Роберт Бейкер

19

Просто тримайтеся подалі від вкладених циклів (O (n ^ 2)) та .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
Це досить дивовижно, особливо якщо ти робиш струни. Числам знадобиться додатковий крок, щоб зберегти їх як такі. Ця функція геть виганяє всі інші параметри, якщо ви не заперечуєте (або не піклуєтесь про те, що все закінчується після завершення). Хороша робота. Результати виступу тут: jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid

18

Спростив найкращу відповідь і перетворив її на приємну функцію:

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

2
Я вважаю, що це набагато чіткіше, ніж прийнята відповідь. Крім того, схоже, що фільтр підтримується в ECMAScript 5.1 +, який зараз досить підтримується.
Том Фобер

Це слід було прийняти
додайте

це набагато більш лаконічно.
Мокс

15

Чому ви не використовуєте об'єкт? Схоже, ви намагаєтеся моделювати набір. Однак це не збереже замовлення.

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
}

Ви не маєте на увазі if (!set1.hasOwnProperty(key))?
Gumbo

2
Чому я це мав на увазі? Мета цієї умови - ігнорувати властивості, які можуть бути в прототипі об'єкта.
Нік Реталак

12

Найкраще рішення ...

Ви можете перевірити безпосередньо в консолі браузера, натиснувши ...

Без дублікату

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

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

З дублікатом

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

Якщо ви хочете без дублікатів, ви можете спробувати краще рішення тут - Shouting Code .

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

Спробуйте на консолі браузера Chrome

 f12 > console

Вихід:

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

[1, 2, 3, "prince"]

Він не видаляє дублікати з вихідного масиву.
Шахар

9

Моя півтора копійки:

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

Він не використовує нічого нового в ES6, я щось пропустив?
Бергі

@Bergi: Так, ти маєш рацію. Дякую за те, що помітили. Я якось грав із цим сценарієм і, ймовірно, була якась версія з функцією ES6, але зараз він містить indexOf, який існує століттями. Моя помилка, вибачте.
Hero Qu

8

Домогтися цього можна просто за допомогою Underscore.js's => uniq :

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

console.log(array3)

Він надрукує ["Vijendra", "Singh", "Shakya"] .


7

Для ES6 лише один рядок:

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

7

Я знаю, що це питання не стосується масиву об’єктів, але шукачі все-таки закінчуються.

тому варто додати майбутнім читачам правильний спосіб з’єднання ES6 та видалення дублікатів

масив об'єктів :

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
  }

Реалізація indexOfметоду для інших браузерів взята з MDC


1
Я не зміг його знайти в w3schools, тому я написав це. w3schools.com/jsref/jsref_obj_array.asp Це приймає fromпараметр btw?
Amarghosh

Дякую @Gumbo та @meder - зараз зміню свої закладки. Мені поки що робити що-небудь серйозне в js, і я використовую w3schools для випадкових довідок (це все, що мені колись було потрібно) - можливо, тому я цього не усвідомлював.
Amarghosh

MDC каже, що indexOf вимагає javascript 1.6 Чи можна було б припустити, що загальні браузери (> = FF2,> IE6 тощо) підтримуватимуть його?
Amarghosh

4
IE6 не підтримує Array.prototype.indexOf, просто вставте метод підтримки, наданий Mozilla, щоб IE не видав помилку.
meder omuraliev

оновлено за допомогою indexOf. Очистив код, видаливши коментовану частину. @meder - ще раз дякую.
Amarghosh

6

Нове рішення (яке використовує Array.prototype.indexOfта 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 )
};

Використання:

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

Array.prototype.indexOf (для 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: якщо замовлення не має значення, то як я це роблю
Vijjendra

1
Це не стандартний метод ECMAScript, визначений для Array.prototype, хоча я знаю, ви можете легко визначити його для IE та інших браузерів, які не підтримують його.
meder omuraliev

Зауважимо, що цей алгоритм є O (n ^ 2).
Gumbo

Який алгоритм - ваша відповідь?
meder omuraliev

@meder: Мій алгоритм є алгоритмом об'єднання. Сам об'єднання робиться в O (n + m), але сортування займає максимум O (n · log n + m · log m). Отже весь алгоритм - O (n · log n + m · log m).
Gumbo

6

Це можна зробити за допомогою 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>


6

Існує так багато рішень для об'єднання двох масивів. Їх можна розділити на дві основні категорії (за винятком використання сторонніх бібліотек, таких як lodash або underscore.js).

а) комбінуйте два масиви та видаліть повторювані елементи.

б) відфільтруйте предмети перед їх комбінуванням.

Поєднайте два масиви та вийміть повторювані елементи

Поєднання

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

Об'єднуючі

Існує багато способів об'єднання масиву, я особисто пропоную нижче два способи.

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

Фільтруйте елементи, перш ніж поєднувати їх

Існує також багато способів, але я особисто пропоную наведений нижче код через його простоту.

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)

Приємно в цьому - це продуктивність і те, що ви, загалом, працюючи з масивами, прив'язуєте такі способи, як фільтр, карта і т. Д., Щоб ви могли додати цей рядок, і він буде стискати і дедупілювати масив2 з array1, не потребуючи посилання на пізніші один (коли ви пов'язані методами, яких у вас немає), наприклад:

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

(Мені не подобається забруднювати Array.prototype, і це був би єдиний спосіб поваги ланцюга - визначення нової функції розірве її - тому я думаю, що щось подібне - це єдиний спосіб досягти цього)



3

Функціональний підхід з ES2015

Дотримуючись функціонального підходу a unionз двох Arrays - це лише склад concatі filter. Для забезпечення оптимальної продуктивності ми вдаємося до нативного Setтипу даних, який оптимізований для пошуку властивостей.

У будь-якому разі ключовим питанням у поєднанні з unionфункцією є те, як поводитися з дублікатами. Можливі такі перестановки:

Array A      + Array B

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

Перші дві перестановки легко обробляти за допомогою однієї функції. Однак останні два складніші, оскільки ви не можете їх обробляти до тих пір, поки покладаєтесь на Setпошук. Оскільки перехід на звичайні старі Objectпошуку ресурсів спричинить за собою серйозну ефективність, наступна реалізація просто ігнорує третю та четверту перестановку. Вам потрібно буде створити окрему версію unionдля їх підтримки.


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

З цього unionnмоменту стає тривіально реалізовувати функцію, яка приймає будь-яку кількість масивів (надихнувши коментарями 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) );

Виявляється, unionnце просто foldl(ака Array.prototype.reduce), яке приймає unionза своє відновлення. Примітка. Оскільки реалізація не використовує додатковий акумулятор, вона видасть помилку, коли ви застосуєте її без аргументів.


1
пара зворотних зв'язків: я помітив , що flipі notfне використовуються. Також unionByпередбачають деталі реалізації витоків (вимагає неявних знань про Setтип). Це може бути приємно, якби ви могли зробити щось подібне: union = unionBy (apply)і unionci = unionBy (p => x => p(x.toLowerCase())). Таким чином користувач просто надсилає будь-яке значення групування p- просто ідея ^ _ ^
Дякую

zsмінливої ​​декларації також бракує var/ letключове слово
Дякую

1
ось фрагмент коду для уточнення [gist: unionBy.js ]
Дякую

@naomik Після деякого переосмислення мого рішення я вже не впевнений, чи це правильний спосіб передавати присудок. Все, що ви отримуєте, - це перетворення кожного елемента другого масиву. Цікаво, чи вирішує такий підхід більше, ніж просто іграшкові проблеми.

3

заради нього ... ось єдине рядкове рішення:

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

Не читабельно, але це може допомогти комусь:

  1. Застосовує функцію зменшення з початковим значенням акумулятора, встановленим до порожнього масиву.
  2. Функція зменшення використовує concat для додавання кожного підмасиву до масиву акумуляторів.
  3. Результат цього передається як параметр конструктора для створення нового Set.
  4. Оператор розповсюдження використовується для перетворення Setмасиву в масив.
  5. sort()Функція застосовується в новий масив.

2
Також замість reduce()вас можна використовуватиArray.from(set)
Еран Голдін

3

Видалення дублювання одиночних або об'єднань та дедублювання декількох входів масиву. Приклад нижче.

за допомогою ES6 - встановлення для руйнування

Я написав цю просту функцію, яка приймає кілька аргументів масиву. Це майже те саме, що рішення над ним просто має більш практичний випадок використання. Ця функція не об'єднує дублюючі значення лише в один масив, щоб він міг їх видалити на більш пізньому етапі.

КОРОТКЕ ВИЗНАЧЕННЯ ФУНКЦІЇ (лише 9 рядків)

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

ВИКОРИСТАННЯ ПРИКЛАДУ КОДЕПЕН :

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

Навіщо використовувати arr.mapтут? Ви використовуєте це як foreach, оскільки результат ігнорується
Ентоні

Я використав return Array.from (set.values ​​()); , тому що vscode дає помилку для повернення [... set];
makkasi


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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.