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

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

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

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

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

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

Ответ 1

Быстрое и грязное использование 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);
});

Ответ 2

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

Ответ 3

Надоело видеть все плохие примеры с помощью 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)

Ответ 4

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

Ответ 5

Используйте 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.

Ответ 6

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

arr = arr.filter (function (value, index, array) { 
    return array.indexOf (value) == index;
});

Ответ 7

Вы можете просто сделать это в 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)

Ответ 8

Одна строка:

let names = ['Mike','Matt','Nancy','Adam','Jenny','Nancy','Carl', 'Nancy'];
let dup = [...new Set(names)];
console.log(dup);

Ответ 9

Самый сжатый способ удаления дубликатов из массива с использованием собственных функций 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)]

ура!

Ответ 10

используйте 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);

Ответ 11

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

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

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

Ответ 13

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

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

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

Ответ 14

Решение 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");

Ответ 15

Ниже более чем на 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]);
        }
    }

Ответ 17

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

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"],
    unique = [...new Set(names)];

Ответ 18

Лучшие ответы имеют сложность 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)];
}

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

Ответ 19

Простым, но эффективным методом является использование метода 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>';

Ответ 20

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

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]

Ответ 21

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

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)

Ответ 22

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

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

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

Ответ 23

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

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

Ответ 24

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

Вот общий и строго функциональный подход с 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) );

Ответ 25

Здесь очень просто для понимания и работы в любом месте (даже в 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"]

Ответ 26

for (i=0; i<originalArray.length; i++) {  
    if (!newArray.includes(originalArray[i])) {
        newArray.push(originalArray[i]); 
    }
}

Ответ 27

Небольшое изменение превосходного ответа 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);
    });
}

Ответ 28

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

Ответ 30

Это, вероятно, один из самых быстрых способов удаления навсегда дубликатов из массива В 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/info/1682/...