Realice .join en valor en una matriz de objetos

274

Si tengo una serie de cadenas, puedo usar el .join()método para obtener una sola cadena, con cada elemento separado por comas, así:

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

Tengo una serie de objetos y me gustaría realizar una operación similar en un valor contenido en él; entonces de

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

realice el joinmétodo solo en el nameatributo, para lograr el mismo resultado que antes.

Actualmente tengo la siguiente función:

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

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

  return out.join(", ");
}

No hay nada de malo en ese código, funciona, pero de repente he pasado de una línea de código simple y sucinta a una función muy imperativa. ¿Existe una forma más sucinta, idealmente más funcional de escribir esto?

Jackweirdy
fuente
2
Para usar un idioma como ejemplo, una forma pitónica de hacerlo sería" ,".join([i.name for i in a])
jackweirdy
66
En ES6 usted puede hacer esto: users.map(x => x.name).join(', ');.
totymedli

Respuestas:

498

Si desea asignar objetos a algo (en este caso, una propiedad). Creo que Array.prototype.mapes lo que estás buscando si quieres codificar funcionalmente.

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

En JavaScript moderno:

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

(violín)

Si desea admitir navegadores más antiguos, que no son compatibles con ES5 , puede calzarlo (hay un polyfill en la página MDN anterior). Otra alternativa sería utilizar el pluckmétodo underscorejs :

var users = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
var result = _.pluck(users,'name').join(",")
Benjamin Gruenbaum
fuente
1
Éste. Sin embargo, tendrá que calzar el miembro prototipo .map para IE <9, si necesita admitir este navegador retro.
Tommi
1
@Tommi buen comentario. Agregué una explicación sobre un polyfill, así como una sugerencia para usar Pluck si se usa subrayado.
Benjamin Gruenbaum
@jackweirdy Me alegro de poder ayudar :) por lo que vale, al igual que map / reduce / filter no son 'pitónicos' y las comprensiones son lo contrario. En la nueva especificación de JavaScript en progreso (Harmony), así como en algunos navegadores (firefox), tienes comprensiones pitónicas. Puede hacerlo [x.name for x of users](spec wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions ). Vale la pena mencionar que al igual que usar map / reduce / filter no es muy 'pitónico', la otra forma probablemente se cumple en JavaScript. CoffeeScript es genial con ellos aunque coffeescript.org .
Benjamin Gruenbaum
1
Solo una actualización: las comprensiones de matriz se han eliminado de ES6, tal vez las obtengamos en ES7.
Benjamin Gruenbaum
En coffeescript:users.map((obj) -> obj.name).join(', ')
Kesha Antonov
71

Bueno, siempre puedes anular el toStringmétodo de tus objetos:

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
fuente
2
Ese es un enfoque bastante interesante, no me di cuenta de que podrías hacer esto en JS. Sin embargo, los datos provienen de una API, por lo que probablemente no sea adecuado para mi caso de uso, pero definitivamente es algo para pensar.
Jackweirdy
44
No parece muy SECO
BadHorsie
3
@BadHorsie No, no está SECO. Pero, por supuesto, podría definir una sola función fuera de la matriz y luego asignar esta función al toStringmétodo de todos los elementos, utilizando un bucle for. O podría usar este enfoque cuando trabaje con funciones de constructor, agregando un toStringmétodo a la prototypepropiedad para el constructor. Sin embargo, se suponía que este ejemplo mostraba el concepto básico.
basilikum
13

También me he encontrado con el reducemétodo, así es como se ve:

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

El (a.name || a)es por lo que el primer elemento se trata correctamente, pero el resto (en la que aes una cadena, y así a.nameno está definida) no se trata como un objeto.

Editar: ahora lo he refactorizado para esto:

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

que creo que es más limpio, ya aque siempre es una cadena, bsiempre es un objeto (debido al uso del parámetro initialValue opcional en reduce)

Editar 6 meses después: Oh, qué estaba pensando. "limpiador". He enojado el código Dioses.

Jackweirdy
fuente
Gracias: D reduceno es tan ampliamente compatible como map(lo cual es extraño, dado que son como hermanas), así que todavía estoy usando tu respuesta :)
jackweirdy
Mirando mi edición 6 meses después, ahora he decidido que no me contrataría a mí mismo ... Es astuto, pero no limpio.
jackweirdy
9

No sé si hay una manera más fácil de hacerlo sin usar una biblioteca externa, pero personalmente me encanta underscore.js, que tiene toneladas de utilidades para manejar matrices, colecciones, etc.

Con el guión bajo, puede hacer esto fácilmente con una línea de código:

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

Ed Hinchliffe
fuente
He escrito un guión bajo antes, aunque esperaba una forma de hacerlo en JS nativo, es un poco tonto extraer una biblioteca completa para usar un método una vez :)
jackweirdy
(¿Dónde arrestá su matriz, naturalmente?)
Ed Hinchliffe
¡lo suficientemente justo! Ahora me encuentro usando guiones bajos por todo el lugar, así que no es un problema.
Ed Hinchliffe
5

En nodo o ES6 +:

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

digamos que la matriz hace referencia a la matriz de objetos users

Si se puede usar ES6, la solución más fácil será:

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

Si no, y lodash se puede usar para:

 _.map(users, function(user) {
     return user.name;
 }).join(', ');
Así es la vida
fuente
2

Si objeto y claves dinámicas: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}

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

Un viejo hilo que conozco pero que sigue siendo súper relevante para cualquiera que se encuentre con esto.

Array.map ha sido sugerido aquí, que es un método increíble que uso todo el tiempo. Array.reduce también fue mencionado ...

Yo personalmente usaría un Array.reduce para este caso de uso. ¿Por qué? A pesar de que el código es un poco menos limpio / claro. Es mucho más eficiente que canalizar la función del mapa a una unión.

La razón de esto es porque Array.map tiene que recorrer cada elemento para devolver una nueva matriz con todos los nombres del objeto en la matriz. Array.join luego recorre el contenido de la matriz para realizar la unión.

Puede mejorar la legibilidad de jackweirdys para reducir la respuesta utilizando literales de plantilla para obtener el código en una sola línea. "Compatible con todos los navegadores modernos también"

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

No estoy seguro, pero todo esto responde que funcionan, pero no son óptimos, ya que están realizando dos escaneos y puede hacerlo en un solo escaneo. Aunque O (2n) se considera O (n) siempre es mejor tener un O (n) verdadero.

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

Esto podría parecerse a la vieja escuela, pero me permite hacer cosas así:

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

prueba esto

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
fuente
2
Esto es lo mismo que la función en la pregunta, excepto que es menos versátil porque ha codificado el nameatributo. ( a[i].nameNo no utilizar su función de nameargumento, que es equivalente a a[i]['name'].)
nnnnnn