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

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

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

Я хочу, чтобы выход должен быть:

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

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

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

30 ответов


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

в ES5 версия использовать Array.concat:

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

var array3 = array1.concat(array2); // Merges both arrays
// [ 'Vijendra', 'Singh', 'Singh', 'Shakya' ]

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

это также сохранит порядок массивы (i.e, сортировка не требуется).

так как многие люди раздражены прототипом увеличения 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;
    }
});

С Подчеркиванием.js или Lo-Dash вы можете сделать:

_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]

http://underscorejs.org/#union

http://lodash.com/docs#union


сначала объедините два массива, затем отфильтруйте только уникальные элементы.

var a = [1, 2, 3], b = [101, 2, 1, 10];
var c = a.concat(b);
var d = c.filter(function (item, pos) {return c.indexOf(item) == pos});

// d is [1,2,3,101,10]

http://jsfiddle.net/simo/98622/

редактировать

как предложил @Dmitry (см. Второй комментарий ниже) более эффективным решением было бы отфильтровать уникальные элементы в b перед объединением с a

var a = [1, 2, 3], b = [101, 2, 1, 10];
var c = a.concat(b.filter(function (item) {
    return a.indexOf(item) < 0;
}));

// d is [1,2,3,101,10]

Это решение ECMAScript 6 с использованием распространение оператор и множество дженериков.

В настоящее время он работает только с Firefox и, возможно, Internet Explorer Technical Preview.

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

// Input: [ [1, 2, 3], [101, 2, 1, 10], [2, 1] ]
// Output: [1, 2, 3, 101, 10]
function mergeDedupe(arr)
{
  return [...new Set([].concat(...arr))];
}

ES6

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

или

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

или

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

вот немного другой взгляд на цикл. С некоторыми оптимизациями в последней версии 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/21

var whileLoopAlt = function(array1, array2) {
    var array3 = [];
    var arr = array1.concat(array2);
    var len = arr.length;
    var assoc = {};

    while(len--) {
        var itm = arr[len];

        if(!assoc[itm]) { // Eliminate the indexOf call
            array3.unshift(itm);
            assoc[itm] = true;
        }
    }

    return array3;
};

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

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

проверить Майк и ответ Дэна Стокера если вы хотите увеличить его на ступеньку в скорости. Это, безусловно, самый быстрый из всех результатов после прохождения почти всех жизнеспособных ответов.


С помощью Set (ECMAScript 2015), это будет так просто:

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

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

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

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

гораздо лучшая функция слияния массивов.


просто бросаю свои два цента.

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.длина + b.длина. Я честно не знаю, как браузер делает хэш, но он хорошо работает на многих тысяч точек данных.


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

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
}

просто держитесь подальше от вложенных циклов (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);
}

мои полторы копейки:

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

упрощенный ответ Симо и превратил его в приятную функцию.

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

самое лучшее решение...

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

без дубликатов

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

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


Это можно сделать с помощью 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>

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]

вы можете достичь этого, просто используя подчеркивание.js=>uniq:

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

console.log(array3)

Он будет печатать ["Vijendra", "Сингха", "Шакья"].


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

хорошая вещь об этом-производительность, и что вы в целом, при работе с массивами, связываете методы, такие как фильтр, карта и т. д., Поэтому вы можете добавить эту строку, и она будет объединять и дедуплицировать 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.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"]

массив.прототип.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;
  };

В Додзе 1.6+

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

dojo.forEach(array3, function(item) {
    if (dojo.indexOf(unique, item) > -1) return;
    unique.push(item); 
});

обновление

посмотреть рабочий код.

http://jsfiddle.net/UAxJa/1/


объединить неограниченное количество массивов или массивов и сохранить его уникальным:

function flatMerge() {
    return Array.prototype.reduce.call(arguments, function (result, current) {
        if (!(current instanceof Array)) {
            if (result.indexOf(current) === -1) {
                result.push(current);
            }
        } else {
            current.forEach(function (value) {
                console.log(value);
                if (result.indexOf(value) === -1) {
                    result.push(value);
                }
            });
        }
        return result;
    }, []);
}

flatMerge([1,2,3], 4, 4, [3, 2, 1, 5], [7, 6, 8, 9], 5, [4], 2, [3, 2, 5]);
// [1, 2, 3, 4, 5, 7, 6, 8, 9]

flatMerge([1,2,3], [3, 2, 1, 5], [7, 6, 8, 9]);
// [1, 2, 3, 5, 7, 6, 8, 9]

flatMerge(1, 3, 5, 7);
// [1, 3, 5, 7]

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

function arrayMerge(base, addendum){
    var out = [].concat(base);
    for(var i=0,len=addendum.length;i<len;i++){
        if(base.indexOf(addendum[i])<0){
            out.push(addendum[i]);
        }
    }
    return out;
}

использование:

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

console.log(array3);
//-> [ 'Vijendra', 'Singh', 'Shakya' ]

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


самый простой способ сделать это, либо использовать concat() объединить массивы, а затем использовать filter() удалить дубликаты, или использовать concat() а затем поместите объединенный массив внутри Set().

Первый способ:

const firstArray = [1,2, 2];
const secondArray = [3,4];
// now lets merge them
const mergedArray = firstArray.concat(secondArray); // [1,2,2,3,4]
//now use filter to remove dups
const removeDuplicates = mergedArray.filter((elem, index) =>  mergedArray.indexOf(elem) === index); // [1,2,3, 4]

Второй способ (но с последствиями для производительности пользовательского интерфейса):

const firstArray = [1,2, 2];
const secondArray = [3,4];
// now lets merge them
const mergedArray = firstArray.concat(secondArray); // [1,2,2,3,4]
const removeDuplicates = new Set(mergedArray);

похоже, что принятый ответ является самым медленным в моих тестах;

Примечание я объединяю 2 массива объектов по ключ

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<button type='button' onclick='doit()'>do it</button>
<script>
function doit(){
    var items = [];
    var items2 = [];
    var itemskeys = {};
    for(var i = 0; i < 10000; i++){
        items.push({K:i, C:"123"});
        itemskeys[i] = i;
    }

    for(var i = 9000; i < 11000; i++){
        items2.push({K:i, C:"123"});
    }

    console.time('merge');
    var res = items.slice(0);

    //method1();
    method0();
    //method2();

    console.log(res.length);
    console.timeEnd('merge');

    function method0(){
        for(var i = 0; i < items2.length; i++){
            var isok = 1;
            var k = items2[i].K;
            if(itemskeys[k] == null){
                itemskeys[i] = res.length;
                res.push(items2[i]);
            }
        }
    }

    function method1(){
        for(var i = 0; i < items2.length; i++){
            var isok = 1;
            var k = items2[i].K;

            for(var j = 0; j < items.length; j++){
                if(items[j].K == k){
                    isok = 0;
                    break;
                }
            }

            if(isok) res.push(items2[i]);
        }  
    }

    function method2(){
        res = res.concat(items2);
        for(var i = 0; i < res.length; ++i) {
            for(var j = i+1; j < res.length; ++j) {
                if(res[i].K === res[j].K)
                    res.splice(j--, 1);
            }
        }
    }
}
</script>
</body>
</html>

ради этого... вот однострочное решение:

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

не особо читабельны, но это может помочь кому-то:

  1. применяет функцию reduce с начальным значением аккумулятора, установленным в пустой массив.
  2. функция reduce использует concat для добавления каждого суб-массива в массив аккумулятора.
  3. результат этого передается в качестве параметра конструктора для создания нового Set.
  4. используется оператор spread чтобы преобразовать Set в массив.
  5. на sort() функция применяется к новому массиву.

дедуплицировать один или объединить и дедуплицировать несколько входов массива. Пример ниже.

использование ES6-Set, для разрушения

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

короткое определение функции ( только 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]

объединить два массива и удалить дубликат в es6

let arr1 = [3, 5, 2, 2, 5, 5];
let arr2 = [2, 1, 66, 5];
let unique = [...new Set([...arr1,...arr2])];
console.log(unique);
// [ 3, 5, 2, 1, 66 ]