it-swarm.com.de

Wie erhält man unterschiedliche Werte aus einem Array von Objekten in JavaScript?

Angenommen, ich habe folgendes:

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

Was ist der beste Weg, um ein Array aller verschiedenen Altersstufen zu erhalten, so dass ich ein Ergebnis-Array mit:

[17, 35]

Gibt es eine Möglichkeit, die Daten oder eine bessere Methode alternativ so zu strukturieren, dass ich nicht jedes Array durchlaufen muss, um den Wert von "age" zu überprüfen und mit einem anderen Array auf seine Existenz zu prüfen und es hinzuzufügen, wenn nicht?

Wenn es einen Weg gäbe, könnte ich einfach die verschiedenen Zeitalter herausziehen, ohne zu iterieren ...

Aktuelle unwirksame Art und Weise möchte ich verbessern ... Wenn es bedeutet, dass "Array" nicht ein Array von Objekten ist, sondern eine "Karte" von Objekten mit einem eindeutigen Schlüssel (dh "1,2,3") ok auch Ich bin nur auf der Suche nach dem leistungsfähigsten Weg.

Das Folgende ist, wie ich es momentan mache, aber für mich scheint die Iteration aus Effizienzgründen nur mürrisch zu sein, obwohl es funktioniert ...

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

Wenn dies PHP wäre, würde ich ein Array mit den Schlüsseln erstellen und am Ende array_keys nehmen, aber JS hat keinen solchen Luxus. Versuchen Sie stattdessen Folgendes:

var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
    if( flags[array[i].age]) continue;
    flags[array[i].age] = true;
    output.Push(array[i].age);
}
99

Wenn Sie ES6/ES2015 oder höher verwenden, können Sie dies folgendermaßen tun:

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

Hier ist ein Beispiel, wie es geht.

357
Vlad Bezden

Sie können einen Wörterbuchansatz wie diesen verwenden. Grundsätzlich weisen Sie den zu unterscheidenden Wert als Schlüssel in einem Wörterbuch zu. Wenn der Schlüssel nicht vorhanden ist, fügen Sie diesen Wert als eindeutig hinzu.

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

Hier ist eine funktionierende Demo: http://jsfiddle.net/jbUKP/1

Dies ist O(n), wobei n die Anzahl der Objekte im Array und m die Anzahl der eindeutigen Werte ist. Es gibt keinen schnelleren Weg als O(n), da Sie jeden Wert mindestens einmal überprüfen müssen.

Leistung

http://jsperf.com/filter-versus-dictionary Als ich dieses Wörterbuch ausgeführt habe, war es 30% schneller.

102
Travis J

mit 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]
89
Ivan Nosov

Ich würde einfach Dups zuordnen und entfernen:

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]

Editieren: Aight! Nicht der effizienteste Weg in Bezug auf die Leistung, aber der einfachste und lesbarste IMO. Wenn Sie sich wirklich für die Mikrooptimierung interessieren oder große Datenmengen haben, wird eine reguläre for-Schleife "effizienter" sein.

51
elclanrs

so können Sie dies mit dem neuen Set über ES6 für TypeScript ab dem 25. August 2017 lösen

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

Mit den ES6-Funktionen können Sie Folgendes tun:

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

Die forEach-Version von @ travis-js Antwort (hilfreich bei modernen Browsern und Node JS World):

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

34% schneller unter Chrome v29.0.1547: http://jsperf.com/filter-versus-dictionary/3

Und eine generische Lösung, die eine Mapper-Funktion übernimmt (etwas langsamer als eine direkte Karte, aber das ist zu erwarten):

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]
17
Mrchief

Ich habe angefangen, Underscore standardmäßig in allen neuen Projekten zu verwenden, nur damit ich nie über diese kleinen Datenprobleme nachdenken muss.

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

Erzeugt [17, 35].

13
Eevee

mit lodash

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

Hier ist ein anderer Weg, um das zu lösen:

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

Ich habe keine Ahnung, wie schnell diese Lösung im Vergleich zu den anderen ist, aber ich mag den saubereren Look. ;-) 


EDIT: Okay, das Obige scheint hier die langsamste Lösung zu sein. 

Ich habe hier einen Performance-Testfall erstellt: http://jsperf.com/distinct-values-from-array

Anstatt auf Alter (Integer) zu testen, habe ich mich entschieden, die Namen (Strings) zu vergleichen. 

Methode 1 (TS-Lösung) ist sehr schnell. Interessanterweise übertrifft Methode 7 alle anderen Lösungen. Hier habe ich gerade .indexOf () losgelassen und eine "manuelle" Implementierung davon verwendet.

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

Der Unterschied in der Leistung bei der Verwendung von Safari und Firefox ist erstaunlich, und es scheint, als würde Chrome die Optimierung optimieren.

Ich bin mir nicht ganz sicher, warum die obigen Schnipsel im Vergleich zu den anderen so schnell sind, vielleicht hat jemand, der klüger ist als ich, eine Antwort. ;-)

7
BaM

Es gibt bereits viele gültige Antworten, aber ich wollte eine hinzufügen, die nur die reduce()-Methode verwendet, da sie sauber und einfach ist.

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

Verwenden Sie es so:

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

var ages = uniqueBy(array, "age");
console.log(ages); // [17, 35]
6
Harry Stevens
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;
}
5

Lodash verwenden

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

_.chain(array).map('age').unique().value();

Rückgabe [17,35]

5
Yash Vekaria

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

ich denke, Sie suchen eine GroupBy-Funktion (mit Lodash)

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

produziert das Ergebnis:

17,35

jsFiddle Demo: http://jsfiddle.net/4J2SX/201/

4
Amil Sajeev
var unique = array
    .map(p => p.age)
    .filter((age, index, arr) => arr.indexOf(age) == index)
    .sort(); // sorting is optional

// or in ES6

var unique = [...new Set(array.map(p => p.age))];
4
oleg gabureac

Wenn Sie Array.prototype.includes haben oder bereit sind, polyfill es zu verwenden, funktioniert dies:

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

Ich habe das gerade gefunden und ich dachte, es wäre nützlich

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

Wieder mit Unterstrich , wenn Sie ein solches Objekt haben

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

sie erhalten nur die eindeutigen Objekte.

Was hier passiert ist, dass indexBy eine Karte wie diese zurückgibt

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

und nur weil es sich um eine Karte handelt, sind alle Schlüssel eindeutig.

Dann ordne ich diese Liste nur wieder dem Array zu.

Falls Sie nur die unterschiedlichen Werte benötigen

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

Denken Sie daran, dass die Variable key als String zurückgegeben wird. Wenn Sie stattdessen ganze Zahlen benötigen, sollten Sie dies tun

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

Wenn Sie, wie ich, eine "funktionellere" Variante bevorzugen, ohne die Geschwindigkeit zu beeinträchtigen, wird in diesem Beispiel die schnelle Suche nach Wörterbüchern verwendet, die in die Funktion "Reduzieren" eingebunden ist.

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<a.length-1) {
        return p
    } else {
        return p[1]
    }
}, [{},[]])

Demnach ist test meine Lösung doppelt so schnell wie die vorgeschlagene Antwort

1
bmaggi

Hier ist eine vielseitige Lösung, die die Funktionen zum Reduzieren, Mappen und Einfügen der Reihenfolge verwendet.

items : Ein Array

mapper : Eine unäre Funktion, die den Artikel den Kriterien zuordnet, oder leer, um den Artikel selbst zuzuordnen.

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;
    }, []);
}

Verwendungszweck

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

Sie können dies zu Ihrem Array-Prototyp hinzufügen und den Parameter items auslassen, wenn dies Ihr Stil ist ...

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

Sie können auch ein Set anstelle eines Arrays verwenden, um den Abgleich zu beschleunigen.

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        acc.add(item);
        return acc;
    }, new Set());
}
1
Steven Spungin
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;
}
1
Devonte
const x = [
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"},
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"}
].reduce(
  (accumulator, current) => accumulator.some(x => x.id === current.id)? accumulator: [...accumulator, current ], []
)

console.log(x)

/* output 
[ 
  { id: '93', name: 'CVAM_NGP_KW' },
  { id: '94', name: 'CVAM_NGP_PB' } 
]
*/
1

Mein unten stehender Code zeigt das eindeutige Feld Alter sowie das neue Feld, das kein doppeltes Alter hat

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

var unique = [];
var tempArr = [];
data.forEach((value, index) => {
    if (unique.indexOf(value.age) === -1) {
        unique.Push(value.age);
    } else {
        tempArr.Push(index);    
    }
});
tempArr.reverse();
tempArr.forEach(ele => {
    data.splice(ele, 1);
});
console.log('Unique Ages', unique);
console.log('Unique Array', data);```
1
Shridhar Sagari

Einfacher Einzeiler mit hervorragender Leistung. 6% schneller als die ES6-Lösungen in meinen Tests .

var age = array.map (Funktion (o) {return o.age}). filter (Funktion (v, i, a) {return a.indexOf (v) === i});

0
Craig

Versuch's einfach

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);
0
Pravin Pareek

Ich weiß, dass dies eine alte und relativ gut beantwortete Frage ist, und die Antwort, die ich gebe, wird das vollständige Objekt zurückbekommen (was ich in vielen Kommentaren zu diesem Beitrag vorgeschlagen sehe). Es mag "klebrig" sein, aber in Bezug auf die Lesbarkeit scheint es viel sauberer (wenn auch weniger effizient) zu sein als viele andere Lösungen.

Dadurch wird ein eindeutiges Array der vollständigen Objekte innerhalb des Arrays zurückgegeben.

let productIds = data.map(d => { 
   return JSON.stringify({ 
      id    : d.sku.product.productId,
      name  : d.sku.product.name,
      price : `${d.sku.product.price.currency} ${(d.sku.product.price.gross / d.sku.product.price.divisor).toFixed(2)}`
   })
})
productIds = [ ...new Set(productIds)].map(d => JSON.parse(d))```
0
Conor Reid

Es gibt eine Bibliothek, die stark typisierte, abfragbare Sammlungen in TypeScript bereitstellt.

Die Sammlungen sind:

  • Liste
  • Wörterbuch

Die Bibliothek heißt ts-generic-collections .

Quellcode auf GitHub:

https://github.com/VeritasSoftware/ts-generic-collections

Sie können unterschiedliche Werte wie unten erhalten

  it('distinct', () => {
    let numbers: number[] = [1, 2, 3, 1, 3];
    let list = new List(numbers);

    let distinct = list.distinct(new EqualityComparer());

    expect(distinct.length == 3);
    expect(distinct.elementAt(0) == 1);
    expect(distinct.elementAt(1) == 2);
    expect(distinct.elementAt(2) == 3);
  });

  class EqualityComparer implements IEqualityComparer<number> {
    equals(x: number, y: number) : boolean {
      return x == y;
    }
  }
0
John

Die Verwendung der neuen Ecma-Funktionen ist großartig, aber noch haben nicht alle Benutzer diese. 

Mit dem folgenden Code wird eine neue Funktion mit dem Namen distinct an das Global Array-Objekt angehängt .. Wenn Sie versuchen, unterschiedliche Werte eines Arrays von Objekten zu erhalten, können Sie den Namen des Werts übergeben, um die unterschiedlichen Werte davon zu erhalten Art. 

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

Check out mein Post in CodePen für eine Demo.

0
Hasan Savran

Ich habe mein eigenes in TypeScript für einen generischen Fall geschrieben, wie das in Kotlins Array.distinctBy {}...

function distinctBy<T, U extends string | number>(array: T[], mapFn: (el: T) => U) {
  const uniqueKeys = new Set(array.map(mapFn));
  return array.filter((el) => uniqueKeys.has(mapFn(el)));
}

Wobei U natürlich hashbar ist. Für Objekte benötigen Sie möglicherweise https://www.npmjs.com/package/es6-json-stable-stringify

0
Polv