Esegui .join sul valore nella matrice di oggetti


274

Se ho una matrice di stringhe, posso usare il .join()metodo per ottenere una singola stringa, con ogni elemento separato da virgole, in questo modo:

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

Ho una serie di oggetti e mi piacerebbe eseguire un'operazione simile su un valore contenuto al suo interno; così da

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

eseguire il joinmetodo solo sull'attributo name, per ottenere lo stesso output di prima.

Attualmente ho la seguente funzione:

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

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

  return out.join(", ");
}

Non c'è niente di sbagliato in quel codice, funziona, ma all'improvviso sono passato da una semplice e succinta linea di codice a una funzione molto imperativa. Esiste un modo più conciso, idealmente più funzionale di scrivere questo?


2
Per usare una lingua come esempio, un modo Pythonic di farlo sarebbe" ,".join([i.name for i in a])
jackweirdy,

6
In ES6 si potrebbe fare questo: users.map(x => x.name).join(', ');.
totymedli,

Risposte:


497

Se vuoi mappare oggetti su qualcosa (in questo caso una proprietà). Penso che Array.prototype.mapsia quello che stai cercando se vuoi codificare funzionalmente.

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

Nel moderno JavaScript:

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

(violino)

Se si desidera supportare browser meno recenti, che non sono conformi a ES5, è possibile evitarlo (è presente un polyfill nella pagina MDN sopra). Un'altra alternativa sarebbe quella di utilizzare il pluckmetodo underscorejs :

var users = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
var result = _.pluck(users,'name').join(",")

1
Questo. Tuttavia, è necessario evitare il membro prototipo .map per IE <9, se è necessario supportare questo browser retrò.
Tommi,

1
@Tommi buon commento. Ho aggiunto una spiegazione su un polyfill e un suggerimento per usare pluck se si usa il trattino basso.
Benjamin Gruenbaum,

@jackweirdy Sono contento di poterti aiutare :) per quello che vale, proprio come map / ridurre / filtro non sono "pitoni" e le comprensioni sono il contrario. Nelle nuove specifiche di JavaScript in corso (Harmony) e in alcuni browser (firefox) hai una comprensione pitonica. Puoi farlo [x.name for x of users](specifica wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions ). Vale la pena ricordare che, proprio come usare map / ridurre / filtro non è molto 'pitone', l'altro modo probabilmente vale per JavaScript. CoffeeScript è fantastico con loro anche se coffeescript.org .
Benjamin Gruenbaum,

1
Solo un aggiornamento: le comprensioni dell'array sono state eliminate da ES6, forse le avremo in ES7.
Benjamin Gruenbaum,

In coffeescript:users.map((obj) -> obj.name).join(', ')
Kesha Antonov,

71

Bene, puoi sempre ignorare il toStringmetodo dei tuoi oggetti:

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"

2
È un approccio piuttosto interessante, non mi ero reso conto che potresti farlo in JS. I dati provengono da un'API, quindi probabilmente non sono adatti per il mio caso d'uso, ma sono sicuramente spunti di riflessione.
jackweirdy,

4
Non sembra molto ASCIUTTO
BadHorsie il

3
@BadHorsie No, non è ASCIUTTO. Ma ovviamente è possibile definire una singola funzione all'esterno dell'array e quindi assegnare questa funzione al toStringmetodo di tutti gli elementi, utilizzando un ciclo for. In alternativa, è possibile utilizzare questo approccio quando si gestiscono le funzioni del costruttore, aggiungendo un toStringmetodo alla prototypeproprietà per il costruttore. Questo esempio, tuttavia, doveva solo mostrare il concetto di base.
Basilikum,

13

Ho anche incontrato il reducemetodo, ecco come si presenta:

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

Il (a.name || a)è così il primo elemento viene trattato correttamente, ma il resto (dove aè una stringa, e così a.namenon è definito) non è trattata come un oggetto.

Modifica: ora l'ho refactored ulteriormente a questo:

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

che credo sia più pulito come asempre una stringa, bè sempre un oggetto (a causa dell'uso del parametro optionalValue opzionale in reduce)

Modifica 6 mesi dopo: Oh, cosa stavo pensando. "Pulito". Ho fatto arrabbiare il codice Gods.


Grazie: D reducenon è ampiamente supportato come map(il che è strano, dato che sono un po 'sorelle) quindi sto ancora usando la tua risposta :)
jackweirdy,

Guardando la mia modifica 6 mesi dopo, ora ho deciso di non assumermi ... È furbo, ma non pulito.
jackweirdy,

9

Non so se esiste un modo più semplice per farlo senza utilizzare una libreria esterna, ma personalmente adoro underscore.js che ha tonnellate di utility per gestire array, raccolte ecc.

Con underscore potresti farlo facilmente con una riga di codice:

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


Ho già dilettato il trattino basso prima, speravo in un modo per farlo in JS nativo - è un po 'stupido estrarre un'intera libreria per usare un metodo una volta :)
jackweirdy,

(dov'è il arrtuo array, naturalmente)
Ed Hinchliffe,

giusto! Mi ritrovo a usare il trattino basso dappertutto ora, quindi non è un problema.
Ed Hinchliffe,

5

Sul nodo o ES6 +:

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

3

supponiamo che la matrice di oggetti faccia riferimento alla variabile users

Se è possibile utilizzare ES6, la soluzione più semplice sarà:

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

Altrimenti, e lodash può essere usato così:

 _.map(users, function(user) {
     return user.name;
 }).join(', ');

2

Se oggetto e chiavi dinamiche: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}

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

0

Un vecchio thread che conosco, ma ancora molto rilevante per chiunque si imbatta in questo.

Array.map è stato suggerito qui, che è un metodo fantastico che uso sempre. Array.reduce è stato anche menzionato ...

Personalmente userei un Array.reduce per questo caso d'uso. Perché? Nonostante il codice sia leggermente meno pulito / chiaro. È molto più efficiente rispetto al piping della funzione della mappa a un join.

Il motivo è dovuto al fatto che Array.map deve eseguire il ciclo su ciascun elemento per restituire un nuovo array con tutti i nomi dell'oggetto nell'array. Array.join quindi passa in rassegna il contenuto dell'array per eseguire il join.

Puoi migliorare la leggibilità di jackweirdys e ridurre la risposta usando i letterali dei template per ottenere il codice su una sola riga. "Supportato anche in tutti i browser moderni"

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

0

non sono sicuro, ma tutto ciò risponde anche se funzionano ma non sono ottimali poiché eseguono due scansioni e puoi eseguirlo in una singola scansione. Anche se O (2n) è considerato O (n) è sempre meglio avere una O (n) vera.

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

Potrebbe sembrare vecchia scuola, ma mi permette di fare così:

skuCombined = Join(option.SKUs, ',', 'SkuNum');

-1

prova questo

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"

2
Questo è lo stesso della funzione nella domanda, tranne per il fatto che è meno versatile perché hai nameattribuito un codice all'attributo. ( a[i].nameNon senza utilizzare la funzione di nameargomento, è equivalente a a[i]['name'].)
nnnnnn
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.