2012-06-28 7 views
86

Как получить список уникальных значений в массиве? Должен ли я всегда использовать второй массив или есть что-то похожее на хэш-карту java в JavaScript?Как получить уникальные значения в массиве

Я собираюсь использовать JavaScript и JQuery только. Никакие дополнительные библиотеки не могут использоваться.

+2

http://stackoverflow.com/questions/5381621/jquery-function- to-get-all-unique-elements-from-an-array - описывает то, что вы хотите, я думаю? – SpaceBison

+1

Вы можете использовать библиотеку 'underscore.js'? – jakee

+0

jash hashmap в основном такой же, как объект javascript. Синтаксис {"key": "value", "key2": "value2"} – Ian

ответ

80

Поскольку я продолжал об этом в комментариях к ответу @ Rocket, я могу также привести пример, который не использует библиотеки. Это требует два новых функций прототипа, contains и unique

Array.prototype.contains = function(v) { 
    for(var i = 0; i < this.length; i++) { 
     if(this[i] === v) return true; 
    } 
    return false; 
}; 

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

Вы можете сделать:

var duplicates = [1,3,4,2,1,2,3,8]; 
var uniques = duplicates.unique(); // result = [1,3,4,2,8] 

Для большей надежности можно заменить contains с indexOf подкладки MDN и проверьте каждый элемент indexOf равно -1: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf

+0

Спасибо за примеры. Я использую их для фильтрации опций для окна выбора. Это должно работать хорошо. – Astronaut

+0

' ~ a.indexOf (b) === (a.indexOf (b) == -1) ' – Orwellophile

+0

@Orwellophile. Вы меня более четко объясняете своим ответом, главным образом для начинающих пользователей, таких как я. Можете ли вы объяснить, как использовать его, пожалуйста? –

2

Не является родным в Javascript, но множество библиотек имеют этот метод.

Underscore.js's _.uniq(array) (link) работает достаточно хорошо (source).

+0

Спасибо за обмен! Эта функция принимает итератор и контекст вместе с массивом как список аргументов из v1.4.3. – KunJ

4

Использование JQuery, вот массив уникальной функцией я сделал:

Array.prototype.unique = function() { 
    var arr = this; 
    return $.grep(arr, function (v, i) { 
     return $.inArray(v, arr) === i; 
    }); 
} 

console.log([1,2,3,1,2,3].unique()); // [1,2,3] 
+3

, если вы собираетесь использовать jQuery внутри прототипа основного ядра javascript, не лучше ли написать функцию jQuery, такую ​​как '$ .uniqueArray (обр) '? Включение ссылок на jQuery в прототипе 'Array' кажется сомнительным – jackwanders

+1

@jackwanders: Что в этом сомнительного? Если вы получили jQuery на странице, давайте использовать его. –

+0

Только что новая уникальная функция, которую вы написали, теперь зависит от jQuery; вы не можете переместить его на новый сайт или приложение, не гарантируя, что JQuery используется там. – jackwanders

13

Если вы хотите оставить исходный массив нетронутыми,

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

Большинство браузеров имеют Array.prototype.filter:

var unique= array1.filter(function(itm, i){ 
    return array1.indexOf(itm)== i; 
    // returns true for only the first instance of itm 
}); 


//if you need a 'shim': 
Array.prototype.filter= Array.prototype.filter || function(fun, scope){ 
    var T= this, A= [], i= 0, itm, L= T.length; 
    if(typeof fun== 'function'){ 
     while(i<L){ 
      if(i in T){ 
       itm= T[i]; 
       if(fun.call(scope, itm, i, T)) A[A.length]= itm; 
      } 
      ++i; 
     } 
    } 
    return A; 
} 
Array.prototype.indexOf= Array.prototype.indexOf || function(what, i){ 
     if(!i || typeof i!= 'number') i= 0; 
     var L= this.length; 
     while(i<L){ 
      if(this[i]=== what) return i; 
      ++i; 
     } 
     return -1; 
    } 
+0

@ Rocket- спасибо! – kennebec

-1

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

JavaScript: 
function getUniqueRadios() { 

var x=document.getElementById("QnA"); 
var ansArray = new Array(); 
var prev; 


for (var i=0;i<x.length;i++) 
    { 
    // Check for unique radio button group 
    if (x.elements[i].type == "radio") 
    { 
      // For the first element prev will be null, hence push it into array and set the prev var. 
      if (prev == null) 
      { 
       prev = x.elements[i].name; 
       ansArray.push(x.elements[i].name); 
      } else { 
        // We will only push the next radio element if its not identical to previous. 
        if (prev != x.elements[i].name) 
        { 
         prev = x.elements[i].name; 
         ansArray.push(x.elements[i].name); 
        } 
      } 
    } 

    } 

    alert(ansArray); 

}

HTML:

<body> 

<form name="QnA" action="" method='post' "> 

<input type="radio" name="g1" value="ANSTYPE1"> good </input> 
<input type="radio" name="g1" value="ANSTYPE2"> avg </input> 

<input type="radio" name="g2" value="ANSTYPE3"> Type1 </input> 
<input type="radio" name="g2" value="ANSTYPE2"> Type2 </input> 


<input type="submit" value='SUBMIT' onClick="javascript:getUniqueRadios()"></input> 


</form> 
</body> 
0

Единственная проблема с решениями данных до сих пор является эффективность. Если вас это беспокоит (и вы, вероятно, должны), вам нужно избегать вложенных циклов: для * for, filter * indexOf, grep * inArray, они все повторяют массив несколько раз. Вы можете реализовать один цикл с решениями, как this или this

128

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

var a = ["1", "1", "2", "3", "3", "1"]; 
var unique = a.filter(function(item, i, ar){ return ar.indexOf(item) === i; }); 

Update 18-04 -17

Кажется, что это «Array.prototype.включает в себя в настоящее время имеет широкую поддержку в последних версиях браузеров (магистральном) compatibility

Update 29/07/2015:

Есть планы работ для браузеров, чтобы поддержать стандартизированную «Array.prototype .includes 'метод, который, хотя прямо не отвечает на этот вопрос; часто связано.

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

["1", "1", "2", "3", "3", "1"].includes("2");  // true 

Pollyfill (browser support, source from mozilla):

// https://tc39.github.io/ecma262/#sec-array.prototype.includes 
if (!Array.prototype.includes) { 
    Object.defineProperty(Array.prototype, 'includes', { 
    value: function(searchElement, fromIndex) { 

     // 1. Let O be ? ToObject(this value). 
     if (this == null) { 
     throw new TypeError('"this" is null or not defined'); 
     } 

     var o = Object(this); 

     // 2. Let len be ? ToLength(? Get(O, "length")). 
     var len = o.length >>> 0; 

     // 3. If len is 0, return false. 
     if (len === 0) { 
     return false; 
     } 

     // 4. Let n be ? ToInteger(fromIndex). 
     // (If fromIndex is undefined, this step produces the value 0.) 
     var n = fromIndex | 0; 

     // 5. If n ≥ 0, then 
     // a. Let k be n. 
     // 6. Else n < 0, 
     // a. Let k be len + n. 
     // b. If k < 0, let k be 0. 
     var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); 

     // 7. Repeat, while k < len 
     while (k < len) { 
     // a. Let elementK be the result of ? Get(O, ! ToString(k)). 
     // b. If SameValueZero(searchElement, elementK) is true, return true. 
     // c. Increase k by 1. 
     // NOTE: === provides the correct "SameValueZero" comparison needed here. 
     if (o[k] === searchElement) { 
      return true; 
     } 
     k++; 
     } 

     // 8. Return false 
     return false; 
    } 
    }); 
} 
+1

Спасибо за это, довольно элегантный – felipekm

+0

Это почти копия пасты из kennebec, но, по общему признанию, передача массива в качестве параметра, а не использование закрытия, вероятно, улучшит производительность. – nus

+0

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

-2

я имею встроенную JQuery Уникального функции.

uniqueValues= jQuery.unique(duplicateValues); 

Для получения дополнительной информации вы можете обратиться к документации API jquery.

http://api.jquery.com/jquery.unique/

+7

_Примечание, что это работает только с массивами элементов DOM, а не строк или цифр._ - цитата из документации. – mco

5

Короткий и сладкий раствор, используя второй массив;

var axes2=[1,4,5,2,3,1,2,3,4,5,1,3,4]; 

    var distinct_axes2=[]; 

    for(var i=0;i<axes2.length;i++) 
     { 
     var str=axes2[i]; 
     if(distinct_axes2.indexOf(str)==-1) 
      { 
      distinct_axes2.push(str); 
      } 
     } 
    console.log("distinct_axes2 : "+distinct_axes2); // distinct_axes2 : 1,4,5,2,3 
+0

Короткие? и сладкий? Вы посмотрели на лучшие решения? –

3

Вам нужен только ванильный JS, чтобы найти uniques с Array.some и Array.reduce. С синтаксисом ES2015 это всего 62 символа.

a.reduce((c, v) => b.some(w => w === v) ? c : c.concat(v)), b) 

Array.some и Array.reduce поддерживаются в IE9 + и других браузерах. Просто измените функции жирной стрелки для регулярных функций для поддержки в браузерах, которые не поддерживают синтаксис ES2015.

var a = [1,2,3]; 
var b = [4,5,6]; 
// .reduce can return a subset or superset 
var uniques = a.reduce(function(c, v){ 
    // .some stops on the first time the function returns true     
    return (b.some(function(w){ return w === v; }) ? 
     // if there's a match, return the array "c" 
     c :  
     // if there's no match, then add to the end and return the entire array           
     c.concat(v)}),         
    // the second param in .reduce is the starting variable. This is will be "c" the first time it runs. 
    b);             

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

0

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

var distinctMap = {}; 
var testArray = ['John', 'John', 'Jason', 'Jason']; 
for (var i = 0; i < testArray.length; i++) { 
    var value = testArray[i]; 
    distinctMap[value] = ''; 
}; 
var unique_values = Object.keys(distinctMap); 
0
Array.prototype.unique = function() { 
    var dictionary = {}; 
    var uniqueValues = []; 
    for (var i = 0; i < this.length; i++) { 
     if (dictionary[this[i]] == undefined){ 
      dictionary[this[i]] = i; 
      uniqueValues.push(this[i]); 
     } 
    } 
    return uniqueValues; 
} 
0
function findUnique(arr) { 
    var result = []; 
    arr.forEach(function (d) { 
     if (result.indexOf(d) === -1) 
      result.push(d); 
    }); 
    return result; 
} 

var unique = findUnique([1, 2, 3, 1, 2, 1, 4]); // [1,2,3,4] 
7

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

var arr = ["a", "a", "b"]; 
var uniqueSet = new Set(arr); // {"a", "b"} 
var uniqueArr = Array.from(uniqueSet); // ["a", "b"] 
//Then continue to use array methods: 
uniqueArr.join(", "); // "a, b" 
+0

Аккуратно, было бы неплохо увидеть некоторые номера производительности, я думаю, что преобразование thosesets, в частности, если они очень динамичные и большие, может быть hickup производительности, единственный способ сказать - это проверить :) – Astronaut

+0

Очень гладкий, к сожалению, до сих пор не поддерживается в IE11 ... – Hank

+0

Если вы используете транспилер или находитесь в среде, которая его поддерживает, вы можете сделать то же самое более кратко: «var uniqueArr = [... new Set (arr)]; // ["a", "b"] ' – Stenerson

-1

Вот один вкладыш решение проблемы:

var seriesValues = [120, 120, 120, 120]; 
 
seriesValues = seriesValues.filter((value, index, seriesValues) => (seriesValues.slice(0, index)).indexOf(value) === -1); 
 
console.log(seriesValues);

Copy вставьте это в консоль браузеров и получите результаты, лет :-)

0

ES6 путь:

const uniq = (arr) => (arr.filter((item, index, arry) => (arry.indexOf(item) === index))); 
45

One Liner, Pure JavaScript

С ES6 синтаксисом

list = list.filter((x, i, a) => a.indexOf(x) == i)

x --> item in array 
i --> index of item 
a --> array reference, (in this case "list") 

enter image description here

С синтаксисом ES5

list = list.filter(function (x, i, a) { 
    return a.indexOf(x) == i; 
}); 

Browser Совместимость: IE9 +

+2

не знаю, почему это было отклонено. Сначала это может быть немного неясным, а _perhaps_ классифицировано как «умное», а не прагматичным для чтения, но оно декларативное, неразрушающее и краткое, в котором отсутствуют большинство других ответов. – Larry

+0

@ Larry это было отклонено, потому что точно такой же ответ был предоставлен за несколько лет до этого. –

+0

@AlexOkrushko достаточно честный - пропустил этот ответ из-за способа его форматирования – Larry

27

Вот гораздо чище решение ES6, что я вижу здесь не включен. Он использует Set и spread operator: ...

var a = [1, 1, 2]; 

[... new Set(a)] 

Который возвращает [1, 2]

+1

Thats so clever! –

+1

Теперь, ** ЭТО ** - однострочный! – Mac

+1

В машинописном тексте вы должны использовать 'Array.from (...new Set (a)) ', поскольку Set не может быть неявно преобразован в тип массива. Просто голова! – Zachscs

0

Я попробовал эту проблему в чистом JS. Я следовал следующие шаги: 1. Сортировка данного массива, 2. петлю через отсортированного массива, 3. Убедитесь, предыдущее значение и следующее значение с текущим значением

// JS 
var inpArr = [1, 5, 5, 4, 3, 3, 2, 2, 2,2, 100, 100, -1]; 

//sort the given array 
inpArr.sort(function(a, b){ 
    return a-b; 
}); 

var finalArr = []; 
//loop through the inpArr 
for(var i=0; i<inpArr.length; i++){ 
    //check previous and next value 
    if(inpArr[i-1]!=inpArr[i] && inpArr[i] != inpArr[i+1]){ 
     finalArr.push(inpArr[i]); 
    } 
} 
console.log(finalArr); 

Demo

3

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

Решение этой проблемы использует reduce и может выполнить эту работу в O (n) времени.

Array.prototype.unique = Array.prototype.unique || function() { 
 
     var arr = []; 
 
\t this.reduce(function (hash, num) { 
 
\t \t if(typeof hash[num] === 'undefined') { 
 
\t \t \t hash[num] = 1; 
 
\t \t \t arr.push(num); 
 
\t \t } 
 
\t \t return hash; 
 
\t }, {}); 
 
\t return arr; 
 
} 
 
    
 
var myArr = [3,1,2,3,3,3]; 
 
console.log(myArr.unique()); //[3,1,2];

Примечание:

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

0

Быстрый, компактный, без вложенных циклов, работает с любым объектом, а не только с строками и числами, берет предикат и всего 5 строк кода!

function findUnique(arr, predicate) { 
    var found = {}; 
    arr.forEach(d => { 
    found[predicate(d)] = d; 
    }); 
    return Object.keys(found).map(key => found[key]); 
} 

Пример: Для того, чтобы найти уникальные предметы по типу:

var things = [ 
    { name: 'charm', type: 'quark'}, 
    { name: 'strange', type: 'quark'}, 
    { name: 'proton', type: 'boson'}, 
]; 

var result = findUnique(things, d => d.type); 
// [ 
// { name: 'charm', type: 'quark'}, 
// { name: 'proton', type: 'boson'} 
// ] 

Если вы хотите, чтобы найти первый уникальный элемент вместо последнего добавления found.hasOwnPropery() проверить там.

3

Используя EcmaScript 2016, вы можете просто сделать это вот так.

var arr = ["a", "a", "b"]; 
var uniqueArray = Array.from(new Set(arr)); // Unique Array ['a', 'b']; 

Наборы всегда уникальны, и с помощью Array.from() вы можете преобразовать набор в массив. Для справки рассмотрим документацию.

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

0
function findUniques(arr){ 
    let uniques = [] 
    arr.forEach(n => { 
    if(!uniques.includes(n)){ 
     uniques.push(n) 
    }  
    }) 
    return uniques 
} 

let arr = ["3", "3", "4", "4", "4", "5", "7", "9", "b", "d", "e", "f", "h", "q", "r", "t", "t"] 

findUniques(arr) 
// ["3", "4", "5", "7", "9", "b", "d", "e", "f", "h", "q", "r", "t"] 
Смежные вопросы