Executar .join no valor na matriz de objetos

274

Se eu tiver uma matriz de cadeias, posso usar o .join()método para obter uma única cadeia, com cada elemento separado por vírgulas, assim:

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

Eu tenho uma matriz de objetos e gostaria de executar uma operação semelhante em um valor mantido dentro dela; então de

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

execute o joinmétodo apenas no nameatributo, para obter a mesma saída de antes.

Atualmente, tenho a seguinte função:

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

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

  return out.join(", ");
}

Não há nada errado com esse código, ele funciona, mas de repente eu passei de uma linha simples e sucinta de código para uma função muito imperativa. Existe uma maneira mais sucinta e idealmente mais funcional de escrever isso?

jackweirdy
fonte
2
Para usar uma linguagem como um exemplo, uma forma Pythonic de fazer isso seria" ,".join([i.name for i in a])
jackweirdy
6
Em ES6 você poderia fazer isso: users.map(x => x.name).join(', ');.
totymedli

Respostas:

497

Se você deseja mapear objetos para algo (neste caso, uma propriedade). Eu acho Array.prototype.mapque é o que você está procurando se quiser codificar funcionalmente.

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

No JavaScript moderno:

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

(violino)

Se você deseja oferecer suporte a navegadores mais antigos, que não sejam compatíveis com o ES5, pode alterá-lo (existe um polyfill na página MDN acima). Outra alternativa seria usar o pluckmétodo do underscorejs :

var users = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
var result = _.pluck(users,'name').join(",")
Benjamin Gruenbaum
fonte
1
Este. No entanto, você precisará calçar o membro do protótipo .map para o IE <9, se precisar oferecer suporte a esses navegadores retros.
Tommi 17/05
1
@Tommi bom comentário. Eu adicionei uma explicação sobre um polyfill e uma sugestão para usar pluck se estiver usando sublinhado.
Benjamin Gruenbaum
@jackweirdy Estou feliz por poder ajudar :) pelo que vale, assim como mapear / reduzir / filtrar não são 'pitônicos' e as compreensões são o contrário. Nas novas especificações do JavaScript em andamento (Harmony), bem como em alguns navegadores (firefox), você tem uma compreensão python. Você pode fazer [x.name for x of users](espec wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions ). Vale ressaltar que, assim como usar o mapa / reduzir / filtrar, não é muito "pitônico", o contrário provavelmente ocorre no JavaScript. O CoffeeScript é legal com eles no coffeescript.org .
Benjamin Gruenbaum
1
Apenas uma atualização - as compreensões de matriz foram retiradas do ES6, talvez as possamos encontrar no ES7.
Benjamin Gruenbaum
Em coffeescript:users.map((obj) -> obj.name).join(', ')
Kesha Antonov
71

Bem, você sempre pode substituir o toStringmétodo de seus 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"
Manjerição
fonte
2
Essa é uma abordagem bastante interessante, eu não sabia que você poderia fazer isso em JS. Os dados são provenientes de uma API, portanto, provavelmente não é adequado para o meu caso de uso, mas é definitivamente uma boa ideia.
jackweirdy
4
Não parece muito SECO
BadHorsie
3
@BadHorsie Não, não é SECO. Mas é claro que você pode definir uma única função fora da matriz e atribuir essa função ao toStringmétodo de todos os itens, usando um loop for. Ou você pode usar essa abordagem ao lidar com funções do construtor, adicionando um toStringmétodo à prototypepropriedade do construtor. Este exemplo, no entanto, deveria apenas mostrar o conceito básico.
22416
13

Eu também me deparei com o reducemétodo, é o que parece:

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

O (a.name || a)é assim que o primeiro elemento é tratado corretamente, mas o restante (onde aé uma string e, portanto, a.nameé indefinido) não é tratado como um objeto.

Edit: Eu agora refatorei ainda mais isso:

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

que eu acredito que é mais limpo, como asempre é uma string, bé sempre um objeto (devido ao uso do parâmetro initialValue opcional em reduce)

Editar 6 meses depois: Oh, o que eu estava pensando? "limpador". Eu irritei o código deuses.

jackweirdy
fonte
Graças: D reducenão é tão amplamente apoiada, como map(o que é estranho, uma vez que eles são o tipo de irmãs) então eu ainda estou usando a sua resposta :)
jackweirdy
Olhando para a minha edição 6 meses depois, agora decidi que não me contrataria ... É astuto, mas não limpo.
jackweirdy
9

Não sei se existe uma maneira mais fácil de fazer isso sem usar uma biblioteca externa, mas eu pessoalmente amo o underscore.js, que possui vários utilitários para lidar com matrizes, coleções etc.

Com o sublinhado, você poderia fazer isso facilmente com uma linha de código:

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

Ed Hinchliffe
fonte
Eu já se envolveu sublinhado antes, eu estava esperando por uma maneira de fazê-lo em JS nativas embora - é um bocado tonto para puxar em uma biblioteca inteira de usar um método, uma vez :)
jackweirdy
(onde arré a sua matriz, naturalmente)
Ed Hinchliffe
justo! Eu me vejo usando sublinhado em todo o lugar agora, então não é um problema.
Ed Hinchliffe
5

No nó ou ES6 +:

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

digamos que a matriz de objetos seja referenciada pela variável users

Se o ES6 puder ser usado, a solução mais fácil será:

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

Caso contrário, e o lodash pode ser usado para:

 _.map(users, function(user) {
     return user.name;
 }).join(', ');
É a vida
fonte
2

Se objeto e chaves dinâmicas: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}

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

Um tópico antigo que conheço, mas ainda super relevante para quem se deparar com isso.

Array.map foi sugerido aqui, que é um método incrível que eu uso o tempo todo. Array.reduce também foi mencionado ...

Eu pessoalmente usaria um Array.reduce para este caso de uso. Por quê? Apesar do código ser um pouco menos limpo / claro. É muito mais eficiente do que canalizar a função de mapa para uma junção.

O motivo disso é que o Array.map precisa fazer um loop sobre cada elemento para retornar uma nova matriz com todos os nomes do objeto na matriz. Array.join, em seguida, percorre o conteúdo da matriz para executar a junção.

Você pode melhorar a legibilidade do jackweirdys e reduzir a resposta usando literais de modelo para obter o código em uma única linha. "Também suportado em todos os navegadores modernos"

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

Não tenho certeza, mas tudo isso responde que eles funcionam, mas não são ideais, pois estão realizando duas verificações e você pode fazer isso em uma única verificação. Mesmo que O (2n) seja considerado O (n), é sempre melhor ter um O (n) verdadeiro.

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

Isso pode parecer uma velha escola, mas me permite fazer coisas assim:

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

tente isso

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
fonte
2
É o mesmo que a função da pergunta, exceto menos versátil, porque você codificou o nameatributo. ( a[i].nameSe não usar o seu de função nameargumento, é equivalente a a[i]['name'].)
nnnnnn