Effectuer .join sur une valeur dans un tableau d'objets

274

Si j'ai un tableau de chaînes, je peux utiliser la .join()méthode pour obtenir une seule chaîne, avec chaque élément séparé par des virgules, comme ceci:

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

J'ai un tableau d'objets et j'aimerais effectuer une opération similaire sur une valeur contenue en son sein; donc de

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

effectuer la joinméthode uniquement sur l' nameattribut, pour obtenir la même sortie qu'auparavant.

Actuellement, j'ai la fonction suivante:

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

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

  return out.join(", ");
}

Il n'y a rien de mal à ce code, cela fonctionne, mais tout d'un coup, je suis passé d'une ligne de code simple et succincte à une fonction très impérative. Existe-t-il une manière plus succincte, idéalement plus fonctionnelle, d'écrire cela?

jackweirdy
la source
2
Pour utiliser une langue comme exemple, une façon de faire " ,".join([i.name for i in a])
Pythonique
6
En ES6 , vous pouvez le faire: users.map(x => x.name).join(', ');.
totymedli

Réponses:

498

Si vous souhaitez mapper des objets à quelque chose (dans ce cas, une propriété). Je pense que Array.prototype.mapc'est ce que vous recherchez si vous voulez coder de manière fonctionnelle.

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

En JavaScript moderne:

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

(violon)

Si vous souhaitez prendre en charge des navigateurs plus anciens, qui ne sont pas conformes à ES5, vous pouvez le caler (il y a un polyfill sur la page MDN ci-dessus). Une autre alternative serait d'utiliser la pluckméthode underscorejs :

var users = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
var result = _.pluck(users,'name').join(",")
Benjamin Gruenbaum
la source
1
Celui-là. Cependant, vous devrez caler le membre du prototype .map pour IE <9, si vous devez prendre en charge ces rétro-navigateurs.
Tommi
1
@Tommi bon commentaire. J'ai ajouté une explication sur un polyfill ainsi qu'une suggestion d'utiliser pluck si vous utilisez un trait de soulignement.
Benjamin Gruenbaum
@jackweirdy Je suis content d'avoir pu aider :) pour ce que ça vaut, tout comme la carte / réduire / filtrer ne sont pas «pythoniques» et les compréhensions sont dans l'autre sens. Dans la nouvelle spécification JavaScript en cours (Harmony) ainsi que dans certains navigateurs déjà (firefox), vous avez des compréhensions pythoniques. Vous pouvez le faire [x.name for x of users](spec wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions ). Il convient de mentionner que, tout comme l'utilisation de map / réduire / filtre n'est pas très «pythonique», l'autre sens est probablement valable en JavaScript. CoffeeScript est cool avec eux via coffeescript.org .
Benjamin Gruenbaum
1
Juste une mise à jour - les compréhensions de tableaux ont été supprimées d'ES6, peut-être les obtiendrons-nous dans ES7.
Benjamin Gruenbaum
En coffeescript:users.map((obj) -> obj.name).join(', ')
Kesha Antonov
71

Eh bien, vous pouvez toujours remplacer la toStringméthode de vos objets:

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
la source
2
C'est une approche assez intéressante, je ne savais pas que vous pouviez le faire dans JS. Cependant, les données proviennent d'une API, donc elles ne conviennent probablement pas à mon cas d'utilisation, mais c'est certainement matière à réflexion.
jackweirdy
4
Ne semble pas très SEC
BadHorsie
3
@BadHorsie Non, ce n'est pas SEC. Mais bien sûr, vous pouvez définir une seule fonction en dehors du tableau, puis affecter cette fonction à la toStringméthode de tous les éléments, en utilisant une boucle for. Ou vous pouvez utiliser cette approche lorsque vous traitez avec des fonctions de constructeur, en ajoutant une toStringméthode à la prototypepropriété du constructeur. Cet exemple était cependant censé montrer le concept de base.
basilikum
13

Je suis également tombé sur l'utilisation de la reduceméthode, voici à quoi cela ressemble:

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

Le (a.name || a)est donc le premier élément est traité correctement, mais le reste (où aest une chaîne, et donc a.namen'est pas défini) n'est pas traité comme un objet.

Edit: je l'ai maintenant refactorisé à la suite de ceci:

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

qui, je crois, est plus propre, comme ac'est toujours une chaîne, best toujours un objet (en raison de l'utilisation du paramètre optionnel initialValue dans reduce)

Edit 6 mois plus tard: Oh à quoi je pensais. "nettoyeur". J'ai mis en colère le code des dieux.

jackweirdy
la source
Merci: D reducen'est pas aussi largement pris en charge que map(ce qui est bizarre, étant donné que ce sont des sœurs), donc j'utilise toujours votre réponse :)
jackweirdy
En regardant mon montage 6 mois plus tard, j'ai décidé de ne pas m'engager moi-même ... C'est rusé, mais pas propre.
jackweirdy
9

Je ne sais pas s'il y a un moyen plus facile de le faire sans utiliser une bibliothèque externe, mais personnellement , je l' aime underscore.js qui a des tonnes de services publics pour faire face à des tableaux, collections , etc.

Avec le soulignement, vous pouvez le faire facilement avec une seule ligne de code:

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

Ed Hinchliffe
la source
J'ai déjà essayé de souligner, j'espérais un moyen de le faire dans JS natif - c'est un peu idiot de tirer dans une bibliothèque entière pour utiliser une seule méthode :)
jackweirdy
(où arrest votre tableau, naturellement)
Ed Hinchliffe
C'est suffisant! Je me retrouve à utiliser le soulignement partout maintenant, donc ce n'est pas un problème.
Ed Hinchliffe
5

Sur le nœud ou ES6 +:

users.map(u => u.name).join(', ')
ariel
la source
3

disons que le tableau d'objets est référencé par la variable users

Si ES6 peut être utilisé, la solution la plus simple sera:

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

Sinon, le lodash peut être utilisé de la manière suivante:

 _.map(users, function(user) {
     return user.name;
 }).join(', ');
C'est la vie
la source
2

Si objet et clés dynamiques: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}

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

Un vieux fil que je connais mais toujours super pertinent pour tous ceux qui rencontrent cela.

Array.map a été suggéré ici, ce qui est une méthode géniale que j'utilise tout le temps. Array.reduce a également été mentionné ...

J'utiliserais personnellement un Array.reduce pour ce cas d'utilisation. Pourquoi? Malgré le code légèrement moins clair / clair. C'est beaucoup plus efficace que de diriger la fonction map vers une jointure.

La raison en est que Array.map doit faire une boucle sur chaque élément pour renvoyer un nouveau tableau avec tous les noms de l'objet dans le tableau. Array.join boucle ensuite sur le contenu du tableau pour effectuer la jointure.

Vous pouvez améliorer la lisibilité des jackweirdys réduire la réponse en utilisant des littéraux de modèle pour obtenir le code sur une seule ligne. "Pris en charge dans tous les navigateurs modernes aussi"

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

Je ne sais pas, mais tout cela répond qu'ils fonctionnent mais ne sont pas optiomaux car ils effectuent deux analyses et vous pouvez effectuer cela en une seule analyse. Même si O (2n) est considéré comme O (n), il est toujours préférable d'avoir un vrai O (n).

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

Cela pourrait ressembler à de la vieille école, mais cela me permet de faire ça comme ceci:

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

essaye ça

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
la source
2
C'est la même chose que la fonction dans la question, sauf moins polyvalente car vous avez codé en dur l' nameattribut. ( a[i].namen'utilise pas l' nameargument de votre fonction , il est équivalent à a[i]['name'].)
nnnnnn