Как объединить два массива в JavaScript и дедуплицировать элементы


1368

У меня есть два массива JavaScript:

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

Я хочу вывод:

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

В выходном массиве должны быть удалены повторяющиеся слова.

Как объединить два массива в JavaScript, чтобы я получал только уникальные элементы из каждого массива в том же порядке, в котором они были вставлены в исходные массивы?


1
Прежде чем опубликовать новый ответ, учтите, что на этот вопрос уже есть 75+ ответов. Пожалуйста, убедитесь, что ваш ответ содержит информацию, которой нет среди существующих ответов.
Янник

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

Если вы хотите более общее решение, которое также охватывает глубокое слияние, взгляните на этот вопрос . Некоторые ответы также охватывают массивы.
Мартин Браун

Ответы:


1667

Чтобы просто объединить массивы (без удаления дубликатов)

Использование версии 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).
Гамбо

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

89
ОП принял первый ответ, который заставил его работать, и, похоже, подписал. Мы все еще сравниваем решения друг друга,
находим и

6
Я изначально проголосовал за это, но передумал. Присвоение прототипов Array.prototype приводит к нарушению операторов "for ... in". Таким образом, лучшее решение, вероятно, состоит в том, чтобы использовать такую ​​функцию, но не назначать ее в качестве прототипа. Некоторые люди могут утверждать, что операторы for ... in в любом случае не должны использоваться для итерации элементов массива, но люди часто используют их таким образом, поэтому по крайней мере это решение следует использовать с осторожностью.
Code Commander

16
Вы должны всегда использовать for ... inс hasOwnPropertyв этом случае метод прототипа является штраф
mulllhausen

601

С 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-совместимый lodash .
Брайан М. Хант

3
@Ygg Из документации по lodash. Msgstr "Возвращает новый массив уникальных значений по порядку , которые присутствуют в одном или нескольких массивах."
Ричард Айотт

4
Я предпочитаю underscore.js. То, что я в конечном итоге использовал, это то underscore.flatten(), что лучше, чем объединение в том, что он принимает массив массивов.
ткач

8
@weaver _.flatten сливается, но не «дублирует».
GijsjanB

9
Быстрый результат - это лучший ответ: lsash
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 (new Set (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.

Но если вы используете Вавилон , вы можете получить его сейчас.

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
Это должно быть добавлено к принятому ответу. Это решение намного более эффективно и намного элегантнее, чем то, что возможно в настоящее время, но это то, что мы неизбежно сможем сделать (и должны сделать, чтобы не отставать в этой области).
ЭммаГамма

Это совсем не то же самое, что вопрос ОП (кажется, что это скорее плоская карта, чем что-либо еще), но повышенный голос, потому что это круто.
jedd.ahyoung

4
Трудно сказать, что это должен быть принятый ответ, так как вопрос задан в 2009 году. Но да, это не только более «производительно», но и «элегантно»
Цезарь Аугусто,

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-й пример - вообще нет union+ 1-й пример взрывает стек для больших Arrays + 3-й пример невероятно медленен и потребляет много памяти, так как Arrayнужно построить две промежуточные s + 3-й пример можно использовать только unionс известными число Arrays во время компиляции.

так как ты это сделаешь?
Дэвид Норена

14
Setэто путь сюда
Филк

3
Обратите внимание, что для set нельзя дедуплицировать два объекта с одинаковыми парами ключ-значение, если они не являются одинаковыми ссылками на объекты.
июнь 711

2
Не работает с массивом объектов, так как он объединяет только ссылки на объекты и не заботится о равенстве самих объектов.
Уилсон Биггс

87

Используя Set (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);
  }
}

цикл while: ~ 589 тыс. операций / с,
фильтр: ~ 445 тыс. операций / с,
lodash: 308 тыс. операций / с.
петли: 225 тыс. операций / с.

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

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 Стокера , если вы хотите , чтобы шаг на ступеньку выше в скорости. Это, безусловно, самый быстрый из всех результатов после прохождения почти всех жизнеспособных ответов.


В вашей методологии есть недостаток: вы помещаете создание массива 3 в фазу установки, в то время как эта стоимость должна быть только частью оценки вашего пока основанного решения. При перемещении этой 1 строки ваше решение падает до скорости цикла на основе цикла. Я понимаю, что массив можно использовать повторно, но, возможно, другие алгоритмы также выиграют от того, что не нужно объявлять и инициализировать каждый необходимый строительный блок.
Дольдт

Я согласен с вашей предпосылкой @doldt, но не согласен с вашими результатами. Существует фундаментальный недостаток дизайна с удалением записей на основе цикла, заключающийся в том, что вы должны перепроверить длину массива после удаления элементов, что приводит к более медленному времени выполнения. Цикл while, работающий в обратном направлении, не имеет этих эффектов. Вот пример с удалением как можно большего количества установочных переменных без чрезмерного изменения их исходного ответа: jsperf.com/merge-two-arrays-keeping-only-unique-values/19
slickplaid

@slickplaid связанные тесты пусты, а следующая ревизия в jsperf висит в цикле while.
Дольдт

@doldt Я ответил на ваши вопросы в своем ответе и добавил к нему также обновленный тест. Дайте мне знать, если вы согласны с этими результатами или нет. Кроме того, я добавил еще один лучший результат, используя ассоциативный массив.
Slickplaid

1
@slickplaid Спасибо за настройку расширенной страницы перфорации. Если я что-то упустил, функция whileLoopAlt2 не работает? Он создает новый массив, содержащий первый массив и второй массив (в обратном порядке). Чтобы избежать путаницы, я сделал еще одну ревизию, которая удаляет сломанную функцию. Я также добавил дополнительный пример: jsperf.com/merge-two-arrays-keeping-only-unique-values/22
Стивен С.

37

Вы можете сделать это просто с ECMAScript 6,

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
  • Используйте оператор распространения для объединения массива.
  • Используйте Set для создания отдельного набора элементов.
  • Снова используйте оператор распространения, чтобы преобразовать Set в массив.

2
Я получаю сообщение об ошибке: Тип «Set <string>» не является типом массива.
gattsbr

3
И если вы по каким - то причинам не хотите использовать оператор распространения, есть также: Array.from(new Set(array1.concat(array2))).
КБА

@gattsbr, с TypeScript 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]
Doubidou

Отличное решение. Я обновил тест jsperf, опубликованный выше @slickplaid ( jsperf.com/merge-two-arrays-keeping-only-unique-values/3 ), и похоже, что это самый быстрый из них.
Кобра

@Cobra Риск звучать мелко, работает на Chrome 40.0.2214 (последний по состоянию на 18.02.15), этот ответ на 53% медленнее, чем мой. OTOH IE11, кажется, не оптимизирован для моего ответа вообще. :) Мобильный Chrome все еще качает это, все же. Честно говоря, если вы используете 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;
}

Этот метод я часто использую, он использует объект в качестве таблицы hashlookup для проверки дубликатов. Предполагая, что хеш равен O (1), тогда он выполняется в O (n), где n равно a.length + b.length. Честно говоря, я понятия не имею, как браузер выполняет хэширование, но он хорошо работает на многих тысячах точек данных.


Очень красиво сделано. Побеждает довольно (если не все) другие результаты на этой странице, используя ассоциативный массив и не допуская зацикливания indexOf и других операций. jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid

Ваш "хэш" - это String()функция в javascript. Это может работать для примитивных значений (хотя и с коллизиями между типами), но не подходит для массивов объектов.
Берги

Я использую аналогичное решение, я разрешаю передавать функцию hashCode или строку, чтобы идентифицировать свойство в объекте для использования в качестве ключа хеша.
Роберт Бейкер

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 +, который сейчас довольно поддерживается.
Том Фобер

Это должно было быть принято
Wallop

это намного более кратко.
Мокс

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))?
Гамбо

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

Если вы хотите без дубликатов, вы можете попробовать лучшее решение здесь - кричащий код .

[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, я что-то пропустил?
Берги

@ Берги: Да, вы правы. Спасибо, что заметили. Каким-то образом я играл с этим сценарием, и, возможно, была какая-то версия с функцией ES6, но теперь она содержит indexOf, который существует веками. Моя ошибка, прости.
Герой Цюй

8

Вы можете добиться этого просто используя Underscore.js's => uniq :

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

console.log(array3)

На нем будут напечатаны ["Вижендра", "Сингх", "Шакья"] .


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параметр между прочим?
Амаргош

Спасибо @Gumbo и @meder - я собираюсь изменить мои закладки сейчас. Мне еще предстоит сделать что-то серьезное в js, и я использую w3schools для случайного ознакомления (это все, что мне когда-либо было нужно) - возможно, именно поэтому я этого не осознавал.
Амаргош

MDC говорит, что indexOf требует javascript 1.6. Можно ли предположить, что его поддерживают обычные браузеры (> = FF2,> IE6 и т. Д.)?
Амаргош

4
IE6 не поддерживает Array.prototype.indexOf, просто вставьте метод поддержки, предоставленный Mozilla, чтобы IE не выдавал ошибку.
Медер Омуралиев

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

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 (для интернет-обозревателя):

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: если порядок не имеет значения, то как я это делаю
Виджендра

1
Это не стандартный метод ECMAScript, определенный для Array.prototype, хотя я знаю, что вы можете легко определить его для IE и других браузеров, которые его не поддерживают.
Медер Омуралиев

Обратите внимание, что этот алгоритм O (n ^ 2).
Гамбо

Какой алгоритм ваш ответ?
Медер Омуралиев

@meder: Мой алгоритм является алгоритмом объединения. Само объединение выполняется в O (n + m), но сортировка занимает не более O (n · log n + m · log m). Таким образом, весь алгоритм O (n · log n + m · log m).
Гамбо

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)

Хорошая вещь об этом - производительность, и вы вообще, при работе с массивами, используете методы цепочки, такие как filter, map и т. Д., Так что вы можете добавить эту строку, и она будет объединять и дедуплицировать array2 с 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момента становится просто реализовать функцию, которая принимает любое количество массивов (вдохновлено комментариями Наомика):

// 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. Функция Reduce использует 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);
}

ПРИМЕР ИСПОЛЬЗОВАНИЯ 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]

Зачем использовать arr.mapздесь? Вы используете его как результат foreach, так как результат игнорируется
Antony

Я использовал return Array.from (set.values ​​()); , потому что vscode выдает ошибку для return [... set];
Маккаси


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.