Führen Sie .join on value in einem Array von Objekten aus

273

Wenn ich ein Array von Zeichenfolgen habe, kann ich die verwenden .join() Methode verwenden, um eine einzelne Zeichenfolge abzurufen, wobei jedes Element wie folgt durch Kommas getrennt ist:

["Joe", "Kevin", "Peter"].join(", ") // => "Joe, Kevin, Peter"

Ich habe eine Reihe von Objekten und möchte eine ähnliche Operation für einen darin enthaltenen Wert ausführen. also von

[
  {name: "Joe", age: 22},
  {name: "Kevin", age: 24},
  {name: "Peter", age: 21}
]

Führen Sie die join Methode nur für das nameAttribut aus, um die gleiche Ausgabe wie zuvor zu erzielen.

Derzeit habe ich folgende Funktion:

function joinObj(a, attr){
  var out = [];

  for (var i = 0; i < a.length; i++){
    out.push(a[i][attr]);
  }

  return out.join(", ");
}

An diesem Code ist nichts auszusetzen, er funktioniert, aber plötzlich bin ich von einer einfachen, prägnanten Codezeile zu einer sehr wichtigen Funktion übergegangen. Gibt es eine prägnantere, idealerweise funktionalere Schreibweise?

Jackweirdy
quelle
1
Um eine Sprache als Beispiel zu verwenden, wäre eine pythonische Methode, dies zu tun," ,".join([i.name for i in a])
jackweirdy
6
In ES6 können Sie dies tun : users.map(x => x.name).join(', ');.
Totymedli

Antworten:

496

Wenn Sie Objekte etwas zuordnen möchten (in diesem Fall eine Eigenschaft). Ich denke, es Array.prototype.mapist das, wonach Sie suchen, wenn Sie funktional codieren möchten.

[
  {name: "Joe", age: 22},
  {name: "Kevin", age: 24},
  {name: "Peter", age: 21}
].map(function(elem){
    return elem.name;
}).join(",");

In modernem JavaScript:

[
  {name: "Joe", age: 22},
  {name: "Kevin", age: 24},
  {name: "Peter", age: 21}
].map(e => e.name).join(",");

(Geige)

Wenn Sie ältere Browser unterstützen möchten, die nicht ES5-kompatibel sind , können Sie diese anpassen (auf der MDN-Seite oben befindet sich eine Polyfüllung). Eine andere Alternative wäre die Verwendung der pluckMethode von underscorejs :

var users = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
var result = _.pluck(users,'name').join(",")
Benjamin Gruenbaum
quelle
1
Dieses. Sie müssen jedoch das .map-Prototypmitglied für IE <9 shim, wenn Sie diese Retro-Browser unterstützen möchten.
Tommi
@Tommi guter Kommentar. Ich habe eine Erklärung zu einer Polyfüllung sowie einen Vorschlag zur Verwendung von Zupfen hinzugefügt, wenn Unterstriche verwendet werden.
Benjamin Gruenbaum
@jackweirdy Ich bin froh, dass ich helfen konnte :) für das, was es wert ist, genau wie Map / Reduce / Filter nicht 'pythonisch' sind und Verständnis das andere ist. In der neuen JavaScript-Spezifikation (Harmony) sowie in einigen Browsern (Firefox) haben Sie pythonische Kenntnisse. Sie können dies tun [x.name for x of users](spezifizieren Sie wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions ). Erwähnenswert ist, dass genau wie die Verwendung von Map / Reduce / Filter nicht sehr "pythonisch" ist, wie es in JavaScript wahrscheinlich der andere Weg ist. CoffeeScript ist cool mit ihnen, obwohl coffeescript.org .
Benjamin Gruenbaum
Nur ein Update - Array-Verständnis wurde aus ES6 entfernt, vielleicht bekommen wir es in ES7.
Benjamin Gruenbaum
In Coffeescript:users.map((obj) -> obj.name).join(', ')
Kesha Antonov
71

Nun, Sie können die toStringMethode Ihrer Objekte jederzeit überschreiben :

var arr = [
    {name: "Joe", age: 22, toString: function(){return this.name;}},
    {name: "Kevin", age: 24, toString: function(){return this.name;}},
    {name: "Peter", age: 21, toString: function(){return this.name;}}
];

var result = arr.join(", ");
//result = "Joe, Kevin, Peter"
Basilikum
quelle
2
Das ist ein ziemlich interessanter Ansatz, ich wusste nicht, dass Sie dies in JS tun können. Die Daten stammen zwar von einer API, sind also wahrscheinlich nicht für meinen Anwendungsfall geeignet, aber definitiv ein Denkanstoß.
Jackweirdy
3
Scheint
3
@ BadHorsie Nein, es ist nicht trocken. Natürlich können Sie auch eine einzelne Funktion außerhalb des Arrays definieren und diese Funktion dann toStringmithilfe einer for-Schleife der Methode aller Elemente zuweisen . Sie können diesen Ansatz auch verwenden, wenn Sie sich mit Konstruktorfunktionen befassen, indem Sie toStringder prototypeEigenschaft für den Konstruktor eine Methode hinzufügen . Dieses Beispiel sollte jedoch nur das Grundkonzept zeigen.
Basilikum
13

Ich bin auch auf die reduceMethode gestoßen, so sieht es aus:

[
  {name: "Joe", age: 22},
  {name: "Kevin", age: 24},
  {name: "Peter", age: 21}
].reduce(function (a, b) {return (a.name || a) + ", " + b.name})

Das (a.name || a) ist so, dass das erste Element korrekt behandelt wird, aber der Rest (wo aein String ist und somit a.nameundefiniert) wird nicht als Objekt behandelt.

Edit: Ich habe es jetzt weiter überarbeitet:

x.reduce(function(a, b) {return a + ["", ", "][+!!a.length] + b.name;}, "");

Was ich für sauberer halte, da aes immer eine Zeichenfolge ist, bist immer ein Objekt (aufgrund der Verwendung vonWas optionalen Parameters initialValue in reduce).

Bearbeiten 6 Monate später: Oh, was habe ich gedacht. "Reiniger". Ich habe den Code Gods verärgert.

Jackweirdy
quelle
Danke: D reducewird nicht so häufig unterstützt wie map(was seltsam ist, wenn man
bedenkt
Als ich 6 Monate später meine Bearbeitung betrachtete, habe ich jetzt entschieden, dass ich mich nicht selbst einstellen würde ... Es ist schlau, aber nicht sauber.
Jackweirdy
9

Ich weiß nicht, ob es einen einfacheren Weg gibt, ohne eine externe Bibliothek zu verwenden, aber ich persönlich liebe underscore.js, das unzählige Dienstprogramme für den Umgang mit Arrays, Sammlungen usw. enthält.

Mit Unterstrich können Sie dies einfach mit einer Codezeile tun:

_.pluck(arr, 'name').join(', ')

Ed Hinchliffe
quelle
Ich habe mich schon einmal mit Unterstrichen beschäftigt, aber ich hatte gehofft, dass dies in nativem JS möglich ist - es ist ein bisschen dumm, eine ganze Bibliothek zu ziehen, um einmal eine Methode zu verwenden :)
jackweirdy
(Wo arrist Ihr Array natürlich)
Ed Hinchliffe
Meinetwegen! Ich benutze jetzt überall Unterstriche, also ist das kein Problem.
Ed Hinchliffe
5

Auf Knoten oder ES6 +:

users.map(u => u.name).join(', ')
Ariel
quelle
3

Angenommen, das Objektarray wird von der Variablen referenziert users

Wenn ES6 verwendet werden kann, ist die einfachste Lösung:

users.map(user => user.name).join(', ');

Wenn nicht, und lodash kann so verwendet werden:

 _.map(users, function(user) {
     return user.name;
 }).join(', ');
So ist das Leben
quelle
1

Wenn Objekt- und dynamische Schlüssel: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}

Object.keys(myObject).map(function (key, index) {
    return myObject[key]
}).join(', ')
Davron Achilov
quelle
0

Ein alter Thread, den ich kenne, der aber für jeden, der darauf stößt, immer noch sehr relevant ist.

Hier wurde Array.map vorgeschlagen, eine großartige Methode, die ich ständig verwende. Array.reduce wurde auch erwähnt ...

Ich würde persönlich eine Array.reduce für diesen Anwendungsfall verwenden. Warum? Obwohl der Code etwas weniger sauber / klar ist. Es ist viel effizienter als das Weiterleiten der Kartenfunktion an einen Join.

Der Grund dafür ist, dass Array.map jedes Element durchlaufen muss, um ein neues Array mit allen Namen des Objekts im Array zurückzugeben. Array.join durchläuft dann den Inhalt des Arrays, um den Join auszuführen.

Sie können die Lesbarkeit von Jackweirdys verbessern, indem Sie Vorlagenliterale verwenden, um den Code in eine einzelne Zeile zu bringen. "Wird auch in allen modernen Browsern unterstützt"

// a one line answer to this question using modern JavaScript
x.reduce((a, b) => `${a.name || a}, ${b.name}`);
Nigelli
quelle
0

Ich bin mir nicht sicher, aber all dies antwortet, obwohl sie funktionieren, aber nicht optiomal sind, da sie zwei Scans durchführen und Sie dies in einem einzigen Scan durchführen können. Obwohl O (2n) als O (n) betrachtet wird, ist es immer besser, ein wahres O (n) zu haben.

const Join = (arr, separator, prop) => {
    let combined = '';
    for (var i = 0; i < arr.length; i++) {
        combined = `${combined}${arr[i][prop]}`;
        if (i + 1 < arr.length)
            combined = `${combined}${separator} `;
    }
    return combined;
}

Das mag wie in der alten Schule aussehen, aber ich kann es so machen:

skuCombined = Join(option.SKUs, ',', 'SkuNum');
Jorge Aguilar
quelle
-1

Versuche dies

var x= [
  {name: "Joe", age: 22},
  {name: "Kevin", age: 24},
  {name: "Peter", age: 21}
]

function joinObj(a, attr) {
  var out = []; 
  for (var i=0; i<a.length; i++) {  
    out.push(a[i][attr]); 
  } 
 return out.join(", ");
}

var z = joinObj(x,'name');
z > "Joe, Kevin, Peter"
var y = joinObj(x,'age');
y > "22, 24, 21"
Haripds
quelle
2
Dies entspricht der Funktion in der Frage, ist jedoch weniger vielseitig, da Sie das nameAttribut fest codiert haben. ( a[i].nameverwendet nicht das nameArgument Ihrer Funktion , es ist äquivalent zu a[i]['name'].)
nnnnnn