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

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

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

Я хочу вывод:

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

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

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

18.10.2009 08:34:57
Прежде чем опубликовать новый ответ, учтите, что на этот вопрос уже есть более 75 ответов. Пожалуйста, убедитесь, что ваш ответ содержит информацию, которой нет среди существующих ответов.
janniks 3.02.2020 12:05:14
[... новый сет ([... [1, 2, 3], ... [2, 3, 4]])] результат [1, 2, 3, 4]
Denis Giffeler 30.03.2020 07:24:39
30 ОТВЕТОВ
РЕШЕНИЕ

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

Использование версии 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;
    }
});
1640
20.09.2019 01:08:57
Обратите внимание, что этот алгоритм O (n ^ 2).
Gumbo 18.10.2009 08:54:59
Позвольте [a, b, c]и [x, b, d]быть массивов (предположим, кавычки). Конкат дает [a, b, c, x, b, d]. Не будет ли уникальный () вывод [a, c, x, b, d]. Это не сохраняет порядок, я думаю - я считаю, что ОП хочет[a, b, c, x, d]
Amarghosh 18.10.2009 09:04:55
ОП принял первый ответ, который заставил его работать, и, похоже, подписал. Мы все еще сравниваем решения друг друга,
Amarghosh 18.10.2009 11:10:43
Я изначально проголосовал за это, но передумал. Присвоение прототипов Array.prototype приводит к нарушению операторов "for ... in". Таким образом, лучшее решение, вероятно, состоит в том, чтобы использовать такую ​​функцию, но не назначать ее в качестве прототипа. Некоторые люди могут утверждать, что операторы for ... in в любом случае не должны использоваться для итерации элементов массива, но люди часто используют их таким образом, поэтому по крайней мере это решение следует использовать с осторожностью.
Code Commander 2.02.2011 00:49:16
Вы должны всегда использовать for ... inс hasOwnPropertyв этом случае метод прототипа является штраф
mulllhausen 1.01.2013 12:17:05

Новое решение (которое использует 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;
  };
6
18.10.2009 11:10:45
@Mender: если порядок не имеет значения, то как я это делаю
Vijjendra 18.10.2009 08:46:54
Это не стандартный метод ECMAScript, определенный для Array.prototype, хотя я знаю, что вы можете легко определить его для IE и других браузеров, которые его не поддерживают.
meder omuraliev 18.10.2009 08:50:30
Обратите внимание, что этот алгоритм O (n ^ 2).
Gumbo 18.10.2009 08:55:38
Какой алгоритм ваш ответ?
meder omuraliev 18.10.2009 08:58:19
@meder: Мой алгоритм является алгоритмом объединения. Само объединение выполняется в O (n + m), но сортировка занимает не более O (n · log n + m · log m). Таким образом, весь алгоритм O (n · log n + m · log m).
Gumbo 18.10.2009 09:19:56
//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

6
18.10.2009 10:56:09
Я не мог найти его в w3schools, поэтому я написал это. w3schools.com/jsref/jsref_obj_array.asp Принимает ли он fromпараметр между прочим?
Amarghosh 18.10.2009 08:51:21
Спасибо @Gumbo и @meder - я собираюсь изменить мои закладки сейчас. Мне еще предстоит сделать что-то серьезное в js, и я использую w3schools для случайного ознакомления (это все, что мне когда-либо было нужно) - возможно, именно поэтому я этого не осознавал.
Amarghosh 18.10.2009 09:15:42
MDC говорит, что indexOf требует JavaScript 1.6. Можно ли предположить, что обычные браузеры (> = FF2,> IE6 и т. Д.) Будут его поддерживать?
Amarghosh 18.10.2009 09:19:48
IE6 не поддерживает Array.prototype.indexOf, просто вставьте метод поддержки, предоставленный Mozilla, чтобы IE не выдавал ошибку.
meder omuraliev 18.10.2009 09:37:53
обновлено с помощью indexOf. Очистил код, удалив закомментированную часть. @meder - еще раз спасибо.
Amarghosh 18.10.2009 09:49:49

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

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
}
15
18.10.2017 06:30:39
Ты имеешь в виду if (!set1.hasOwnProperty(key))?
Gumbo 18.10.2009 08:56:51
Почему я имел в виду это? Цель этого условия - игнорировать свойства, которые могут быть в прототипе объекта.
Nick Retallack 18.10.2009 19:13:01
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;
};

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

19
22.08.2011 18:44:21
var test = ['a', 'b', 'c']; console.log(test); будет печатать ["a", "b", "c", merge: function]
Doubidou 4.05.2014 11:18:36
Отличное решение. Я обновил тест jsperf, опубликованный выше @slickplaid ( jsperf.com/merge-two-arrays-keeping-only-unique-values/3 ), и похоже, что это самый быстрый из них.
Cobra 8.08.2014 16:14:43
@Cobra С риском звучать мелко, работает на Chrome 40.0.2214 (последний по состоянию на 18.02.15), этот ответ на 53% медленнее, чем мой. OTOH IE11, кажется, не оптимизирован для моего ответа вообще. :) Мобильный Chrome все еще качает это, все же. Честно говоря, если вы используете lodash / _, что следует большинству из нас, истинный ответ уже довольно высок в этом списке. :)
slickplaid 18.02.2015 14:14:05
@slickplaid Правда, и это немного быстрее, даже по сравнению с lodash / _. Я, вероятно, в конечном итоге переключу мою реализацию в тот или иной момент на что-то похожее на ваше. : D
Cobra 19.02.2015 16:52:29
Не уверен, какова стоимость метода indexOf (), но это, вероятно, самый быстрый ES5-совместимый метод. Также ничего не стоит, что переменная длина аргументов не нужна. Этот метод является цепным. @slickplaid Загрузка библиотеки никогда не является ответом на вопрос «как это сделать в javascript». конечно, во многих библиотеках есть функция для выполнения этой 7-строчной работы.
de hart 28.12.2018 12:40:58

В Додзе 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/

2
12.02.2012 13:30:21
Зачем использовать dojo только для функции forEach?
Lajos Meszaros 26.07.2013 11:19:20
Кроме того, вам не нужно объединять слишком много массивов. Просто переберите второй массив и добавьте их значения, если они не существуют в первом массиве.
Lajos Meszaros 26.07.2013 11:23:28
@ MészárosLajos Нет, я бы никогда не загрузил Dojo только для функции forEach. Я разместил это на тот случай, если кто-то уже использовал Dojo. Что касается оптимизации, это невозможно, если вы не знаете, что первый массив содержит уникальные значения.
Richard Ayotte 26.07.2013 16:08:14

С 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

593
20.09.2019 03:48:02
Или, возможно, даже лучше, чем подчеркивание, API-совместимый lodash .
Brian M. Hunt 9.02.2013 15:02:47
@Ygg Из документации по lodash. «Возвращает новый массив уникальных значений по порядку , которые присутствуют в одном или нескольких массивах».
Richard Ayotte 2.08.2013 00:42:14
Я предпочитаю underscore.js. То, что я в конечном итоге использовал, это то underscore.flatten(), что лучше, чем объединение в том, что он принимает массив массивов.
weaver 18.02.2014 23:25:19
@weaver _.flatten сливается, но не «дублирует».
GijsjanB 25.03.2014 11:06:56
Быстрый результат - это лучший ответ: lsash
slickplaid 4.08.2014 14:08:46

Просто добавляю два моих цента.

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

19
12.12.2012 19:50:20
Очень красиво сделано. Превышает (если не все) другие результаты на этой странице, используя ассоциативный массив и не допуская циклического выполнения indexOf и других операций. jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid 16.04.2015 17:29:48
Ваш "хэш" - это String()функция в javascript. Это может работать для примитивных значений (хотя и с коллизиями между типами), но не подходит для массивов объектов.
Bergi 19.02.2016 14:55:55
Я использую аналогичное решение, я разрешаю передавать функцию hashCode или строку, чтобы идентифицировать свойство в объекте для использования в качестве ключа хеша.
Robert Baker 22.03.2017 20:35:34

Возьмите два массива a и b

var a = ['a','b','c'];

var b = ['d','e','f'];
var c = a.concat(b); 


//c is now an an array with: ['a','b','c','d','e','f']
-4
21.03.2013 20:24:54
какие браузеры поддерживают concat?
chovy 22.12.2013 04:37:40
@chovy - concat - старый метод, из ECMAScript 3. У вас не должно возникнуть проблем с его использованием. Смотрите таблицу совместимости на MDN .
Ian Hunter 30.12.2013 22:54:37
Не соответствует критерию дублирования
hendrix 28.03.2014 13:57:27
Этот ответ был скопирован с stackoverflow.com/questions/3975170/…
xoxox 22.04.2016 20:02:44

Вот мое решение https://gist.github.com/4692150 с глубоким равным и простым в использовании результатом:

function merge_arrays(arr1,arr2)
{
   ... 
   return {first:firstPart,common:commonString,second:secondPart,full:finalString}; 
}

console.log(merge_arrays(
[
[1,"10:55"] ,
[2,"10:55"] ,
[3,"10:55"]
],[
[3,"10:55"] ,
[4,"10:55"] ,
[5,"10:55"]
]).second);

result:
[
[4,"10:55"] ,
[5,"10:55"]
]
0
1.02.2013 16:01:19
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
26.07.2013 11:38:21

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

Array.prototype.merge = function (arr) {
    var key;
    for(key in arr) 
        this[key] = arr[key];
};
-1
18.10.2017 06:33:11
Это не объединяет массивы - это просто перезаписывает одно на другое. Например,var f = [1]; f.merge([2]);
Ian Hunter 30.12.2013 23:20:23
Хммм ... но я думал, что ключи, которые уже там, остались, и вы хотите, чтобы новые и объединенный перезаписали? О, может быть, я думал словари или что-то.
MistereeDevlord 15.04.2015 13:08:12

Только что написал по той же причине (работает с любым количеством массивов):

/**
 * Returns with the union of the given arrays.
 *
 * @param Any amount of arrays to be united.
 * @returns {array} The union array.
 */
function uniteArrays()
{
    var union = [];
    for (var argumentIndex = 0; argumentIndex < arguments.length; argumentIndex++)
    {
        eachArgument = arguments[argumentIndex];
        if (typeof eachArgument !== 'array')
        {
            eachArray = eachArgument;
            for (var index = 0; index < eachArray.length; index++)
            {
                eachValue = eachArray[index];
                if (arrayHasValue(union, eachValue) == false)
                union.push(eachValue);
            }
        }
    }

    return union;
}    

function arrayHasValue(array, value)
{ return array.indexOf(value) != -1; }
0
5.01.2014 02:16:01

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

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]
2
20.03.2014 18:02:29

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

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]

284
20.09.2019 09:41:02
Оригинальное решение здесь имеет преимущество удаления дубликатов в каждом исходном массиве. Я думаю, это зависит от вашего контекста, который вы бы использовали.
theGecko 26.09.2015 21:17:26
Вы можете объединить разные для IE6-поддержки: c = Array.from (new Set (c));
Tobi G. 18.10.2016 22:37:13
Если я действительно хочу изменить, aчтобы добавить b, то будет ли лучше циклически проходить и использовать push? a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
awe 10.11.2016 10:49:08
Просто напоминание о текущем использовании браузера caniuse.com/usage-table для людей, озабоченных IE6.
pmrotule 21.11.2016 09:55:47
@ Андрей: Еще лучше: 1. var c = [...a, ...b.filter(o => !~a.indexOf(o))]; 2. var c = [...new Set([...a, ...b])];
7vujy0f0hy 8.04.2017 17:49:06

Это функция, которую я использую, когда мне нужно объединить (или вернуть объединение) двух массивов.

var union = function (a, b) {
  for (var i = 0; i < b.length; i++)
    if (a.indexOf(b[i]) === -1)
      a.push(b[i]);
  return a;
};

var a = [1, 2, 3, 'a', 'b', 'c'];
var b = [2, 3, 4, 'b', 'c', 'd'];

a = union(a, b);
//> [1, 2, 3, "a", "b", "c", 4, "d"]

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

var array3 = union(array1, array2);
//> ["Vijendra", "Singh", "Shakya"]
-1
22.04.2014 10:43:26
function set(a, b) {
  return a.concat(b).filter(function(x,i,c) { return c.indexOf(x) == i; });
}
-1
6.06.2014 02:15:53

Вот самый эффективный с точки зрения времени вычислений. Также он сохраняет первоначальный порядок элементов.

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

var a = [1,2,3];
var b = [5,4,3];
var c = a.concat(b.filter(function(i){
    return a.indexOf(i) == -1;
}));
console.log(c); // [1, 2, 3, 5, 4]

Вот немного улучшенная (более быстрая) версия с недостатком, что массивы не должны пропускать значения:

var i, c = a.slice(), ci = c.length;
for(i = 0; i < b.length; i++){
    if(c.indexOf(b[i]) == -1)
        c[ci++] = b[i];
}
1
18.10.2017 06:34:44
Я не понимаю, почему это должно быть более эффективным по времени, чем решение LiraNuna. Вы оба используете функцию concat, и для поиска уникальных элементов у вас обоих есть временная сложность O (n ^ 2). Ваше решение, однако, состоит из меньшего количества строк кода и легче для чтения.
Anton Gildebrand 12.06.2014 08:28:31
@AntonGildebrand Больше бесполезных итераций там. Вы можете проверить сравнение здесь (откройте консоль браузера, чтобы увидеть): jsbin.com/wabahuha/1/edit?js,output
AlexTR 12.06.2014 09:05:35

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

38
17.01.2019 23:35:01
В вашей методологии есть недостаток: вы помещаете создание массива 3 в фазу установки, в то время как эти затраты должны быть только частью оценки вашего пока основанного решения. При перемещении этой 1 строки ваше решение падает до скорости цикла на основе цикла. Я понимаю, что массив можно использовать повторно, но, возможно, другие алгоритмы также выиграют от того, что не нужно объявлять и инициализировать каждый необходимый строительный блок.
doldt 14.04.2015 14:19:16
Я согласен с вашей предпосылкой @doldt, но не согласен с вашими результатами. Существует фундаментальный недостаток дизайна с удалением записей на основе цикла, заключающийся в том, что вам необходимо перепроверить длину массива после удаления элементов, что приводит к более медленному времени выполнения. Цикл while, работающий в обратном направлении, не имеет этих эффектов. Вот пример с удалением как можно большего количества установочных переменных без чрезмерного изменения их исходного ответа: jsperf.com/merge-two-arrays-keeping-only-unique-values/19
slickplaid 15.04.2015 20:55:58
@slickplaid связанные тесты пусты, а следующая ревизия в jsperf висит в цикле while.
doldt 16.04.2015 06:58:51
@doldt Я ответил на ваши вопросы в своем ответе и добавил к нему также обновленный тест. Дайте мне знать, если вы согласны с этими результатами или нет. Кроме того, я добавил еще один лучший результат, используя ассоциативный массив.
slickplaid 16.04.2015 16:42:51
@slickplaid Спасибо за настройку расширенной страницы перфорации. Если я что-то упустил, функция whileLoopAlt2 не работает? Он создает новый массив, содержащий первый массив и второй массив (в обратном порядке). Чтобы избежать путаницы, я сделал еще одну ревизию, которая удаляет сломанную функцию. Я также добавил дополнительный пример: jsperf.com/merge-two-arrays-keeping-only-unique-values/22
Stephen S 5.06.2015 00:24:58

Это просто и может быть сделано в одну строку с помощью jQuery:

var arr1 = ['Vijendra', 'Singh'], arr2 =['Singh', 'Shakya'];

$.unique(arr1.concat(arr2))//one line

["Vijendra", "Singh", "Shakya"]
1
18.10.2017 06:38:33
Из документации jQuery «Обратите внимание, что это работает только с массивами элементов DOM, а не со строками или числами». ( api.jquery.com/jQuery.unique ). Метод устарел.
Melanie 25.05.2016 16:13:38

Это решение 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));

199
25.11.2019 16:59:20
Это должно быть добавлено к принятому ответу. Это решение намного более эффективно и намного более элегантно, чем то, что возможно в настоящее время, но это то, что мы неизбежно сможем сделать (и должны сделать, чтобы не отставать в этой области).
EmmaGamma 21.01.2015 23:08:54
Это совсем не то же самое, что вопрос ОП (кажется, что это скорее плоская карта, чем что-либо другое), но это голос против, потому что это круто.
jedd.ahyoung 12.02.2016 16:48:30
Трудно сказать, что это должен быть принятый ответ, так как вопрос задан в 2009 году. Но да, это не только более «производительно», но и «элегантно»
Cezar Augusto 21.09.2016 19:43:23
Array.fromможно использовать вместо оператора распространения: Array.from(new Set([].concat(...arr)))
Henry Blyth 16.02.2017 12:08:28
Это очень элегантно. К сожалению, Typescript пока не поддерживает это. stackoverflow.com/questions/33464504/…
Ben Carp 21.04.2019 17:26:20

Просто держитесь подальше от вложенных циклов (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);
}
19
20.02.2015 14:55:47
Это довольно удивительно, особенно если вы делаете струны. Числам потребуется дополнительный шаг, чтобы сохранить их как таковые. Эта функция без особого труда выбивает все остальные параметры, если вы не возражаете (или не заботитесь) о том, что после завершения все является строкой. Хорошая работа. Результаты производительности здесь: jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid 16.04.2015 17:38:38
Array.prototype.pushUnique = function(values)
{
    for (var i=0; i < values.length; i++)
        if (this.indexOf(values[i]) == -1)
            this.push(values[i]);
};

Пытаться:

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
array1.pushUnique(array2);
alert(array1.toString());  // Output: Vijendra,Singh,Shakya
0
18.04.2015 01:49:30

Вот простой пример:

var unique = function(array) {
    var unique = []
    for (var i = 0; i < array.length; i += 1) {
        if (unique.indexOf(array[i]) == -1) {
            unique.push(array[i])
        }
    }
    return unique
}

var uniqueList = unique(["AAPL", "MSFT"].concat(["MSFT", "BBEP", "GE"]));

Мы определяем unique(array)для удаления избыточных элементов и используем concatфункцию для объединения двух массивов.

1
18.10.2017 06:40:48

Если, как и мне, вам нужна поддержка старых браузеров, это работает с IE6 +

function es3Merge(a, b) {
    var hash = {},
        i = (a = a.slice(0)).length,
        e;

    while (i--) {
        hash[a[i]] = 1;
    }

    for (i = 0; i < b.length; i++) {
        hash[e = b[i]] || a.push(e);
    }

    return a;
};

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

0
4.06.2015 23:36:57

Я сталкивался с этим постом, пытаясь сделать то же самое, но я хотел попробовать что-то другое. Я только что сделал функцию ниже. У меня также была другая переменная, «CompareKeys» (массив ключей) для сравнения мелких объектов. Я собираюсь изменить его в будущем.

Во всяком случае, я не включил эту часть, потому что это не относится к вопросу. Я также поместил свой код в jsperf. Отредактировано: я исправил свою запись в jsperf. Моя функция получает около 99 тыс. Операций в секунду по сравнению с 140 тыс. Операций.

К коду: я сначала создаю массив доступных индексов, а затем устраняю их, перебирая первый массив. Наконец, я добавляю «остатки», используя усеченный массив индексов, которые не совпадают между двумя массивами.

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

function indiceMerge(a1, a2) {
    var ai = [];
    for (var x = 0; x < a2.length; x++) {
        ai.push(x)
    };

    for (var x = 0; x < a1.length; x++) {
        for (var y = 0; y < ai.length; y++) {
            if (a1[x] === a2[ai[y]]) {
                ai.splice(y, 1);
                y--;
            }
        }
    }

    for (var x = 0; x < ai.length; x++) {
        a1.push(a2[ai[x]]);
    }

    return a1;
}
-1
18.10.2017 06:42:00
Чем это отличается?
djechlin 10.01.2019 17:58:19
Я написал это с учетом производительности. Есть много способов сделать то, что хочет OP, но производительность обычно упускается из виду. Вы можете проверить ссылку для обновления, три года спустя. @djechlin
David Kirk 11.01.2019 19:53:20

Это мой второй ответ, но я считаю самым быстрым? Я хотел бы, чтобы кто-то проверил меня и ответил в комментариях.

Моя первая попытка набрала около 99 тыс. Операций в секунду, и это говорит о том, что 390 тыс. Операций в секунду против другого ведущего теста jsperf в 140 тыс. (Для меня).

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

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

function findMerge(a1, a2) {
    var len1 = a1.length;

    for (var x = 0; x < a2.length; x++) {
        var found = false;

        for (var y = 0; y < len1; y++) {
            if (a2[x] === a1[y]) {
                found = true;
                break;
            }
        }

        if(!found){
            a1.push(a2.splice(x--, 1)[0]);
        }
    }

    return a1;
}

Изменить: я сделал некоторые изменения в моей функции, и производительность по сравнению с другими на сайте jsperf значительно.

1
21.08.2015 02:34:20

Моя полторы копейки

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);
9
20.02.2016 22:29:23
Он не использует ничего нового в ES6, я что-то пропустил?
Bergi 19.02.2016 14:49:46
@ Берги: Да, вы правы. Спасибо, что заметили. Каким-то образом я играл с этим сценарием, и, возможно, была какая-то версия с функцией ES6, но теперь она содержит indexOf, который существует веками. Моя ошибка, прости.
Hero Qu 20.02.2016 15:18:20

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

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' ]
2
8.02.2016 23:27:58

Это быстро, сортирует любое количество массивов и работает как с числами, так и со строками.

function collate(a){ // Pass an array of arrays to collate into one array
    var h = { n: {}, s: {} };
    for (var i=0; i < a.length; i++) for (var j=0; j < a[i].length; j++)
        (typeof a[i][j] === "number" ? h.n[a[i][j]] = true : h.s[a[i][j]] = true);
    var b = Object.keys(h.n);
    for (var i=0; i< b.length; i++)
        b[i]=Number(b[i]);
    return b.concat(Object.keys(h.s));
}

> a = [ [1,2,3], [3,4,5], [1,5,6], ["spoon", "fork", "5"] ]
> collate( a )

[1, 2, 3, 4, 5, 6, "5", "spoon", "fork"]

Если вам не нужно различать 5 и 5, то

function collate(a){
    var h = {};
    for (i=0; i < a.length; i++) for (var j=0; j < a[i].length; j++)
        h[a[i][j]] = typeof a[i][j] === "number";
    for (i=0, b=Object.keys(h); i< b.length; i++)
        if (h[b[i]])
            b[i]=Number(b[i]);
    return b;
}
[1, 2, 3, 4, "5", 6, "spoon", "fork"]

Сделаю.

И если вы не возражаете (или предпочли бы), чтобы все значения в любом случае заканчивались строками, просто вот так:

function collate(a){
    var h = {};
    for (var i=0; i < a.length; i++)
        for (var j=0; j < a[i].length; j++)
            h[a[i][j]] = true;
    return Object.keys(h)
}
["1", "2", "3", "4", "5", "6", "spoon", "fork"]

Если вам на самом деле не нужен массив, но вы просто хотите собрать уникальные значения и перебрать их, то (в большинстве браузеров (и node.js)):

h = new Map();
for (i=0; i < a.length; i++)
    for (var j=0; j < a[i].length; j++)
        h.set(a[i][j]);

Это может быть предпочтительнее.

-1
18.10.2017 06:46:01
Огромное спасибо. В синхронных операциях jQuery это был тот вариант, который работал для меня для встроенных сгенерированных массивов.
Ruslan Abuzant 8.04.2016 11:30:01