Come ottenere valori distinti da una serie di oggetti in JavaScript?

Supponendo che ho il seguente:

var array = [ {"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35} ] 

Qual è il modo migliore per essere in grado di ottenere una matrice di tutte le età distinte in modo tale da ottenere una matrice di risultati di:

 [17, 35] 

C’è un modo in cui potrei alternativamente strutturare i dati o un metodo migliore in modo tale che non dovrei iterare attraverso ogni array controllando il valore di “age” e confrontarlo con un altro array per la sua esistenza, e aggiungerlo se non?

Se ci fosse un modo in cui avrei potuto tirare fuori le epoche distinte senza iterare …

Attuale modo inefficiente che vorrei migliorare … Se significa che invece di “array” è una matrice di oggetti, ma una “mappa” di oggetti con qualche chiave univoca (cioè “1,2,3”) che sarebbe va bene anche Sto solo cercando il modo più performante.

Quello che segue è come lo faccio al momento, ma per me, l’iterazione sembra essere scadente per efficienza anche se funziona …

 var distinct = [] for (var i = 0; i < array.length; i++) if (array[i].age not in distinct) distinct.push(array[i].age) 

    Se questo fosse PHP, avrei costruito un array con le chiavi e preso array_keys alla fine, ma JS non ha un tale lusso. Invece, prova questo:

     var flags = [], output = [], l = array.length, i; for( i=0; i 

    Se si utilizza ES6 / ES2015 o versione successiva, è ansible farlo in questo modo:

     const unique = [...new Set(array.map(item => item.age))]; 

    Ecco un esempio su come farlo.

    Potresti usare un approccio al dizionario come questo. Fondamentalmente si assegna il valore che si desidera essere distinti come chiave in un dizionario. Se la chiave non esiste, si aggiunge quel valore come distinto.

     var unique = {}; var distinct = []; for( var i in array ){ if( typeof(unique[array[i].age]) == "undefined"){ distinct.push(array[i].age); } unique[array[i].age] = 0; } 

    Ecco una demo funzionante: http://jsfiddle.net/jbUKP/1

    Questo sarà O (n) dove n è il numero di oggetti nell’array e m è il numero di valori univoci. Non esiste un modo più veloce di O (n) perché è necessario esaminare ogni valore almeno una volta.

    Prestazione

    http://jsperf.com/filter-versus-dictionary Quando ho eseguito questo dizionario era il 30% più veloce.

    usando ES6

     let array = [ { "name": "Joe", "age": 17 }, { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 } ]; array.map(item => item.age) .filter((value, index, self) => self.indexOf(value) === index) > [17, 35] 

    Vorrei solo mappare e rimuovere i duplicati:

     var ages = array.map(function(obj) { return obj.age; }); ages = ages.filter(function(v,i) { return ages.indexOf(v) == i; }); console.log(ages); //=> [17, 35] 

    Modifica: Aight! Non il modo più efficiente in termini di prestazioni, ma l’IMO più semplice e leggibile. Se ti interessa davvero la micro-ottimizzazione o hai enormi quantità di dati, allora un ciclo regolare for ciclo sarà più “efficiente”.

    Usando le funzionalità di ES6, potresti fare qualcosa come:

     const uniqueAges = [...new Set( array.map(obj => obj.age)) ]; 

    questo è il modo in cui dovresti risolvere questo problema utilizzando il nuovo Set tramite ES6 per Typescript dal 25 agosto 2017

     Array.from(new Set(yourArray.map((item: any) => item.id))) 

    La versione forEach della risposta di @ travis-j (utile sui browser moderni e sul mondo Node JS):

     var unique = {}; var distinct = []; array.forEach(function (x) { if (!unique[x.age]) { distinct.push(x.age); unique[x.age] = true; } }); 

    34% più veloce su Chrome v29.0.1547: http://jsperf.com/filter-versus-dictionary/3

    E una soluzione generica che accetta una funzione di mapping (un po ‘più lento della mappa diretta, ma è previsto):

     function uniqueBy(arr, fn) { var unique = {}; var distinct = []; arr.forEach(function (x) { var key = fn(x); if (!unique[key]) { distinct.push(key); unique[key] = true; } }); return distinct; } // usage uniqueBy(array, function(x){return x.age;}); // outputs [17, 35] 

    Ho iniziato ad attaccare Underscore in tutti i nuovi progetti di default solo così non dovrei mai pensare a questi piccoli problemi di mungitura dei dati.

     var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}]; console.log(_.chain(array).map(function(item) { return item.age }).uniq().value()); 

    Produce [17, 35] .

    usando il lodash

     var array = [ { "name": "Joe", "age": 17 }, { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 } ]; _.chain(array).pluck('age').unique().value(); > [17, 35] 

    Ecco un altro modo per risolvere questo:

     var result = {}; for(var i in array) { result[array[i].age] = null; } result = Object.keys(result); 

    Non ho idea di quanto sia veloce questa soluzione rispetto alle altre, ma mi piace l’aspetto più pulito. 😉


    EDIT: Ok, quanto sopra sembra essere la soluzione più lenta di tutti qui.

    Ho creato un caso di test delle prestazioni qui: http://jsperf.com/distinct-values-from-array

    Invece di testare per età (Interi), ho scelto di confrontare i nomi (stringhe).

    Il Metodo 1 (la soluzione di TS) è molto veloce. È interessante notare che il Metodo 7 supera tutte le altre soluzioni, qui mi sono appena sbarazzato di .indexOf () e ho usato un’implementazione “manuale” di esso, evitando la chiamata di funzioni in loop:

     var result = []; loop1: for (var i = 0; i < array.length; i++) { var name = array[i].name; for (var i2 = 0; i2 < result.length; i2++) { if (result[i2] == name) { continue loop1; } } result.push(name); } 

    La differenza di prestazioni con Safari e Firefox è sorprendente e sembra che Chrome abbia il miglior risultato sull'ottimizzazione.

    Non sono esattamente sicuro del motivo per cui i frammenti di cui sopra siano così veloci rispetto agli altri, forse qualcuno più saggio di me ha una risposta. 😉

    underscore.js _.uniq(_.pluck(array,"age"))

    Usando Lodash

     var array = [ { "name": "Joe", "age": 17 }, { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 } ]; _.chain(array).map('age').unique().value(); 

    Restituisce [17,35]

     function get_unique_values_from_array_object(array,property){ var unique = {}; var distinct = []; for( var i in array ){ if( typeof(unique[array[i][property]]) == "undefined"){ distinct.push(array[i]); } unique[array[i][property]] = 0; } return distinct; } 

    penso che tu stia cercando la funzione groupBy (usando Lodash)

     _personsList = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}]; _uniqAgeList = _.groupBy(_personsList,"age"); _uniqAges = Object.keys(_uniqAgeList); 

    produce risultato:

     17,35 

    demo di jsFiddle: http://jsfiddle.net/4J2SX/201/

    Ho appena trovato questo e ho pensato che fosse utile

     _.map(_.indexBy(records, '_id'), function(obj){return obj}) 

    Ancora usando il carattere di sottolineatura , quindi se hai un object come questo

     var records = [{_id:1,name:'one', _id:2,name:'two', _id:1,name:'one'}] 

    ti darà solo gli oggetti unici.

    Quello che succede qui è che indexBy restituisce una mappa come questa

     { 1:{_id:1,name:'one'}, 2:{_id:2,name:'two'} } 

    e solo perché è una mappa, tutti i tasti sono unici.

    Quindi sto solo mappando questa lista di nuovo alla matrice.

    Nel caso in cui hai bisogno solo dei valori distinti

     _.map(_.indexBy(records, '_id'), function(obj,key){return key}) 

    Tieni presente che la key viene restituita come una stringa, quindi, se invece hai bisogno di numeri interi, dovresti farlo

     _.map(_.indexBy(records, '_id'), function(obj,key){return parseInt(key)}) 

    Se hai Array.prototype.includes o sei disposto a eseguire il polyfill , questo funziona:

     var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.push(x.age); }); 

    Se come me preferisci un aspetto più “funzionale” senza compromettere la velocità, in questo esempio viene utilizzata una rapida ricerca del dizionario avvolta all’interno per ridurre la chiusura.

     var array = [ {"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35} ] var uniqueAges = array.reduce((p,c,i,a) => { if(!p[0][c.age]) { p[1].push(p[0][c.age] = c.age); } if(i 

    Secondo questo test la mia soluzione è due volte più veloce della risposta proposta

    Ecco una soluzione versatile che utilizza la riduzione, consente la mapping e mantiene l’ordine di inserimento.

    elementi : un array

    mappatore : una funzione unaria che associa l’elemento ai criteri o vuoto per mappare l’elemento stesso.

     function distinct(items, mapper) { if (!mapper) mapper = (item)=>item; return items.map(mapper).reduce((acc, item) => { if (acc.indexOf(item) === -1) acc.push(item); return acc; }, []); } 

    uso

     const distinctLastNames = distinct(items, (item)=>item.lastName); const distinctItems = distinct(items); 

    Puoi aggiungere questo al tuo prototipo di Array e lasciare fuori il parametro items se questo è il tuo stile …

     const distinctLastNames = items.distinct( (item)=>item.lastName) ) ; const distinctItems = items.distinct() ; 

    Puoi anche usare un Set invece di una Matrice per accelerare la corrispondenza.

     function distinct(items, mapper) { if (!mapper) mapper = (item)=>item; return items.map(mapper).reduce((acc, item) => { acc.add(item); return acc; }, new Set()); } 

    Esistono già molte risposte valide, ma volevo aggiungerne una che utilizza solo il metodo reduce() perché è semplice e pulita.

     function uniqueBy(arr, prop){ return arr.reduce((a, d) => { if (!a.includes(d[prop])) a.push(d[prop]); return a; }, []); } 

    Usalo in questo modo:

     var array = [ {"name": "Joe", "age": 17}, {"name": "Bob", "age": 17}, {"name": "Carl", "age": 35} ]; var ages = uniqueBy(array, "age"); console.log(ages); // [17, 35] 

    i miei due centesimi su questa funzione:

     var result = []; for (var len = array.length, i = 0; i < len; ++i) { var age = array[i].age; if (result.indexOf(age) > -1) continue; result.push(age); } 

    Puoi vedere il risultato qui (metodo 8) http://jsperf.com/distinct-values-from-array/3

    L’utilizzo delle nuove funzionalità di Ecma è ottimo, ma non tutti gli utenti ne hanno ancora disponibili.

    Il codice seguente allegherà una nuova funzione denominata distinta all’object Global Array. Se stai provando a ottenere valori distinti di una matrice di oggetti, puoi passare il nome del valore per ottenere i valori distinti di quel tipo.

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

    Controlla il mio post in CodePen per una demo.

    Prova

     var x = [] ; for (var i = 0 ; i < array.length ; i++) { if(x.indexOf(array[i]['age']) == -1) { x.push(array[i]['age']); } } console.log(x); 

    utilizzando d3.js v3 :

      ages = d3.set( array.map(function (d) { return d.age; }) ).values(); 
     unique(obj, prop) { let result = []; let seen = new Set(); Object.keys(obj) .forEach((key) => { let value = obj[key]; let test = !prop ? value : value[prop]; !seen.has(test) && seen.add(test) && result.push(value); }); return result; } 

    questa funzione può un array e un object unici

     function oaunic(x,n=0){ if(n==0) n = "elem"; else n = "elem."+n; var uval = []; var unic = x.filter(function(elem, index, self){ if(uval.indexOf(eval(n)) < 0){ uval.push(eval(n)); return index == self.indexOf(elem); } }) return unic; } 

    usa questo come questo

     tags_obj = [{name:"milad"},{name:"maziar"},{name:"maziar"}] tags_arr = ["milad","maziar","maziar"] console.log(oaunic(tags_obj,"name")) //for object console.log(oaunic(tags_arr)) //for array