Obtenga todos los valores únicos en una matriz de JavaScript (elimine duplicados)

1488

Tengo una serie de números que necesito para asegurarme de que sean únicos. Encontré el fragmento de código a continuación en Internet y funciona muy bien hasta que la matriz tiene un cero. Encontré este otro script aquí en Stack Overflow que se ve casi exactamente igual, pero no falla.

Entonces, en aras de ayudarme a aprender, ¿alguien puede ayudarme a determinar dónde va mal el script prototipo?

Array.prototype.getUnique = function() {
 var o = {}, a = [], i, e;
 for (i = 0; e = this[i]; i++) {o[e] = 1};
 for (e in o) {a.push (e)};
 return a;
}

Más respuestas de preguntas duplicadas:

Pregunta similar:

Mottie
fuente
3
@hippietrail Esa pregunta anterior se trata de encontrar y devolver solo los duplicados (¡también estaba confundido!). Mi pregunta es más acerca de por qué esta función falla cuando una matriz tiene un cero.
Mottie
Probablemente también desee que el título de su pregunta sea menos vago.
hippietrail
Para futuros lectores, cuando comience a descubrir que tiene que modificar algorítmicamente el contenido de su estructura de datos todo el tiempo (ordenarlos, eliminar elementos repetidos, etc.) o buscar elementos dentro de él en cada iteración, es seguro asumir que usted En primer lugar, utiliza una estructura de datos incorrecta y comienza a utilizar una que sea más apropiada para la tarea en cuestión (en este caso, un conjunto de hash en lugar de una matriz).
nurettin
Copié el código de otro lugar, hace mucho tiempo ... pero parece bastante sencillo: o= object, a= array, i= indexy e= umm, algo: P
Mottie
Posible duplicado de Cómo obtener valores únicos en una matriz
Adeel Imran

Respuestas:

2661

Con JavaScript 1.6 / ECMAScript 5 puede usar el filtermétodo nativo de una matriz de la siguiente manera para obtener una matriz con valores únicos:

function onlyUnique(value, index, self) { 
    return self.indexOf(value) === index;
}

// usage example:
var a = ['a', 1, 'a', 2, '1'];
var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']

El método nativo filterrecorrerá la matriz y dejará solo aquellas entradas que pasen la función de devolución de llamada dada onlyUnique.

onlyUniquecomprueba, si el valor dado es el primero que ocurre. De lo contrario, debe ser un duplicado y no se copiará.

Esta solución funciona sin ninguna biblioteca adicional como jQuery o prototype.js.

Funciona también para matrices con tipos de valores mixtos.

Para los navegadores antiguos (<ie9), que no admiten los métodos nativos filtery indexOfpuede encontrar soluciones alternativas en la documentación de MDN para filter e indexOf .

Si desea mantener la última aparición de un valor, simplemente reemplace indexOfpor lastIndexOf.

Con ES6 se podría acortar a esto:

// usage example:
var myArray = ['a', 1, 'a', 2, '1'];
var unique = myArray.filter((v, i, a) => a.indexOf(v) === i); 

// unique is ['a', 1, 2, '1']

Gracias a Camilo Martin por la pista en el comentario.

ES6 tiene un objeto nativo Setpara almacenar valores únicos. Para obtener una matriz con valores únicos, ahora puede hacer esto:

var myArray = ['a', 1, 'a', 2, '1'];

let unique = [...new Set(myArray)]; 

// unique is ['a', 1, 2, '1']

El constructor de Settoma un objeto iterable, como Array, y el operador de propagación ...transforma el conjunto nuevamente en un Array. Gracias a Lukas Liese por la pista en el comentario.

TLindig
fuente
55
Esta solución correrá mucho más lento, desafortunadamente. Estás dando vueltas dos veces, una con filtro y otra con índice de
Jack Franzen
19
@JackFranzen ¿Más lento que qué? ¿La solución de Rafael ? La solución de Rafaels no funciona para matrices de tipos mixtos. Para mi ejemplo ['a', 1, 'a', 2, '1'], obtendrías ['a', 1, 2]. Pero esto no es lo que esperaba. Por cierto, mucho más lento es muy relativo.
TLindig
25
En JS moderno: .filter((v,i,a)=>a.indexOf(v)==i)(notación de flecha gruesa).
Camilo Martin
164
let unique_values = [...new Set(random_array)]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Lukas Liesis
55
Para obtener una respuesta mucho más detallada, que incluye muchas posibilidades, como ordenar primero y tratar con diferentes tipos de datos, consulte stackoverflow.com/a/9229821/368896
Dan Nissenbaum el
877

Respuesta actualizada para ES6 / ES2015 : Usando el Set , la solución de línea única es:

var items = [4,5,4,6,3,4,5,2,23,1,4,4,4]
var uniqueItems = Array.from(new Set(items))

Que vuelve

[4, 5, 6, 3, 2, 23, 1]

Como le_m sugirió, esto también se puede acortar usando el operador de propagación , como

var uniqueItems = [...new Set(items)]
A
fuente
11
Tenga en cuenta que esa matriz interna no funcionaríaArray.from(new Set([[1,2],[1,2],[1,2,3]]))
Alexander Goncharov
3
Rendimiento de esta solución en comparación con myArray.filter((v, i, a) => a.indexOf(v) === i);?
Simoyw
43
Tenga en cuenta que si usa Sety agrega objetos en lugar de valores primitivos, contendrá referencias únicas a los objetos. Así, el conjunto sen let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]);volverá esto: Set { { Foo: 'Bar' }, { Foo: 'Bar' } }que es una Setcon referencias a objetos únicos a objetos que contienen los mismos valores. Si escribe let o = {Foo:"Bar"};y luego crea un conjunto con dos referencias como esta: let s2 = new Set([o,o]);entonces s2 seráSet { { Foo: 'Bar' } }
mortb
2
Ambas opciones tienen problemas en IE10 incluso con una capa polyfill.io presente
Thymine
2
nuevo caso de prueba jsperf.com/array-filter-unique-vs-new-set/1 parece new Setel trofeo
shuk
167

Me doy cuenta de que esta pregunta ya tiene más de 30 respuestas. Pero primero leí todas las respuestas existentes e hice mi propia investigación.

Dividí todas las respuestas a 4 posibles soluciones:

  1. Use la nueva función ES6: [...new Set( [1, 1, 2] )];
  2. Usar objeto { }para evitar duplicados
  3. Usar matriz auxiliar [ ]
  4. Utilizar filter + indexOf

Aquí hay ejemplos de códigos encontrados en las respuestas:

Use la nueva función ES6: [...new Set( [1, 1, 2] )];

function uniqueArray0(array) {
  var result = Array.from(new Set(array));
  return result    
}

Usar objeto { }para evitar duplicados

function uniqueArray1( ar ) {
  var j = {};

  ar.forEach( function(v) {
    j[v+ '::' + typeof v] = v;
  });

  return Object.keys(j).map(function(v){
    return j[v];
  });
} 

Usar matriz auxiliar [ ]

function uniqueArray2(arr) {
    var a = [];
    for (var i=0, l=arr.length; i<l; i++)
        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
            a.push(arr[i]);
    return a;
}

Utilizar filter + indexOf

function uniqueArray3(a) {
  function onlyUnique(value, index, self) { 
      return self.indexOf(value) === index;
  }

  // usage
  var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']

  return unique;
}

Y me preguntaba cuál es más rápido. Hice una muestra de Google Sheet para probar funciones. Nota: ECMA 6 no está disponible en Hojas de cálculo de Google, por lo que no puedo probarlo.

Aquí está el resultado de las pruebas: ingrese la descripción de la imagen aquí

Esperaba ver que el código que usa el objeto { }ganará porque usa hash. Así que me alegro de que las pruebas mostraron los mejores resultados para este algoritmo en Chrome e IE. Gracias a @rab por el código .

Max Makhrov
fuente
La opción "filter + indexOf" es extremadamente lenta en matrices de más de 100.000 elementos. Tuve que usar el enfoque de "mapa de objetos", sin embargo, rompe la clasificación original.
liberborn
¿El número más alto es más lento, no más rápido?
fletchsod
3
@ fletchsod, los números son el tiempo en ms para ejecutar el código.
Max Makhrov
1
Los números son wong! El script de aplicaciones de Google se ejecuta en su servidor, no en el navegador del cliente, y, por lo tanto, el rendimiento en Chrome / IE (o prácticamente cualquier navegador) será el mismo.
Jay Dadhania
1
Si bien es posible ejecutar JS del lado del cliente desde Google Script (ya sea a través de google.script.host o google.script.run , no estoy seguro de cuál), la respuesta indica claramente que "ECMA 6 no está disponible en Google Sheets" - por lo tanto Podemos suponer con seguridad que el póster usó Google Script del lado del servidor para esto, ¡y por lo tanto la respuesta muestra el rendimiento del servidor de Google, no del navegador!
Jay Dadhania
134

También puedes usar underscore.js .

console.log(_.uniq([1, 2, 1, 3, 1, 4]));
<script src="http://underscorejs.org/underscore-min.js"></script>

que devolverá:

[1, 2, 3, 4]
kornfridge
fuente
22
Por favor hagan esto amigos. No conecte algo al prototipo de matriz. Por favor.
Jacob Dalton el
55
@JacobDalton: esto no está ampliando el prototipo de matriz. Su espacio de nombres en el objeto _.
superluminary
25
@superluminary Sé que es por eso que dije por favor haz esto. La solución aceptada sugiere modificar el prototipo de matriz. No hagas eso.
Jacob Dalton
21
@JacobDalton Por favor, no hagas esto. No hay necesidad de agregar una biblioteca adicional solo para un pequeño trabajo que se puede hacer conarray = [...new Set(array)]
3
Esto derrotó el propósito de usar las funciones de ES y agregar más bibliotecas que solo hinchan más el sitio web.
fletchsod
68

One Liner, JavaScript puro

Con sintaxis 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")

ingrese la descripción de la imagen aquí

Con sintaxis ES5

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

Compatibilidad del navegador : IE9 +

Vamsi
fuente
14
@Spets Tiene un costo cuadrático, por lo que no es la mejor respuesta
Oriol
@Spets como todos los únicos / distintivos ... sí, sea inteligente, use radix para ordenar primero, sea más lento que 0 (n2) búsqueda rápida en la mayoría de los casos.
doker
¡gracias chicos! No me di cuenta cuando vi el código por primera vez, pero ahora es un poco más obvio.
Pasa el
51

Desde entonces he encontrado un buen método que usa jQuery

arr = $.grep(arr, function(v, k){
    return $.inArray(v ,arr) === k;
});

Nota: Este código fue retirado del puesto de perforación de pato de Paul Irish - Me olvidé de dar crédito: P

Mottie
fuente
8
Una solución concisa, pero llamar a Array es mucho menos eficiente que llamar a hasOwnProperty.
Señor Smith
Esto también es O (N ^ 2), ¿verdad? Mientras que el enfoque del diccionario o hasOwnProperty probablemente sea O (N * logN).
Speedplane
Esto funcionó para mí, todas las demás soluciones no eran compatibles con Internet Explorer.
kerl
51

La solución más corta con ES6: [...new Set( [1, 1, 2] )];

O si desea modificar el prototipo de matriz (como en la pregunta original):

Array.prototype.getUnique = function() {
    return [...new Set( [this] )];
};

Por el momento, EcmaScript 6 solo se implementa parcialmente en los navegadores modernos (agosto de 2015), pero Babel se ha vuelto muy popular por trasladar ES6 (e incluso ES7) a ES5. ¡De esa manera puedes escribir el código ES6 hoy!

Si se pregunta qué ...significa, se llama operador de propagación . Desde MDN : «El operador de propagación permite que una expresión se expanda en lugares donde se esperan múltiples argumentos (para llamadas a funciones) o múltiples elementos (para literales de matriz)». Debido a que un conjunto es iterable (y solo puede tener valores únicos), el operador de expansión expandirá el conjunto para llenar la matriz.

Recursos para aprender ES6:

Torsten Becker
fuente
3
puedes hacerlo aún más corto, a = [...Set(a)]pero, de todos modos, esto es solo Firefox, por ahora.
c69
@ c69, correcto, no se acortará más que eso. Los usuarios de SpiderMonkey también lo apreciarán.
Torsten Becker
Trabaja con Babel. Solo necesita incluir Array.from shim también:require ( "core-js/fn/array/from" );
Vladislav Rastrusny
debería ser Array.prototype.getUnique = function () {return [... new Set ([this])]; }; o, Array.prototype.getUnique = function () {return [... new Set (this)]; }; Lo apliqué en el siguiente- $ ('body'). Html (). ToLowerCase (). Match (/ ([a-zA-Z0-9 ._ + -] + @ [a-zA-Z0-9. _-] + \. [a-zA-Z0-9 ._-] +) / gi) .getUnique (); El primero me devolvió un valor único solo donde el segundo reajustó la eliminación de todos los duplicados.
ashique
[...Set(['a', 1, 'a', 2, '1'])]lanzará un TypeError, por lo que es prudente retener el new:[...new Set(['a', 1, 'a', 2, '1'])]
Adam Katz
36

La solución más simple:

var arr = [1, 3, 4, 1, 2, 1, 3, 3, 4, 1];
console.log([...new Set(arr)]);

O:

var arr = [1, 3, 4, 1, 2, 1, 3, 3, 4, 1];
console.log(Array.from(new Set(arr)));

Pedro L.
fuente
44
Tenga en cuenta que esto no es compatible con IE10 y versiones posteriores. Tenga en cuenta que IE11 + y Safari ofrecen soporte limitado (ni siquiera estoy seguro de que el ejemplo anterior funcione) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
oriadam
32

La forma más simple y rápida (en Chrome) de hacer esto:

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

Simplemente revisa todos los elementos de la matriz, comprueba si ese elemento ya está en la lista y, si no lo está, pasa a la matriz que se devuelve.

Según jsPerf, esta función es la más rápida de las que pude encontrar en cualquier lugar ; no obstante, siéntase libre de agregar la suya.

La versión no prototipo:

function uniques(arr) {
    var a = [];
    for (var i=0, l=arr.length; i<l; i++)
        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
            a.push(arr[i]);
    return a;
}

Clasificación

Cuando también se necesita ordenar la matriz, lo siguiente es lo más rápido:

Array.prototype.sortUnique = function() {
    this.sort();
    var last_i;
    for (var i=0;i<this.length;i++)
        if ((last_i = this.lastIndexOf(this[i])) !== i)
            this.splice(i+1, last_i-i);
    return this;
}

o no prototipo:

function sortUnique(arr) {
    arr.sort();
    var last_i;
    for (var i=0;i<arr.length;i++)
        if ((last_i = arr.lastIndexOf(arr[i])) !== i)
            arr.splice(i+1, last_i-i);
    return arr;
}

Esto también es más rápido que el método anterior en la mayoría de los navegadores que no son de Chrome.

Joeytje50
fuente
En Linux, Chrome 55.0.2883 prefiere su arr.unique () y arrclone2.sortFilter () de swilliams es el más lento (78% más lento). Sin embargo, Firefox 51.0.0 (con muchos complementos) tiene swilliams como el más rápido (pero aún más lento en Ops / seg que cualquier otro resultado de Chrome) con jQuery $ .grep (arr, jqFilter) de mottie más lento (46% más lento). Su arr.uniq () fue un 30% más lento. Ejecuté cada prueba dos veces y obtuve resultados consistentes. Rafael's arr.getUnique () obtuvo el segundo lugar en ambos navegadores.
Adam Katz
jsPerf tiene errores en este momento, por lo que mi edición de esta prueba no lo confirmó todo, pero resultó en la adición de dos pruebas: Cocco's toUnique () supera a Vamsi ES6 list.filter () en ambos navegadores, superando a sortFilter de swilliams () para el n. ° 1 en FF (sortFilter fue un 16% más lento) y superó sus pruebas ordenadas (que fue más lento en un 2%) para el n. ° 3 en Chrome.
Adam Katz
Ah, no me había dado cuenta de que esas pruebas eran trivialmente pequeñas y realmente no importan. Un comentario a la respuesta aceptada describe ese problema y ofrece una corrección en una revisión de la prueba, en la que el código de Rafael es fácilmente el más rápido y el código de arr.unique de Joetje50 es un 98% más lento. También hice otra revisión como se señala en este comentario .
Adam Katz
1
Bueno, en realidad el algoritmo que implementó en la uniquefunción tiene complejidad O (n ^ 2) mientras que el que getUniqueestá en es O (n). El primero puede ser más rápido en conjuntos de datos pequeños, pero ¿cómo puede discutir con las matemáticas :) Puede asegurarse de que el último sea más rápido si lo ejecuta en una matriz de, por ejemplo, 1e5 elementos únicos
Mikhail Dudin,
31

RENDIMIENTO SOLO! este código es probablemente 10 veces más rápido que todos los códigos aquí * funciona en todos los navegadores y también tiene el menor impacto en la memoria ... y más

si no necesita reutilizar la matriz anterior; por cierto, haga las otras operaciones necesarias antes de convertirla en única aquí, probablemente sea la forma más rápida de hacerlo, también muy breve.

var array=[1,2,3,4,5,6,7,8,9,0,1,2,1];

entonces puedes probar esto

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 1];

function toUnique(a, b, c) { //array,placeholder,placeholder
  b = a.length;
  while (c = --b)
    while (c--) a[b] !== a[c] || a.splice(c, 1);
  return a // not needed ;)
}
console.log(toUnique(array));
//[3, 4, 5, 6, 7, 8, 9, 0, 2, 1]

Se me ocurrió esta función leyendo este artículo ...

http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/

No me gusta el bucle for. tiene muchos parámetros. Me gusta el bucle while. while es el bucle más rápido en todos los navegadores, excepto el que a todos nos gusta tanto ... Chrome.

de todos modos, escribí la primera función que usa while. Y sí, es un poco más rápido que la función encontrada en el artículo. pero no lo suficiente.unique2()

El siguiente paso es usar js modernos. Object.keys Reemplacé el otro bucle for con Object.keys de js1.7 ... un poco más rápido y más corto (en Chrome 2x más rápido);). ¡No es suficiente!. unique3().

En este punto estaba pensando en lo que realmente necesito en MI función única. No necesito la matriz anterior, quiero una función rápida. así que usé 2 while loops + splice.unique4()

Inútil decir que estaba impresionado.

cromo: las 150,000 operaciones habituales por segundo aumentaron a 1,800,000 operaciones por segundo.

es decir: 80,000 op / s vs 3,500,000 op / s

ios: 18,000 op / s vs 170,000 op / s

safari: 80,000 op / s vs 6,000,000 op / s

Prueba http://jsperf.com/wgu o mejor usa console.time ... microtime ... lo que sea

unique5() es solo para mostrarle lo que sucede si desea mantener la matriz anterior.

No lo uses Array.prototypesi no sabes lo que estás haciendo. Acabo de hacer un montón de copia y pasado. Úselo Object.defineProperty(Array.prototype,...,writable:false,enumerable:false})si desea crear un prototipo nativo. Ejemplo: https://stackoverflow.com/a/20463021/2450730

Demo http://jsfiddle.net/46S7g/

NOTA: su matriz anterior se destruye / se vuelve única después de esta operación.

si no puede leer el código anterior, pregunte, lea un libro de JavaScript o aquí hay algunas explicaciones sobre el código más corto. https://stackoverflow.com/a/21353032/2450730

algunos están usando indexOf ... no ... http://jsperf.com/dgfgghfghfghghgfhgfhfghfhgfh

para matrices vacías

!array.length||toUnique(array); 
coco
fuente
44
probado en node.js, con una matriz de 100k de URL (cadenas). El resultado fue 2 veces más lento que el subrayado.js _.uniq ... aunque un jsperf separado está de acuerdo con usted ( jsperf.com/uniq-performance/5 ), estoy decepcionado :(
xShirase
3
no está probando correctamente en jsperf ... en su ejemplo, define la función cada vez ... pero las funciones de subrayado.js ya están definidas ... esto penaliza mi función. también prueba 3 y 4. Otra cosa que debería decir es que si usa variables mixtas (cadenas y números) debe reemplazar a [b]! == a [c] con a [b]! = a [c]
cocco
2
No estoy seguro de si jsPerf corrigió, pero parece que la alternativa Reducir (que, para mí es más fácil de entender) es un 94% más rápida que su solución. jsperf.com/reduce-for-distinct Editar: el tuyo es más lento en Chrome, el mismo en Edge y más rápido en Firefox.
Victor Ivens
3
Solo se puede usar con matrices pequeñas / porcentaje bajo de duplicados . Cada vez que se encuentre un objeto no único, el motor se verá obligado a desplazar todos los elementos restantes a la izquierda, al: 1) iterar sobre ellos 2) leerlos 3) escribirlos en una nueva ubicación. Y luego, también crea una nueva matriz con ese elemento empalmado, y lo devuelve como resultado ... El algoritmo se degrada rápidamente con matrices más grandes / duplicados más frecuentes. Para matrices de tamaño 1000-> 10000-> 50000 y ~ 40% de duplicados, el tiempo promedio tomado será de 2-> 775-> 19113 ms. (el tamaño cambia como 1-> x10-> x5, el tiempo como 1-> x10-> x25) en Chrome.
ankhzet
1
Gracias. Buen enfoque. Pero, ¿qué pasa con el pedido correcto de artículos?
liberborn
28

Muchas de las respuestas aquí pueden no ser útiles para principiantes. Si la eliminación de la duplicación de una matriz es difícil, ¿sabrán realmente sobre la cadena de prototipos, o incluso sobre jQuery?

En los navegadores modernos, una solución limpia y simple es almacenar datos en un Conjunto , que está diseñado para ser una lista de valores únicos.

const cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
const uniqueCars = Array.from(new Set(cars));

Esto Array.fromes útil para convertir el conjunto de nuevo a una matriz para que tenga fácil acceso a todos los métodos (características) increíbles que tienen las matrices. También hay otras formas de hacer lo mismo. Pero es posible que no necesite Array.fromnada, ya que los Sets tienen muchas características útiles como forEach .

Si necesita admitir Internet Explorer anterior y, por lo tanto, no puede usar Set, una técnica simple es copiar los elementos en una nueva matriz y verificar de antemano si ya están en la nueva matriz.

// Create a list of cars, with duplicates.
var cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
// Create a list of unique cars, to put a car in if we haven't already.
var uniqueCars = [];

// Go through each car, one at a time.
cars.forEach(function (car) {
    // The code within the following block runs only if the
    // current car does NOT exist in the uniqueCars list
    // - a.k.a. prevent duplicates
    if (uniqueCars.indexOf(car) === -1) {
        // Since we now know we haven't seen this car before,
        // copy it to the end of the uniqueCars list.
        uniqueCars.push(car);
    }
});

Para que esto sea reutilizable al instante, vamos a ponerlo en una función.

function deduplicate(data) {
    if (data.length > 0) {
        var result = [];

        data.forEach(function (elem) {
            if (result.indexOf(elem) === -1) {
                result.push(elem);
            }
        });

        return result;
    }
}

Entonces, para deshacernos de los duplicados, ahora haríamos esto.

var uniqueCars = deduplicate(cars);

La deduplicate(cars)parte se convierte en lo que llamamos resultado cuando se completa la función.

Simplemente pásele el nombre de cualquier matriz que desee.

Seth Holladay
fuente
Por cierto, usé una matriz llena de cadenas para mostrar que mi técnica es flexible. Funcionará correctamente para los números.
Seth Holladay
20
["Defects", "Total", "Days", "City", "Defects"].reduce(function(prev, cur) {
  return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
 }, []);

[0,1,2,0,3,2,1,5].reduce(function(prev, cur) {
  return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
 }, []);
sergeyz
fuente
¿Puedes explicar por qué no simplemente pushel elemento en la matriz en lugar de usar concat? Intenté usar push y falló. Estoy buscando una explicación.
makenova
1
El motivo está en el valor de retorno. concat devuelve la matriz modificada (exactamente lo que debe devolverse dentro de la función de reducción), mientras que push devuelve un índice en el que puede acceder al valor empujado. Eso responde tu pregunta?
sergeyz
Aparentemente, esta solución es bastante rápida en Chrome (y nodo). jsperf.com/reduce-for-distinct Esa es la versión ES6: [0,1,2,0,3,2,1,5].reduce((prev, cur) => ~prev.indexOf(cur) ? prev : prev.concat([cur]), []);
Victor Ivens
nota al margen: esta implementación no es NaNamigable
Alireza
19

Podemos hacer esto usando conjuntos ES6:

var duplicatedArray = [1, 2, 3, 4, 5, 1, 1, 1, 2, 3, 4];
var uniqueArray = Array.from(new Set(duplicatedArray));

console.log(uniqueArray);

// La salida será

uniqueArray = [1,2,3,4,5];
chinmayan
fuente
17

Este prototipo getUniqueno es totalmente correcto, porque si tengo una matriz como: ["1",1,2,3,4,1,"foo"]volverá ["1","2","3","4"]y "1"es una cadena y 1es un número entero; ellos son diferentes.

Aquí hay una solución correcta:

Array.prototype.unique = function(a){
    return function(){ return this.filter(a) }
}(function(a,b,c){ return c.indexOf(a,b+1) < 0 });

utilizando:

var foo;
foo = ["1",1,2,3,4,1,"foo"];
foo.unique();

Lo anterior producirá ["1",2,3,4,1,"foo"].

Gabriel Silveira
fuente
1
Tenga en cuenta que $foo = 'bar'es la forma PHP de declarar variables. Funcionará en JavaScript, pero creará un global implícito, y generalmente no debería hacerse.
Camilo Martin
@CamiloMartin lo siento pero te equivocas, $ foo es global porque el ejemplo no está cerrado y le falta la palabra clave var. Nada que ver con el dólar jsfiddle.net/robaldred/L2MRb
Rob
8
@Rob eso es exactamente lo que estoy diciendo, la gente de PHP pensará que $fooes la forma de declarar variables en JavaScript mientras que en realidad lo var fooes.
Camilo Martin
13

Sin extender Array.prototype (se dice que es una mala práctica) o usar jquery / subrayado, puede simplemente filterla matriz.

Al mantener la última ocurrencia:

    function arrayLastUnique(array) {
        return array.filter(function (a, b, c) {
            // keeps last occurrence
            return c.indexOf(a, b + 1) < 0;
        });
    },

o primera aparición:

    function arrayFirstUnique(array) {
        return array.filter(function (a, b, c) {
            // keeps first occurrence
            return c.indexOf(a) === b;
        });
    },

Bueno, es solo JavaScript ECMAScript 5+, lo que significa solo IE9 +, pero es bueno para un desarrollo en HTML / JS nativo (aplicación de la Tienda Windows, Firefox OS, Sencha, Phonegap, Titanium, ...).

Coeur
fuente
2
El hecho de que sea js 1.6 no significa que no puedas usarlo filter. En la página MDN tienen una implementación para Internet Explorer, quiero decir, navegadores más antiguos. Además: JS 1.6 se refiere solo al motor js de Firefox, pero lo correcto es decir que es ECMAScript 5.
Camilo Martin
@CamiloMartin Cambié 1.6 a ECMAScript5. Gracias.
Coeur
13

magia

a.filter(e=>!(t[e]=e in t)) 

O (n) rendimiento ; asumimos que su matriz está en ay t={}. Explicación aquí (+ Jeppe impr.)

Kamil Kiełczewski
fuente
14
este aspecto tan súper genial, que sin una explicación sólida caí que vas a extraer bitcoins cuando ejecuto esto
Ondřej Želazko
3
lo que quise decir es que debes expandir tu respuesta con alguna explicación y deconstrucción comentada de la misma. no esperes que la gente encuentre respuestas útiles como esta. (aunque realmente se ve genial, probablemente funcione)
Ondřej Želazko
1
@Jeppe cuando ejecuto su mejora, entonces experimento un efecto aha (antes de no saber que puedo usar el inoperador fuera de la otra construcción que no sea el forbucle: P) - Gracias - Lo aprecio y le daré +2 a sus otras buenas respuestas .
Kamil Kiełczewski
2
¿No crea eso una variable global tque se mantiene viva después del filtrado ... ??
philipp
1
Esta es realmente una solución difícil. Desafortunadamente, debe declarar t fuera de la expresión a menos que desee definir una variable global y / o confiar en su paquete de módulos para ocultar el global. Parece elegante, pero hazte un favor a ti mismo (y a tu equipo de desarrollo) y solo escribe dos líneas: deja t = {}; a.filter (e =>! (t [e] = e in t))
ford04
13
[...new Set(duplicates)]

Este es el más simple y referenciado de MDN Web Docs .

const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]) // [2, 3, 4, 5, 6, 7, 32]
ifelse.codes
fuente
Si bien este código puede resolver la pregunta, incluir una explicación de cómo y por qué esto resuelve el problema realmente ayudaría a mejorar la calidad de su publicación, y probablemente resultaría en más votos positivos. Recuerde que está respondiendo la pregunta para los lectores en el futuro, no solo la persona que pregunta ahora. Edite su respuesta para agregar una explicación e indique qué limitaciones y supuestos se aplican.
id.ot
11

Si está utilizando Prototype framework, no es necesario hacer bucles 'for', puede usar http://www.prototypejs.org/api/array/uniq de esta manera:

var a = Array.uniq();  

Lo que producirá una matriz duplicada sin duplicados. Encontré su pregunta buscando un método para contar distintos registros de matriz, así que después

uniq ()

solía

Talla()

y ahí estaba mi resultado simple. PD Lo siento si me equivoqué algo

editar: si desea escapar de registros indefinidos, puede agregar

compacto()

antes, así:

var a = Array.compact().uniq();  
Decebal
fuente
14
porque encontré una mejor respuesta, creo que los temas son para todas las personas, no solo para quienes preguntaron
diciembre
11

Ahora, usando conjuntos, puede eliminar duplicados y convertirlos nuevamente a la matriz.

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

console.log([...new Set(names)])

Otra solución es usar ordenar y filtrar

var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];
var namesSorted = names.sort();
const result = namesSorted.filter((e, i) => namesSorted[i] != namesSorted[i+1]);
console.log(result);

Krishnadas PC
fuente
10

Eso es porque 0es un valor falso en JavaScript.

this[i] será falso si el valor de la matriz es 0 o cualquier otro valor falso.

Luca Matteis
fuente
Ahhhh, ok ya veo ... pero ¿habría una solución fácil para que funcione?
Mottie
10
Array.prototype.getUnique = function() {
    var o = {}, a = []
    for (var i = 0; i < this.length; i++) o[this[i]] = 1
    for (var e in o) a.push(e)
    return a
}
efímero
fuente
Creo que esto no funcionará si la matriz contiene objetos / matrices, y no estoy seguro de si conservará el tipo de escalares.
Camilo Martin
Sí, todo se enrosca. Eso podría solucionarse almacenando el valor original en olugar de solo a 1, aunque la comparación de igualdad seguiría siendo secuencial (aunque, de todas las posibles igualdades de Javascript, no parece demasiado irrazonable).
Ephemient
El Array.prototype podría extenderse solo con métodos no enumerables ... Object.defineProperty (Array.prototype, "getUnique", {}) ... pero la idea de usar un objeto auxiliar es muy agradable
bortunac
10

Tuve un problema ligeramente diferente en el que necesitaba eliminar objetos con propiedades de identificación duplicadas de una matriz. Esto funcionó.

let objArr = [{
  id: '123'
}, {
  id: '123'
}, {
  id: '456'
}];

objArr = objArr.reduce((acc, cur) => [
  ...acc.filter((obj) => obj.id !== cur.id), cur
], []);

console.log(objArr);

shunryu111
fuente
9

La respuesta más simple es:

const array = [1, 1, 2, 2, 3, 5, 5, 2];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // [1, 2, 3, 5]
Sukanta Bala
fuente
Simple pero no funciona con una variedad de objetos.
Thanwa Ch.
7

No estoy seguro de por qué Gabriel Silveira escribió la función de esa manera, pero una forma más simple que funciona para mí igual de bien y sin la minificación es:

Array.prototype.unique = function() {
  return this.filter(function(value, index, array) {
    return array.indexOf(value, index + 1) < 0;
  });
};

o en CoffeeScript:

Array.prototype.unique = ->
  this.filter( (value, index, array) ->
    array.indexOf(value, index + 1) < 0
  )
Dan Fox
fuente
7

Si está de acuerdo con las dependencias adicionales, o si ya tiene una de las bibliotecas en su base de código, puede eliminar duplicados de una matriz en su lugar utilizando LoDash (o subrayado).

Uso

Si aún no lo tiene en su base de código, instálelo usando npm:

npm install lodash

Luego úsalo de la siguiente manera:

import _ from 'lodash';
let idArray = _.uniq ([
    1,
    2,
    3,
    3,
    3
]);
console.dir(idArray);

Fuera:

[ 1, 2, 3 ]
NikeshPathania
fuente
7

Esto ha sido respondido mucho, pero no atendió mi necesidad particular.

Muchas respuestas son así:

a.filter((item, pos, self) => self.indexOf(item) === pos);

Pero esto no funciona para matrices de objetos complejos.

Digamos que tenemos una matriz como esta:

const a = [
 { age: 4, name: 'fluffy' },
 { age: 5, name: 'spot' },
 { age: 2, name: 'fluffy' },
 { age: 3, name: 'toby' },
];

Si queremos los objetos con nombres únicos, deberíamos usar en array.prototype.findIndexlugar de array.prototype.indexOf:

a.filter((item, pos, self) => self.findIndex(v => v.name === item.name) === pos);
Dave
fuente
Gran solución, tenga cuidado con que una nueva matriz regresará de una función (no se modifica a sí mismo)
Thanwa Ch.
6

De Shamasis Bhattacharya el blog 's (complejidad O (2n)):

Array.prototype.unique = function() {
    var o = {}, i, l = this.length, r = [];
    for(i=0; i<l;i+=1) o[this[i]] = this[i];
    for(i in o) r.push(o[i]);
    return r;
};

Del blog de Paul Irish : mejora en JQuery .unique():

(function($){

    var _old = $.unique;

    $.unique = function(arr){

        // do the default behavior only if we got an array of elements
        if (!!arr[0].nodeType){
            return _old.apply(this,arguments);
        } else {
            // reduce the array to contain no dupes via grep/inArray
            return $.grep(arr,function(v,k){
                return $.inArray(v,arr) === k;
            });
        }
    };
})(jQuery);

// in use..
var arr = ['first',7,true,2,7,true,'last','last'];
$.unique(arr); // ["first", 7, true, 2, "last"]

var arr = [1,2,3,4,5,4,3,2,1];
$.unique(arr); // [1, 2, 3, 4, 5]
Z escarchado
fuente
6

Encontrar valores únicos de matriz en un método simple

function arrUnique(a){
  var t = [];
  for(var x = 0; x < a.length; x++){
    if(t.indexOf(a[x]) == -1)t.push(a[x]);
  }
  return t;
}
arrUnique([1,4,2,7,1,5,9,2,4,7,2]) // [1, 4, 2, 7, 5, 9]
Saravanan Rajaraman
fuente
6

Parece que hemos perdido la respuesta de Rafael , que se mantuvo como la respuesta aceptada durante algunos años. Esta fue (al menos en 2017) la solución de mejor rendimiento si no tiene una matriz de tipo mixto :

Array.prototype.getUnique = function(){
    var u = {}, a = [];
    for (var i = 0, l = this.length; i < l; ++i) {
        if (u.hasOwnProperty(this[i])) {
            continue;
        }
        a.push(this[i]);
        u[this[i]] = 1;
    }
return a;
}

Si hacer un arreglo de tipo mixto, puede serializar la tecla de almohadilla:

Array.prototype.getUnique = function() {
    var hash = {}, result = [], key; 
    for ( var i = 0, l = this.length; i < l; ++i ) {
        key = JSON.stringify(this[i]);
        if ( !hash.hasOwnProperty(key) ) {
            hash[key] = true;
            result.push(this[i]);
        }
    }
    return result;
}
Jason
fuente
5

Para solucionar el problema al revés, puede ser útil no tener duplicados mientras carga su matriz, de la forma en que lo haría el objeto Set , pero aún no está disponible en todos los navegadores. Ahorra memoria y es más eficiente si necesita ver su contenido muchas veces.

Array.prototype.add = function (elem) {
   if (this.indexOf(elem) == -1) {
      this.push(elem);
   }
}

Muestra:

set = [];
[1,3,4,1,2,1,3,3,4,1].forEach(function(x) { set.add(x); });

Te dio set = [1,3,4,2]

Le Droid
fuente