Удалить повторяющиеся значения из массива JS

У меня очень простой массив JavaScript, который может содержать или не содержать дубликаты.

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

Мне нужно удалить дубликаты и поместить уникальные значения в новый массив.

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

Аналогичный вопрос:

+1194
источник поделиться
56 ответов
  • 1
  • 2

Быстрое и грязное использование jQuery:

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
$.each(names, function(i, el){
    if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});
+425
источник

TL; DR

Использование Set конструктора и распространяемого синтаксиса:

uniq = [...new Set(array)];

"Умный", но наивный способ

uniqueArray = a.filter(function(item, pos) {
    return a.indexOf(item) == pos;
})

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

Используя третий ("этот массив") параметр обратного вызова фильтра, мы можем избежать закрытия переменной массива:

uniqueArray = a.filter(function(item, pos, self) {
    return self.indexOf(item) == pos;
})

Несмотря на краткость, этот алгоритм не особенно эффективен для больших массивов (квадратичное время).

Hashtables на помощь

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

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

  • поскольку хеш-ключи могут быть только строками в JavaScript, этот код не различает числа и "числовые строки". То есть uniq([1,"1"]) вернется только [1]
  • по той же причине все объекты будут считаться равными: uniq([{foo:1},{foo:2}]) вернет только [{foo:1}].

Тем не менее, если ваши массивы содержат только примитивы и вам не нужны типы (например, это всегда числа), это решение является оптимальным.

Лучшее из двух миров

Универсальное решение сочетает в себе оба подхода: оно использует поиск по хешу для примитивов и линейный поиск объектов.

function uniq(a) {
    var prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];

    return a.filter(function(item) {
        var type = typeof item;
        if(type in prims)
            return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
        else
            return objs.indexOf(item) >= 0 ? false : objs.push(item);
    });
}

сортировка | уник

Другой вариант - сначала отсортировать массив, а затем удалить каждый элемент, равный предыдущему:

function uniq(a) {
    return a.sort().filter(function(item, pos, ary) {
        return !pos || item != ary[pos - 1];
    })
}

Опять же, это не работает с объектами (потому что все объекты равны для sort). Кроме того, мы молча меняем исходный массив как побочный эффект - не хорошо! Однако, если ваш ввод уже отсортирован, это путь (просто удалите sort из вышеупомянутого).

Уникальный...

Иногда требовалось унифицировать список на основе некоторых критериев, отличных от равенства, например, чтобы отфильтровать объекты, которые отличаются, но имеют некоторое свойство. Это можно сделать элегантно, передав обратный вызов. Этот обратный вызов "ключа" применяется к каждому элементу, а элементы с одинаковыми "ключами" удаляются. Поскольку ожидается, что key вернет примитив, хеш-таблица будет работать нормально:

function uniqBy(a, key) {
    var seen = {};
    return a.filter(function(item) {
        var k = key(item);
        return seen.hasOwnProperty(k) ? false : (seen[k] = true);
    })
}

Особенно полезным key() является JSON.stringify, который удаляет объекты, которые физически отличаются, но "выглядят" одинаково:

a = [[1,2,3], [4,5,6], [1,2,3]]
b = uniqBy(a, JSON.stringify)
console.log(b) // [[1,2,3], [4,5,6]]

Если key не является примитивным, вам придется прибегнуть к линейному поиску:

function uniqBy(a, key) {
    var index = [];
    return a.filter(function (item) {
        var k = key(item);
        return index.indexOf(k) >= 0 ? false : index.push(k);
    });
}

В ES6 вы можете использовать Set:

function uniqBy(a, key) {
    let seen = new Set();
    return a.filter(item => {
        let k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}

или Map:

function uniqBy(a, key) {
    return [
        ...new Map(
            a.map(x => [key(x), x])
        ).values()
    ]
}

которые также работают с не примитивными ключами.

Первый или последний?

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

Используйте описанный выше вариант Set, чтобы сохранить первое, и Map, чтобы сохранить последнее:

function uniqByKeepFirst(a, key) {
    let seen = new Set();
    return a.filter(item => {
        let k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}


function uniqByKeepLast(a, key) {
    return [
        ...new Map(
            a.map(x => [key(x), x])
        ).values()
    ]
}

//

data = [
    {a:1, u:1},
    {a:2, u:2},
    {a:3, u:3},
    {a:4, u:1},
    {a:5, u:2},
    {a:6, u:3},
];

console.log(uniqByKeepFirst(data, it => it.u))
console.log(uniqByKeepLast(data, it => it.u))

Библиотеки

Оба подчеркивания и Lo-Dash предоставляют методы uniq. Их алгоритмы в основном похожи на первый фрагмент выше и сводятся к следующему:

var result = [];
a.forEach(function(item) {
     if(result.indexOf(item) < 0) {
         result.push(item);
     }
});

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

Если вы используете jQuery и не можете ничего перенести без доллара перед этим, это выглядит так:

  $.uniqArray = function(a) {
        return $.grep(a, function(item, pos) {
            return $.inArray(item, a) === pos;
        });
  }

который, опять же, является вариантом первого фрагмента.

Производительность

Вызовы функций в JavaScript дороги, поэтому приведенные выше решения, какими бы краткими они ни были, не особенно эффективны. Для максимальной производительности замените filter на цикл и избавьтесь от других вызовов функций:

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

Этот кусок уродливого кода делает то же самое, что и фрагмент №3 выше, , но на порядок быстрее (по состоянию на 2017 год он только в два раза быстрее - основные сотрудники JS делают отличную работу!)

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

/////

var r = [0,1,2,3,4,5,6,7,8,9],
    a = [],
    LEN = 1000,
    LOOPS = 1000;

while(LEN--)
    a = a.concat(r);

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq(a);
document.write('<br>uniq, ms/loop: ' + (new Date() - d)/LOOPS)

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq_fast(a);
document.write('<br>uniq_fast, ms/loop: ' + (new Date() - d)/LOOPS)

ES6

ES6 предоставляет объект Set, который значительно упрощает задачу:

function uniq(a) {
   return Array.from(new Set(a));
}

или

let uniq = a => [...new Set(a)];

Обратите внимание, что, в отличие от Python, наборы ES6 повторяются в порядке вставки, поэтому этот код сохраняет порядок исходного массива.

Однако, если вам нужен массив с уникальными элементами, почему бы не использовать множества с самого начала?

Генераторы

"Ленивая", основанная на генераторе версия uniq может быть построена на той же основе:

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

function* uniqIter(a) {
    let seen = new Set();

    for (let x of a) {
        if (!seen.has(x)) {
            seen.add(x);
            yield x;
        }
    }
}

// example:

function* randomsBelow(limit) {
    while (1)
        yield Math.floor(Math.random() * limit);
}

// note that randomsBelow is endless

count = 20;
limit = 30;

for (let r of uniqIter(randomsBelow(limit))) {
    console.log(r);
    if (--count === 0)
        break
}

// exercise for the reader: what happens if we set 'limit' less than 'count' and why

+3090
источник
другие ответы

Связанные вопросы


Похожие вопросы

Надоело видеть все плохие примеры с помощью for-loops или jQuery. Javascript имеет идеальные инструменты для этого в наши дни: сортировка, карта и сокращение.

Uniq уменьшает при сохранении существующего порядка

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

var uniq = names.reduce(function(a,b){
    if (a.indexOf(b) < 0 ) a.push(b);
    return a;
  },[]);

console.log(uniq, names) // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

// one liner
return names.reduce(function(a,b){if(a.indexOf(b)<0)a.push(b);return a;},[]);

Более быстрый uniq с сортировкой

Есть, вероятно, более быстрые способы, но этот довольно приличный.

var uniq = names.slice() // slice makes copy of array before sorting it
  .sort(function(a,b){
    return a > b;
  })
  .reduce(function(a,b){
    if (a.slice(-1)[0] !== b) a.push(b); // slice(-1)[0] means last item in array without removing it (like .pop())
    return a;
  },[]); // this empty array becomes the starting value for a

// one liner
return names.slice().sort(function(a,b){return a > b}).reduce(function(a,b){if (a.slice(-1)[0] !== b) a.push(b);return a;},[]);

Обновление 2015: версия ES6:

В ES6 у вас есть Sets and Spread, который делает его очень простым и эффективным для удаления всех дубликатов:

var uniq = [ ...new Set(names) ]; // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

Сортировка по вступлению:

Кто-то спросил о заказе результатов в зависимости от количества уникальных имен:

var names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl']

var uniq = names
  .map((name) => {
    return {count: 1, name: name}
  })
  .reduce((a, b) => {
    a[b.name] = (a[b.name] || 0) + b.count
    return a
  }, {})

var sorted = Object.keys(uniq).sort((a, b) => uniq[a] < uniq[b])

console.log(sorted)
+308
источник

Vanilla JS: удаление дубликатов с использованием объекта, такого как Set

Вы всегда можете попробовать поместить его в объект, а затем повторить его ключи:

function remove_duplicates(arr) {
    var obj = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        obj[arr[i]] = true;
    }
    for (var key in obj) {
        ret_arr.push(key);
    }
    return ret_arr;
}

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

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

function remove_duplicates_safe(arr) {
    var seen = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        if (!(arr[i] in seen)) {
            ret_arr.push(arr[i]);
            seen[arr[i]] = true;
        }
    }
    return ret_arr;

}

ECMAScript 6: используйте новую структуру данных Set (безопасный порядок)

В ECMAScript 6 добавлена ​​новая структура данных Set, которая позволяет хранить значения любого типа. Set.values возвращает элементы в порядке размещения.

function remove_duplicates_es6(arr) {
    let s = new Set(arr);
    let it = s.values();
    return Array.from(it);
}

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

a = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

b = remove_duplicates(a);
// b:
// ["Adam", "Carl", "Jenny", "Matt", "Mike", "Nancy"]

c = remove_duplicates_safe(a);
// c:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

d = remove_duplicates_es6(a);
// d:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]
+97
источник

Используйте Underscore.js

Это библиотека с множеством функций для управления массивами.

Это связь с jQuery tux и Backbone.js подвесы.

_. uniq

_.uniq(array, [isSorted], [iterator]) Псевдоним: уникальный
Производит дублируемую версию массива , используя === для тестирования объекта равенство. Если вы заранее знаете, что массив сортируется, передавая true для isSorted будет работать гораздо быстрее алгоритм. Если ты хочешь вычислить уникальные элементы на основе преобразования, передать итераторфункция.

Пример

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

alert(_.uniq(names, false));

Примечание: Lo-Dash (участник underscore) также предлагает сопоставимый . uniq.

+71
источник

Версия с одной строкой с использованием фильтра массива и функций indexOf:

arr = arr.filter (function (value, index, array) { 
    return array.indexOf (value) == index;
});
+68
источник

Вы можете просто сделать это в JavaScript, с помощью второго - индекс - параметра метода filter:

var a = [2,3,4,5,5,4];
a.filter(function(value, index){ return a.indexOf(value) == index });

или в краткосрочной перспективе

a.filter((v,i) => a.indexOf(v) == i)
+54
источник

Одна строка:

let names = ['Mike','Matt','Nancy','Adam','Jenny','Nancy','Carl', 'Nancy'];
let dup = [...new Set(names)];
console.log(dup);
+48
источник

Самый сжатый способ удаления дубликатов из массива с использованием собственных функций javascript - использовать последовательность, как показано ниже:

vals.sort().reduce(function(a, b){ if (b != a[0]) a.unshift(b); return a }, [])

нет необходимости в slice и indexOf в функции сокращения, как я видел в других примерах! имеет смысл использовать его вместе с функцией фильтрации, хотя:

vals.filter(function(v, i, a){ return i == a.indexOf(v) })

Еще один способ ES6 (2015), который уже работает в нескольких браузерах, заключается в следующем:

Array.from(new Set(vals))

или даже с помощью оператора распространения:

[...new Set(vals)]

ура!

+34
источник

используйте Array.filter() как это

var actualArr = ['Apple', 'Apple', 'Banana', 'Mango', 'Strawberry', 'Banana'];

console.log('Actual Array: ' + actualArr);

var filteredArr = actualArr.filter(function(item, index) {
  if (actualArr.indexOf(item) == index)
    return item;
});

console.log('Filtered Array: ' + filteredArr);

это может быть сокращено в ES6 до

actualArr.filter((item,index,self) => self.indexOf(item)==index);

Вот хорошее объяснение Array.filter()

+31
источник

Идем для этого:

var uniqueArray = duplicateArray.filter(function(elem, pos) {
    return duplicateArray.indexOf(elem) == pos;
}); 

Теперь uniqueArray не содержит дубликатов.

+20
источник

Самый простой, с которым я столкнулся. В es6.

 var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl", "Mike", "Nancy"]

 var noDupe = Array.from(new Set(names))

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

+19
источник

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

Я считаю, что это лучший способ сделать это

var myArray = [100, 200, 100, 200, 100, 100, 200, 200, 200, 200],
    reduced = Object.keys(myArray.reduce((p,c) => (p[c] = true,p),{}));
console.log(reduced);

OK.. даже если это O (n), а остальные O (n ^ 2) Мне было любопытно увидеть сравнительное сравнение между этой таблицей уменьшения/поиска и фильтрами /indexOf (я выбираю Jeetendras очень приятно реализация fooobar.com/questions/12937/...). Я готовлю массив элементов 100K, заполненный случайными положительными целыми числами в диапазоне 0-9999, и удаляет дубликаты. Я повторяю тест 10 раз, и среднее из результатов показывает, что они не соответствуют производительности.

  • В firefox v47 уменьшить и lut: 14.85ms против фильтра и indexOf: 2836ms
  • В chrome v51 уменьшить и lut: 23.90ms против фильтра и indexOf: 1066ms

Хорошо, пока все хорошо. Но давайте сделаем это правильно на этот раз в стиле ES6. Это выглядит так здорово..! Но на данный момент, как это будет действовать против мощного решения, для меня это загадка. Давайте сначала посмотрим на код, а затем сравним его.

var myArray = [100, 200, 100, 200, 100, 100, 200, 200, 200, 200],
    reduced = [...myArray.reduce((p,c) => p.set(c,true),new Map()).keys()];
console.log(reduced);

Ничего себе, это было коротко..! Но как насчет производительности..? Это красиво... Поскольку тяжелый вес фильтра /indexOf поднялся над нашими плечами, теперь я могу проверить массив 1M случайных элементов положительных целых чисел в диапазоне 0..99999, чтобы получить среднее значение из 10 последовательных тестов. На этот раз я могу сказать, что это настоящий матч. Посмотрите результат самостоятельно:)

var ranar = [],
     red1 = a => Object.keys(a.reduce((p,c) => (p[c] = true,p),{})),
     red2 = a => reduced = [...a.reduce((p,c) => p.set(c,true),new Map()).keys()],
     avg1 = [],
     avg2 = [],
       ts = 0,
       te = 0,
     res1 = [],
     res2 = [],
     count= 10;
for (var i = 0; i<count; i++){
  ranar = (new Array(1000000).fill(true)).map(e => Math.floor(Math.random()*100000));
  ts = performance.now();
  res1 = red1(ranar);
  te = performance.now();
  avg1.push(te-ts);
  ts = performance.now();
  res2 = red2(ranar);
  te = performance.now();
  avg2.push(te-ts);
}

avg1 = avg1.reduce((p,c) => p+c)/count;
avg2 = avg2.reduce((p,c) => p+c)/count;

console.log("reduce & lut took: " + avg1 + "msec");
console.log("map & spread took: " + avg2 + "msec");

Какой из них вы бы использовали..? Ну не так быстро...! Не обманывайтесь. Карта находится при перемещении. Теперь посмотрим... во всех вышеперечисленных случаях мы заполняем массив размера n числами диапазона < п. Я имею в виду, что у нас есть массив размером 100, и мы заполняем случайные числа 0..9, поэтому есть определенные дубликаты и "почти" определенно каждый номер имеет дубликат. Как насчет заполнения массива размером 100 со случайными номерами 0..9999. Теперь посмотрим на карту, играющую дома. На этот раз массив из 100K элементов, но случайный диапазон чисел составляет 0..100M. Мы проведем 100 последовательных тестов, чтобы усреднить результаты. Хорошо давайте посмотрим ставки..! < - no typo

var ranar = [],
     red1 = a => Object.keys(a.reduce((p,c) => (p[c] = true,p),{})),
     red2 = a => reduced = [...a.reduce((p,c) => p.set(c,true),new Map()).keys()],
     avg1 = [],
     avg2 = [],
       ts = 0,
       te = 0,
     res1 = [],
     res2 = [],
     count= 100;
for (var i = 0; i<count; i++){
  ranar = (new Array(100000).fill(true)).map(e => Math.floor(Math.random()*100000000));
  ts = performance.now();
  res1 = red1(ranar);
  te = performance.now();
  avg1.push(te-ts);
  ts = performance.now();
  res2 = red2(ranar);
  te = performance.now();
  avg2.push(te-ts);
}

avg1 = avg1.reduce((p,c) => p+c)/count;
avg2 = avg2.reduce((p,c) => p+c)/count;

console.log("reduce & lut took: " + avg1 + "msec");
console.log("map & spread took: " + avg2 + "msec");

Теперь это впечатляющее возвращение Карты()..! Может быть, теперь вы можете принять лучшее решение, если хотите удалить обманщиков.

Ну ладно, мы все счастливы. Но ведущая роль всегда заканчивается аплодисментами. Я уверен, что некоторые из вас задаются вопросом, что будет делать объект Set. Теперь, поскольку мы открыты для ES6, и мы знаем, что карта является победителем предыдущих игр, сравним Map with Set как final. Типичная игра "Реал Мадрид против Барселоны" на этот раз... или не так ли? Посмотрим, кто выиграет el classico:)

var ranar = [],
     red1 = a => reduced = [...a.reduce((p,c) => p.set(c,true),new Map()).keys()],
     red2 = a => Array.from(new Set(a)),
     avg1 = [],
     avg2 = [],
       ts = 0,
       te = 0,
     res1 = [],
     res2 = [],
     count= 100;
for (var i = 0; i<count; i++){
  ranar = (new Array(100000).fill(true)).map(e => Math.floor(Math.random()*10000000));
  ts = performance.now();
  res1 = red1(ranar);
  te = performance.now();
  avg1.push(te-ts);
  ts = performance.now();
  res2 = red2(ranar);
  te = performance.now();
  avg2.push(te-ts);
}

avg1 = avg1.reduce((p,c) => p+c)/count;
avg2 = avg2.reduce((p,c) => p+c)/count;

console.log("map & spread took: " + avg1 + "msec");
console.log("set & A.from took: " + avg2 + "msec");

Ничего себе.. мужчина..! Ну, неожиданно это оказалось совсем не классическим. Больше, чем в Барселоне против CA Osasuna:))

+17
источник

Решение 1

Array.prototype.unique = function() {
    var a = [];
    for (i = 0; i < this.length; i++) {
        var current = this[i];
        if (a.indexOf(current) < 0) a.push(current);
    }
    return a;
}

Решение 2 (с использованием Set)

Array.prototype.unique = function() {
    return Array.from(new Set(this));
}

Тестовое задание

var x=[1,2,3,3,2,1];
x.unique() //[1,2,3]

Спектакль

Когда я тестировал как реализацию (с и без Set) для производительности в хром, я обнаружил, что один с Set намного быстрее!

Array.prototype.unique1 = function() {
    var a = [];
    for (i = 0; i < this.length; i++) {
        var current = this[i];
        if (a.indexOf(current) < 0) a.push(current);
    }
    return a;
}


Array.prototype.unique2 = function() {
    return Array.from(new Set(this));
}

var x=[];
for(var i=0;i<10000;i++){
	x.push("x"+i);x.push("x"+(i+1));
}

console.time("unique1");
console.log(x.unique1());
console.timeEnd("unique1");



console.time("unique2");
console.log(x.unique2());
console.timeEnd("unique2");
+17
источник

Ниже более чем на 80% быстрее, чем приведенный метод jQuery (см. Тесты ниже). Это ответ от аналогичного вопроса несколько лет назад. Если я встречу человека, который изначально предложил его, я отправлю кредит. Чистый JS.

var temp = {};
for (var i = 0; i < array.length; i++)
  temp[array[i]] = true;
var r = [];
for (var k in temp)
  r.push(k);
return r;

Сравнение моих тестов: http://jsperf.com/remove-duplicate-array-tests

+16
источник

Вот простой ответ на вопрос.

var names = ["Alex","Tony","James","Suzane", "Marie", "Laurence", "Alex", "Suzane", "Marie", "Marie", "James", "Tony", "Alex"];
var uniqueNames = [];

    for(var i in names){
        if(uniqueNames.indexOf(names[i]) === -1){
            uniqueNames.push(names[i]);
        }
    }
+14
источник

В ECMAScript 6 (иначе ECMAScript 2015) Set можно использовать для фильтрации дубликатов. Затем его можно преобразовать обратно в массив с помощью оператора spread.

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"],
    unique = [...new Set(names)];
+14
источник

Лучшие ответы имеют сложность O(n²), но это можно сделать только с помощью O(n), используя объект в качестве хеша:

function getDistinctArray(arr) {
    var dups = {};
    return arr.filter(function(el) {
        var hash = el.valueOf();
        var isDup = dups[hash];
        dups[hash] = true;
        return !isDup;
    });
}

Это будет работать для строк, чисел и дат. Если ваш массив содержит объекты, приведенное выше решение не будет работать, поскольку при приведении к строке все они будут иметь значение "[object Object]" (или что-то подобное), и это не подходит в качестве значения поиска. Вы можете получить реализацию O(n) для объектов, установив флаг для самого объекта:

function getDistinctObjArray(arr) {
    var distinctArr = arr.filter(function(el) {
        var isDup = el.inArray;
        el.inArray = true;
        return !isDup;
    });
    distinctArr.forEach(function(el) {
        delete el.inArray;
    });
    return distinctArr;
}

Редакция 2019 года: Современные версии JavaScript значительно облегчают решение этой проблемы. Использование Set будет работать независимо от того, содержит ли ваш массив объекты, строки, числа или любой другой тип.

function getDistinctArray(arr) {
    return [...new Set(arr)];
}

Реализация настолько проста, что определение функции больше не требуется.

+12
источник

Простым, но эффективным методом является использование метода filter в сочетании с фильтром function(value, index){ return this.indexOf(value) == index }.

Пример кода:

var data = [2,3,4,5,5,4];
var filter = function(value, index){ return this.indexOf(value) == index };
var filteredData = data.filter(filter, data );

document.body.innerHTML = '<pre>' + JSON.stringify(filteredData, null, '\t') +  '</pre>';

См. также этот скрипт.

+10
источник

Итак, параметры:

let a = [11,22,11,22];
let b = []


b = [ ...new Set(a) ];     
// b = [11, 22]

b = Array.from( new Set(a))   
// b = [11, 22]

b = a.filter((val,i)=>{
  return a.indexOf(val)==i
})                        
// b = [11, 22]
+9
источник

здесь простой метод без специальных библиотек - это специальная функция,

name_list = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
get_uniq = name_list.filter(function(val,ind) { return name_list.indexOf(val) == ind; })

console.log("Original name list:"+name_list.length, name_list)
console.log("\n Unique name list:"+get_uniq.length, get_uniq)

введите описание изображения здесь

+9
источник

Помимо более простого и более тонкого решения, чем текущие ответы (за вычетом будущих ES6), я тестировал это, и это было намного быстрее:

var uniqueArray = dupeArray.filter(function(item, i, self){
  return self.lastIndexOf(item) == i;
});

Одно предостережение: Array.lastIndexOf() добавлен в IE9, поэтому, если вам нужно идти ниже этого, вам нужно искать в другом месте.

+8
источник

Вы также можете использовать метод Array.unique() из библиотека JavaScript Lab - или украсть идею оттуда.

Однако код там не очень хорошо написан, так как он объявляет метод unique() как свойство прототипа Array, добавляя его к каждому массиву, нарушая функциональность for...in (поскольку цикл for...in всегда перебирайте и переменную unique).

+7
источник

Общий функциональный подход

Вот общий и строго функциональный подход с ES2015:

// small, reusable auxiliary functions

const apply = f => a => f(a);

const flip = f => b => a => f(a) (b);

const uncurry = f => (a, b) => f(a) (b);

const push = x => xs => (xs.push(x), xs);

const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);

const some = f => xs => xs.some(apply(f));


// the actual de-duplicate function

const uniqueBy = f => foldl(
   acc => x => some(f(x)) (acc)
    ? acc
    : push(x) (acc)
 ) ([]);


// comparators

const eq = y => x => x === y;

// string equality case insensitive :D
const seqCI = y => x => x.toLowerCase() === y.toLowerCase();


// mock data

const xs = [1,2,3,1,2,3,4];

const ys = ["a", "b", "c", "A", "B", "C", "D"];


console.log( uniqueBy(eq) (xs) );

console.log( uniqueBy(seqCI) (ys) );

Мы можем легко получить unique из unqiueBy или использовать более быструю реализацию, используя Set s:

const unqiue = uniqueBy(eq);

// const unique = xs => Array.from(new Set(xs));

Преимущества такого подхода:

  • общее решение с помощью отдельной функции компаратора
  • декларативная и лаконичная реализация
  • повторное использование других небольших, общих функций

Особенности производительности

uniqueBy не так быстро, как императивная реализация с циклами, но это более выразительно из-за его типичности.

Если вы идентифицируете uniqueBy как причину конкретного штрафа за производительность в своем приложении, замените его на оптимизированный код. То есть, напишите свой код сначала функциональным, декларативным способом. Впоследствии при условии, что вы столкнулись с проблемами производительности, попробуйте оптимизировать код в местах, которые являются причиной проблемы.

Потребление памяти и сбор мусора

uniqueBy использует мутации (push(x) (acc)), скрытые внутри своего тела. Он повторно использует аккумулятор вместо того, чтобы отбрасывать его после каждой итерации. Это уменьшает потребление памяти и давление GC. Поскольку этот побочный эффект завернут внутри функции, все внешнее остается чистым.

+7
источник

Здесь очень просто для понимания и работы в любом месте (даже в PhotoshopScript) коде. Проверьте это!

var peoplenames = new Array("Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl");

peoplenames = unique(peoplenames);
alert(peoplenames);

function unique(array){
    var len = array.length;
    for(var i = 0; i < len; i++) for(var j = i + 1; j < len; j++) 
        if(array[j] == array[i]){
            array.splice(j,1);
            j--;
            len--;
        }
    return array;
}

//*result* peoplenames == ["Mike","Matt","Nancy","Adam","Jenny","Carl"]
+7
источник
for (i=0; i<originalArray.length; i++) {  
    if (!newArray.includes(originalArray[i])) {
        newArray.push(originalArray[i]); 
    }
}
+5
источник

Небольшое изменение превосходного ответа thg435 на использование пользовательского компаратора:

function contains(array, obj) {
    for (var i = 0; i < array.length; i++) {
        if (isEqual(array[i], obj)) return true;
    }
    return false;
}
//comparator
function isEqual(obj1, obj2) {
    if (obj1.name == obj2.name) return true;
    return false;
}
function removeDuplicates(ary) {
    var arr = [];
    return ary.filter(function(x) {
        return !contains(arr, x) && arr.push(x);
    });
}
+4
источник
$(document).ready(function() {

    var arr1=["dog","dog","fish","cat","cat","fish","apple","orange"]

    var arr2=["cat","fish","mango","apple"]

    var uniquevalue=[];
    var seconduniquevalue=[];
    var finalarray=[];

    $.each(arr1,function(key,value){

       if($.inArray (value,uniquevalue) === -1)
       {
           uniquevalue.push(value)

       }

    });

     $.each(arr2,function(key,value){

       if($.inArray (value,seconduniquevalue) === -1)
       {
           seconduniquevalue.push(value)

       }

    });

    $.each(uniquevalue,function(ikey,ivalue){

        $.each(seconduniquevalue,function(ukey,uvalue){

            if( ivalue == uvalue)

            {
                finalarray.push(ivalue);
            }   

        });

    });
    alert(finalarray);
});
+4
источник

Если вы случайно использовали

D3.js

Вы могли бы сделать

d3.set(["foo", "bar", "foo", "baz"]).values() ==> ["foo", "bar", "baz"]

https://github.com/mbostock/d3/wiki/Arrays#set_values

+4
источник

Это, вероятно, один из самых быстрых способов удаления навсегда дубликатов из массива В 10 раз быстрее, чем большинство функций здесь. & 78x быстрее в сафари

function toUnique(a,b,c){               //array,placeholder,placeholder
 b=a.length;while(c=--b)while(c--)a[b]!==a[c]||a.splice(c,1)
}

если вы не можете прочитать приведенный выше код, прочитайте javascript книгу или вот некоторые объяснения относительно более короткого кода. fooobar.com/questions/1682/...

+3
источник
  • 1
  • 2

Посмотрите другие вопросы по меткам или Задайте вопрос