Премахване на дублиращи се стойности от масив JS
Имам много прост масив на JavaScript, който може да съдържа или да не съдържа дубликати.
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
Трябва да премахна дубликатите и да поставя уникалните стойности в нов масив.
Бих могъл да посоча всички кодове, които съм'пробвал, но мисля, че е'безполезно, защото не работят. Приемам и решения на jQuery.
Подобен въпрос:
1217
3
TL;DR
Използване на конструктора Set и синтаксиса spread syntax:
"Умен" но наïве начин
В общи линии претърсваме масива и за всеки елемент проверяваме дали първата позиция на този елемент в масива е равна на текущата позиция. Очевидно е, че тези две позиции са различни за дублиращи се елементи. Използвайки третия ("this array") параметър на обратната връзка на филтъра, можем да избегнем затварянето на променливата на масива:
Въпреки че е кратък, този алгоритъм не е особено ефективен за големи масиви (квадратично време). Hashtables на помощ
Обикновено това се прави по този начин. Идеята е всеки елемент да се постави в хаштабъл и след това да се проверява незабавно за наличието му. Това ни дава линейно време, но има поне два недостатъка:
uniq([1,"1"])
ще върне само[1]
по същата причина всички обекти ще се считат за равни:
uniq([{foo:1},{foo:2}])
ще върне само[{foo:1}]
. Въпреки това, ако масивите ви съдържат само примитиви и не ви интересуват типовете (например винаги са числа), това решение е оптимално. Най-доброто от два святаУниверсалното решение съчетава и двата подхода: то използва хеш претърсвания за примитиви и линейно търсене за обекти.
сортиране | uniq
Друг вариант е първо да сортирате масива и след това да премахнете всеки елемент, равен на предходния:
Отново, това не работи с обекти (защото всички обекти са равни за
sort
). Освен това, като страничен ефект ние безшумно променяме оригиналния масив - не е добре! Въпреки това, ако входните данни вече са сортирани, това е начинът да се направи (просто премахнетеsort
от горното). Уникално от...Понякога е желателно да уникализирате списък въз основа на някакъв критерий, различен от равенство, например за да филтрирате обекти, които са различни, но имат общо свойство. Това може да се направи по елегантен начин, като се подаде обратна връзка. Това обратно извикване "key" се прилага към всеки елемент и елементите с еднакви "keys" се премахват. Тъй като се очаква
ключ
да връща примитив, хеш-таблицата ще работи добре тук:Особено полезен
ключ()
еJSON.stringify
, който ще премахне обекти, които са физически различни, но "изглеждат" еднакво:Ако
ключът
не е примитивен, трябва да се прибегне до линейното търсене:В ES6 можете да използвате
Set
:или
Map
:които също работят с непървични ключове. Първи или последен?
Когато премахвате обекти по ключ, може да искате да запазите първия от "равните" обекти или последния. Използвайте варианта
Set
по-горе, за да запазите първия, иMap
, за да запазите последния:Библиотеки
Както underscore, така и Lo-Dash предоставят методите
uniq
. Техните алгоритми са в общи линии подобни на първия фрагмент по-горе и се свеждат до следното:Това е квадратично, но има хубави допълнителни екстри, като например обвиване на native
indexOf
, възможност за uniqify по ключ (iteratee
на техния език) и оптимизации за вече сортирани масиви. Ако използвате jQuery и не можете да понасяте нищо без долар пред него, това става по следния начин:което отново е разновидност на първия фрагмент. Изпълнение
Извикванията на функции са скъпи в JavaScript, затова горните решения, колкото и кратки да са, не са особено ефективни. За да постигнете максимална производителност, заменете
filter
с цикъл и се отървете от други извиквания на функции:Тази част от грозния код прави същото като откъс № 3 по-горе, но на порядък по-бързо (към 2017 г. е'само два пъти по-бърз - хората от ядрото на JS вършат страхотна работа!)
ES6
ES6 предоставя обекта Set, който прави нещата много по-лесни:
или
Обърнете внимание, че за разлика от питон, в ES6 множествата се итерират в реда на вмъкване, така че този код запазва реда на оригиналния масив. Въпреки това, ако имате нужда от масив с уникални елементи, защо да не използвате множества от самото начало? Генератори
На същата основа може да се изгради "мързелива", базирана на генератори версия на
uniq
:Бързо и мръсно използване на jQuery:
Vanilla JS: Премахване на дубликати с помощта на обект като множество
Винаги можете да опитате да го поставите в обект и след това да итерирате през ключовете му:
Vanilla JS: Премахване на дубликати чрез проследяване на вече видяни стойности (безопасно за реда)
Или, за версия, която не нарушава реда, използвайте обект за съхраняване на всички вече видени стойности и проверявайте стойностите спрямо него, преди да ги добавите към масива.
ECMAScript 6: Използване на новата структура за данни Set (сигурна за реда)
ECMAScript 6 добавя новата структура данни
Set
, която ви позволява да съхранявате стойности от всякакъв тип.Set.values
връща елементи в реда на въвеждане.Примерно използване: