Extrahieren Sie aus einem Array von Objekten den Wert einer Eigenschaft als Array

1019

Ich habe ein JavaScript-Objektarray mit der folgenden Struktur:

objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];

Ich möchte aus jedem Objekt ein Feld extrahieren und ein Array mit den Werten abrufen, z. B. Feld foo würde ergeben [ 1, 3, 5 ].

Ich kann dies mit diesem trivialen Ansatz tun:

function getFields(input, field) {
    var output = [];
    for (var i=0; i < input.length ; ++i)
        output.push(input[i][field]);
    return output;
}

var result = getFields(objArray, "foo"); // returns [ 1, 3, 5 ]

Gibt es eine elegantere oder idiomatischere Möglichkeit, dies zu tun, sodass eine benutzerdefinierte Dienstprogrammfunktion nicht erforderlich wäre?


Hinweis zum vorgeschlagenen Duplikat. Hier erfahren Sie, wie Sie ein einzelnes Objekt in ein Array konvertieren .

Hyde
quelle
4
Die Prototyp-Bibliothek hat dem Array-Prototyp (glaube ich) eine "Zupf" -Funktion hinzugefügt, damit Sie schreiben können var foos = objArray.pluck("foo");.
Pointy
3
@hyde - jsperf.com/map-vs-native-for-loop - bitte schauen Sie sich das an, hoffen Sie einfach, sich eine gute Lösung zu
schleifen
1
@ N20084753 für einen fairen Test sollten Sie auch die native Array.prototype.mapFunktion vergleichen, wo es existiert
Alnitak
@Alnitak - ya Sie haben Recht, aber ich kann nicht verstehen, wie die native Array.prototype.map als native for-Schleife optimiert ist, wie auch immer wir das gesamte Array durchlaufen müssen.
N20084753
3
OP, ich ziehe Ihren Ansatz allen anderen vor, die vorgeschlagen wurden. Daran ist nichts auszusetzen.

Antworten:

1336

Hier ist ein kürzerer Weg, um dies zu erreichen:

let result = objArray.map(a => a.foo);

ODER

let result = objArray.map(({ foo }) => foo)

Sie können auch überprüfen Array.prototype.map().

Jalasem
quelle
3
Nun, dies ist dasselbe wie der Kommentar einer anderen Antwort von totymedli, aber trotzdem ist es (meiner Meinung nach) tatsächlich besser als bei anderen Antworten , also ... Ändern Sie es in eine akzeptierte Antwort.
Hyde
4
@PauloRoberto Pfeilfunktionen werden grundsätzlich überall außer im IE unterstützt.
Tgies
1
Sicher, es ist erlaubt, aber meiner Meinung nach gibt es nichts, was diese Antwort objektiv besser macht, außer dass sie eine Syntax verwendet, die zum Zeitpunkt der Frage nicht verfügbar war und in einigen Browsern nicht einmal unterstützt wird. Ich möchte auch darauf hinweisen, dass diese Antwort eine direkte Kopie der Kommentare ist, die fast ein Jahr vor der Veröffentlichung dieser Antwort zu der ursprünglich akzeptierten Antwort abgegeben wurden.
Alnitak
26
@Alnitak neuere Funktionalität verwenden, in meiner Sicht, ist objektiv besser. Dieser Ausschnitt ist sehr verbreitet, daher bin ich nicht davon überzeugt, dass dies Plagiat ist. Es ist nicht wirklich wertvoll, veraltete Antworten an der Spitze zu halten.
Rob
6
Kommentare sind keine Antworten. Wenn jemand einen Kommentar anstelle einer Antwort veröffentlicht, ist es seine eigene Schuld, wenn jemand ihn in eine Antwort kopiert.
Aequitas
619

Ja, aber es basiert auf einer ES5-Funktion von JavaScript. Dies bedeutet, dass es in IE8 oder älter nicht funktioniert.

var result = objArray.map(function(a) {return a.foo;});

Auf ES6 kompatibel JS Dolmetscher können Sie einen verwenden Pfeil Funktion der Kürze:

var result = objArray.map(a => a.foo);

Array.prototype.map-Dokumentation

Niet the Dark Absol
quelle
5
Diese Lösung scheint ordentlich und einfach zu sein, ist jedoch optimiert als die einfache Schleifenmethode.
N20084753
Will das OP nicht, dass eine Methode ein Feld erhält , das nicht nur fest codiert ist foo?
Alnitak
2
in ES6 können Sie tunvar result = objArray.map((a) => (a.foo));
Black
28
@ Black Noch besser:var result = objArray.map(a => a.foo);
Totymedli
4
@FaizKhan Beachten Sie das Jahr. Diese Antwort wurde am 25. Oktober 2013 veröffentlicht. Die "akzeptierte" Antwort wurde am 11. Oktober 2017 veröffentlicht. Wenn überhaupt, ist es bemerkenswert, dass das Häkchen zum anderen ging.
Niet the Dark Absol
52

Schauen Sie sich die_.pluck() Funktion von Lodash oder Underscore an_.pluck() . Beide machen in einem einzigen Funktionsaufruf genau das, was Sie wollen!

var result = _.pluck(objArray, 'foo');

Update: _.pluck()wurde ab Lodash v4.0.0 entfernt , zugunsten von _.map()in Kombination mit etwas ähnlichem wie Niets Antwort . _.pluck()ist weiterhin in Unterstrich verfügbar .

Update 2: Wie Mark betont in den Kommentaren hervorhebt, wurde irgendwo zwischen Lodash v4 und 4.3 eine neue Funktion hinzugefügt, die diese Funktionalität wieder bereitstellt. _.property()ist eine Kurzfunktion, die eine Funktion zum Abrufen des Werts einer Eigenschaft in einem Objekt zurückgibt.

Zusätzlich, _.map() jetzt eine Zeichenfolge als zweiter Parameter übergeben werden, an den übergeben wird _.property(). Infolgedessen entsprechen die folgenden zwei Zeilen dem obigen Codebeispiel aus Pre-Lodash 4.

var result = _.map(objArray, 'foo');
var result = _.map(objArray, _.property('foo'));

_.property(), und daher _.map() auch eine durch Punkte getrennte Zeichenfolge oder ein Array bereitstellen, um auf Untereigenschaften zuzugreifen:

var objArray = [
    {
        someProperty: { aNumber: 5 }
    },
    {
        someProperty: { aNumber: 2 }
    },
    {
        someProperty: { aNumber: 9 }
    }
];
var result = _.map(objArray, _.property('someProperty.aNumber'));
var result = _.map(objArray, _.property(['someProperty', 'aNumber']));

Beide _.map() Aufrufe im obigen Beispiel werden zurückgegeben [5, 2, 9].

Wenn Sie sich ein wenig mehr mit funktionaler Programmierung beschäftigen, werfen Sie einen Blick auf Ramdas R.pluck() Funktion, die ungefähr so aussehen würde:

var result = R.pluck('foo')(objArray);  // or just R.pluck('foo', objArray)
cpdt
quelle
5
Gute Nachricht: Irgendwo zwischen Lodash 4.0.0 und 4.3.0 _.property('foo')( lodash.com/docs#property ) wurde als Abkürzung für hinzugefügt function(o) { return o.foo; }. Dies verkürzt den Lodash-Anwendungsfall auf var result = _.pluck(objArray, _.property('foo'));Zur weiteren Bequemlichkeit ermöglicht die _.map() Methode von Lodash 4.3.0 auch die Verwendung einer Kurzschrift _.property()unter der Haube, was dazu führt, dassvar result = _.map(objArray, 'foo');
Mark A. Fitzgerald
45

Ich habe festgestellt, dass eine einfache indizierte forSchleife , so unelegant sie auch sein mag, leistungsfähiger ist als ihre Alternativen.

Extrahieren einer einzelnen Eigenschaft aus einem 100000-Element-Array (über jsPerf)

Traditionell für Schleife 368 Ops / Sek

var vals=[];
for(var i=0;i<testArray.length;i++){
   vals.push(testArray[i].val);
}

ES6 für die Schleife 303 Ops / Sek

var vals=[];
for(var item of testArray){
   vals.push(item.val); 
}

Array.prototype.map 19 Ops / Sek

var vals = testArray.map(function(a) {return a.val;});

TL; DR - .map () ist langsam, aber Sie können es gerne verwenden, wenn Sie der Meinung sind, dass Lesbarkeit mehr wert ist als Leistung.

Edit # 2: 6/2019 - jsPerf-Link unterbrochen, entfernt.

pscl
quelle
1
Rufen Sie mich an, wie Sie möchten, aber die Leistung zählt. Wenn Sie die Karte verstehen können, können Sie eine for-Schleife verstehen.
illcrx
Es ist zwar nützlich, von Ihren Benchmarking-Ergebnissen zu erfahren, aber ich denke, dass dies hier unangemessen ist, da es diese Frage nicht beantwortet. Fügen Sie zur Verbesserung entweder eine echte Antwort hinzu oder komprimieren Sie sie in einen Kommentar (falls möglich).
Ifedi Okonkwo
16

Es ist besser, eine Art von Bibliotheken wie lodash oder Unterstrich für die browserübergreifende Sicherheit zu verwenden.

In Lodash können Sie Werte einer Eigenschaft im Array mithilfe der folgenden Methode abrufen

_.map(objArray,"foo")

und in Unterstrich

_.pluck(objArray,"foo")

Beide werden zurückkehren

[1, 2, 3]
Tejas Patel
quelle
15

Verwenden von Array.prototype.map:

function getFields(input, field) {
    return input.map(function(o) {
        return o[field];
    });
}

Unter dem obigen Link finden Sie eine Unterlegscheibe für Browser vor ES5.

Alnitak
quelle
10

In ES6 können Sie Folgendes tun:

const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)
Yuxuan Chen
quelle
Was ist, wenn foo undefiniert ist? Ein Array von undefinierten ist nicht gut.
Godhar
6

Dies mapist zwar eine geeignete Lösung, um 'Spalten' aus einer Liste von Objekten auszuwählen, hat jedoch einen Nachteil. Wenn nicht explizit überprüft wird, ob die Spalten vorhanden sind oder nicht, wird ein Fehler ausgegeben und (bestenfalls) angezeigt undefined. Ich würde mich für eine reduceLösung entscheiden, die die Eigenschaft einfach ignorieren oder Sie sogar mit einem Standardwert einrichten kann.

function getFields(list, field) {
    //  reduce the provided list to an array only containing the requested field
    return list.reduce(function(carry, item) {
        //  check if the item is actually an object and does contain the field
        if (typeof item === 'object' && field in item) {
            carry.push(item[field]);
        }

        //  return the 'carry' (which is the list of matched field values)
        return carry;
    }, []);
}

jsbin Beispiel

Dies würde auch dann funktionieren, wenn eines der Elemente in der bereitgestellten Liste kein Objekt ist oder das Feld nicht enthält.

Es kann sogar flexibler gestaltet werden, indem ein Standardwert ausgehandelt wird, falls ein Element kein Objekt ist oder das Feld nicht enthält.

function getFields(list, field, otherwise) {
    //  reduce the provided list to an array containing either the requested field or the alternative value
    return list.reduce(function(carry, item) {
        //  If item is an object and contains the field, add its value and the value of otherwise if not
        carry.push(typeof item === 'object' && field in item ? item[field] : otherwise);

        //  return the 'carry' (which is the list of matched field values)
        return carry;
    }, []);
}

jsbin Beispiel

Dies wäre bei map dasselbe, da die Länge des zurückgegebenen Arrays der Länge des bereitgestellten Arrays entsprechen würde. (In diesem Fall mapist a etwas billiger als a reduce):

function getFields(list, field, otherwise) {
    //  map the provided list to an array containing either the requested field or the alternative value
    return list.map(function(item) {
        //  If item is an object and contains the field, add its value and the value of otherwise if not
        return typeof item === 'object' && field in item ? item[field] : otherwise;
    }, []);
}

jsbin Beispiel

Und dann gibt es die flexibelste Lösung, mit der Sie zwischen beiden Verhaltensweisen wechseln können, indem Sie einfach einen alternativen Wert angeben.

function getFields(list, field, otherwise) {
    //  determine once whether or not to use the 'otherwise'
    var alt = typeof otherwise !== 'undefined';

    //  reduce the provided list to an array only containing the requested field
    return list.reduce(function(carry, item) {
        //  If item is an object and contains the field, add its value and the value of 'otherwise' if it was provided
        if (typeof item === 'object' && field in item) {
            carry.push(item[field]);
        }
        else if (alt) {
            carry.push(otherwise);
        }

        //  return the 'carry' (which is the list of matched field values)
        return carry;
    }, []);
}

jsbin Beispiel

Da die obigen Beispiele (hoffentlich) etwas Licht in die Funktionsweise bringen, können Sie die Funktion durch Verwendung der Array.concatFunktion etwas verkürzen .

function getFields(list, field, otherwise) {
    var alt = typeof otherwise !== 'undefined';

    return list.reduce(function(carry, item) {
        return carry.concat(typeof item === 'object' && field in item ? item[field] : (alt ? otherwise : []));
    }, []);
}

jsbin Beispiel

Rogier Spieker
quelle
6

Wenn Sie Objektwerte extrapolieren möchten, die sich innerhalb eines Arrays befinden (wie in der Frage beschrieben), können Sie im Allgemeinen die Destrukturierung reduzieren, abbilden und Array verwenden.

ES6

let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }];
let b = a.reduce((acc, obj) => [...acc, Object.values(obj).map(y => y)], []);

console.log(b)

Das Äquivalent für die for- Schleife wäre:

for (let i in a) {
  let temp = [];
  for (let j in a[i]) {
    temp.push(a[i][j]);
  }
  array.push(temp);
}

Produzierte Ausgabe: ["Wort", "wieder", "einige", "1", "2", "3"]

Eugen Sunic
quelle
3

Es hängt von Ihrer Definition von "besser" ab.

Die anderen Antworten weisen auf die Verwendung von Karten hin, die natürlich (insbesondere für Männer, die an funktionalen Stil gewöhnt sind) und prägnant sind. Ich empfehle dringend, es zu verwenden (wenn Sie sich nicht mit den wenigen IE8-IT-Leuten beschäftigen). Wenn also "besser" "prägnanter", "wartbar", "verständlich" bedeutet, dann ist es viel besser.

Andererseits kommt diese Schönheit nicht ohne zusätzliche Kosten. Ich bin kein großer Fan von Microbench, aber ich habe hier einen kleinen Test durchgeführt . Das Ergebnis ist vorhersehbar, der alte hässliche Weg scheint schneller zu sein als die Kartenfunktion. Also, wenn "besser" "schneller" bedeutet, dann nein, bleiben Sie bei der alten Schulmode.

Auch dies ist nur eine Mikrobank und spricht sich in keiner Weise gegen die Verwendung von aus map, es sind nur meine zwei Cent :).

sitifensys
quelle
Nun, dies ist eigentlich serverseitig, nicht in einem Browser, also ist IE8 irrelevant. Ja, mapist der offensichtliche Weg, irgendwie habe ich ihn mit Google einfach nicht gefunden (ich habe nur die Karte von jquery gefunden, die nicht relevant ist).
Hyde
3

Wenn Sie auch Array-ähnliche Objekte unterstützen möchten, verwenden Sie Array.from (ES2015):

Array.from(arrayLike, x => x.foo);

Der Vorteil gegenüber der Array.prototype.map () -Methode besteht darin, dass die Eingabe auch ein Set sein kann :

let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
TechWisdom
quelle
2

Wenn Sie mehrere Werte in ES6 + möchten, funktioniert Folgendes

objArray = [ { foo: 1, bar: 2, baz: 9}, { foo: 3, bar: 4, baz: 10}, { foo: 5, bar: 6, baz: 20} ];

let result = objArray.map(({ foo, baz }) => ({ foo, baz }))

Dies funktioniert wie {foo, baz}auf der linken Seite wird mit Objekt destructoring und auf der rechten Seite des Pfeils entspricht {foo: foo, baz: baz}zurückzuführen ES6 des erweiterten Objektliterale .

Chris Magnuson
quelle
genau das, was ich brauchte, wie schnell / langsam ist diese Lösung Ihrer Meinung nach?
Ruben
1
@ Ruben Um wirklich zu sagen, ich denke, Sie müssten verschiedene Optionen von dieser Seite nehmen und sie mit etwas wie jsperf.com testen
Chris Magnuson
1

Die Funktionszuordnung ist eine gute Wahl, wenn es um Objektarrays geht. Obwohl bereits einige gute Antworten veröffentlicht wurden, kann das Beispiel der Verwendung einer Karte mit Kombination mit Filter hilfreich sein.

Wenn Sie die Eigenschaften ausschließen möchten, deren Werte nicht definiert sind, oder nur eine bestimmte Eigenschaft ausschließen möchten, können Sie Folgendes tun:

    var obj = {value1: "val1", value2: "val2", Ndb_No: "testing", myVal: undefined};
    var keysFiltered = Object.keys(obj).filter(function(item){return !(item == "Ndb_No" || obj[item] == undefined)});
    var valuesFiltered = keysFiltered.map(function(item) {return obj[item]});

https://jsfiddle.net/ohea7mgk/

Niko Gamulin
quelle
0

Die oben angegebene Antwort eignet sich zum Extrahieren einer einzelnen Eigenschaft. Was ist, wenn Sie mehr als eine Eigenschaft aus einem Array von Objekten extrahieren möchten? Hier ist die Lösung !! In diesem Fall können wir einfach _.pick (Objekt, [Pfade]) verwenden.

_.pick(object, [paths])

Nehmen wir an, objArray hat Objekte mit drei Eigenschaften wie unten

objArray = [ { foo: 1, bar: 2, car:10}, { foo: 3, bar: 4, car:10}, { foo: 5, bar: 6, car:10} ];

Jetzt wollen wir foo- und bar-Eigenschaften aus jedem Objekt extrahieren und in einem separaten Array speichern. Zuerst werden wir Array-Elemente mit map iterieren und dann die Lodash Library Standard _.pick () -Methode darauf anwenden.

Jetzt können wir die Eigenschaften 'foo' und 'bar' extrahieren.

var newArray = objArray.map((element)=>{ return _.pick(element, ['foo','bar'])}) console.log(newArray);

und das Ergebnis wäre [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]

genießen!!!

Akash Jain
quelle
1
Woher kommt _.pickdas? Es ist keine Standardfunktion.
Neves
Ich habe gerade die Antwort aktualisiert. _.Pick () ist eine Standardmethode der Lodash-Bibliothek.
Akash Jain
0

Wenn Sie verschachtelte Arrays haben, können Sie dies folgendermaßen ausführen:

const objArray = [ 
     { id: 1, items: { foo:4, bar: 2}},
     { id: 2, items: { foo:3, bar: 2}},
     { id: 3, items: { foo:1, bar: 2}} 
    ];

    let result = objArray.map(({id, items: {foo}}) => ({id, foo}))
    
    console.log(result)

Qui-Gon Jinn
quelle