Выполнить .join по значению в массиве объектов

274

Если у меня есть массив строк, я могу использовать .join() метод для получения одной строки, каждый элемент которой разделен запятыми, например:

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

У меня есть массив объектов, и я хотел бы выполнить аналогичную операцию со значением, содержащимся в нем; так из

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

выполнить join метод только для nameатрибута, чтобы получить тот же результат, что и раньше.

В настоящее время у меня есть следующая функция:

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

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

  return out.join(", ");
}

В этом коде нет ничего плохого, он работает, но внезапно я перешел от простой, лаконичной строки кода к очень важной функции. Есть ли более сжатый, идеально более функциональный способ написания этого?

jackweirdy
источник
2
Чтобы использовать один язык в качестве примера, Pythonic способ сделать это" ,".join([i.name for i in a])
Jackweirdy
6
В ES6 вы могли бы сделать это: users.map(x => x.name).join(', ');.
Тотимедли

Ответы:

498

Если вы хотите сопоставить объекты чему-либо (в данном случае это свойство). Я думаю, Array.prototype.mapэто то, что вы ищете, если вы хотите функционально кодировать.

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

В современном JavaScript:

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

(Скрипка)

Если вы хотите поддерживать более старые браузеры, которые не совместимы с ES5, вы можете их изменить (на странице MDN выше есть полифильтр). Другой альтернативой будет использование pluckметода underscorejs :

var users = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
var result = _.pluck(users,'name').join(",")
Бенджамин Грюнбаум
источник
1
Вот этот. Тем не менее, если вам нужна поддержка этих ретро-браузеров, вам нужно будет добавить элемент прототипа .map для IE <9.
Томми
1
@ Томми хороший комментарий. Я добавил объяснение о polyfill, а также предложение использовать pluck при использовании подчеркивания.
Бенджамин Грюнбаум
@jackweirdy Я рад, что смог помочь :), несмотря на то, что он стоит, так же как карта / уменьшение / фильтр не являются «питоническими», а понимание - другой способ. В новой спецификации JavaScript (Harmony), а также в некоторых браузерах (Firefox) у вас есть pythonic понимания. Вы можете сделать это [x.name for x of users](spec wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions ). Стоит отметить, что использование карты / редуктора / фильтра не очень «питонно», в отличие от JavaScript. CoffeeScript классен с ними, хотя coffeescript.org .
Бенджамин Грюнбаум
1
Просто обновление - из ES6 исключены массивы, возможно, мы получим их в ES7.
Бенджамин Грюнбаум
В coffeescript:users.map((obj) -> obj.name).join(', ')
Кеша Антонов
71

Ну, вы всегда можете переопределить toStringметод ваших объектов:

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
источник
2
Это довольно интересный подход, я не осознавал, что вы можете сделать это в JS. Данные поступают из API, хотя, вероятно, они не подходят для моего случая использования, но это определенно пища для размышлений.
Jackweirdy
4
Не кажется очень
сухим
3
@ BadHorsie Нет, это не СУХОЙ. Но, конечно, вы можете определить одну функцию вне массива, а затем назначить эту функцию toStringметоду всех элементов, используя цикл for. Или вы можете использовать этот подход при работе с функциями конструктора, добавив один toStringметод к prototypeсвойству конструктора. Однако этот пример должен был показать основную концепцию.
Базиликум
13

Я также сталкивался с использованием reduceметода, вот как это выглядит:

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

(a.name || a)Так первый элемент обрабатывается правильно, но остальное (где aэто строка, и такa.name определены) не рассматриваются как объект.

Изменить: я теперь рефакторинг это дальше к этому:

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

который я считаю чище, как aвсегда строка, bвсегда является объектом (из-за использования необязательного параметра initialValue вreduce )

Редактировать 6 месяцев спустя: о чем я думал. "очиститель". Я разозлил кодекс богов.

jackweirdy
источник
Спасибо: D reduceне так широко поддерживается, как map(что странно, учитывая, что они вроде сестры), поэтому я все еще использую ваш ответ :)
jackweirdy
Глядя на мою редакцию 6 месяцев спустя, я решил, что не найду себя ... Это хитро, но не чисто.
Jackweirdy
9

Я не знаю, есть ли более простой способ сделать это без использования внешней библиотеки, но мне лично нравится underscore.js котором есть множество утилит для работы с массивами, коллекциями и т. Д.

С подчеркиванием вы можете сделать это легко с помощью одной строки кода:

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

Эд Хинклифф
источник
Раньше я подчеркивал подчеркивание, я надеялся найти способ сделать это в нативном JS, хотя - немного глупо использовать целую библиотеку, чтобы использовать один метод один раз :)
jackweirdy
(где arrтвой массив, естественно)
Эд Хинчлифф
справедливо! Теперь я использую подчеркивание повсеместно, так что это не проблема.
Эд Хинклифф
5

На узле или ES6 +:

users.map(u => u.name).join(', ')
Ariel
источник
3

скажем, массив объектов ссылается на переменную users

Если можно использовать ES6, то самым простым решением будет:

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

Если нет, то и лодаш можно использовать так:

 _.map(users, function(user) {
     return user.name;
 }).join(', ');
Такова жизнь
источник
2

Если объект и динамические ключи: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}

Object.keys(myObject).map(function (key, index) {
    return myObject[key]
}).join(', ')
Даврон Ачилов
источник
0

Старая ветка, которую я знаю, но все еще очень важна для всех, кто сталкивался с этим.

Array.map был предложен здесь, это отличный метод, который я использую все время. Array.reduce был также упомянут ...

Я бы лично использовал Array.reduce для этого варианта использования. Зачем? Несмотря на то, что код немного менее чистый / понятный. Это гораздо эффективнее, чем привязка функции карты к объединению.

Причина этого в том, что Array.map должен перебирать каждый элемент, чтобы возвращать новый массив со всеми именами объекта в массиве. Затем Array.join перебирает содержимое массива, чтобы выполнить соединение.

Вы можете улучшить читабельность Jackweirdys уменьшить ответ, используя литералы шаблона, чтобы код был в одной строке. «Поддерживается во всех современных браузерах»

// a one line answer to this question using modern JavaScript
x.reduce((a, b) => `${a.name || a}, ${b.name}`);
Nigelli
источник
0

Не уверен, но все это отвечает тем, что они работают, но не являются оптимальными, поскольку выполняют два сканирования, и вы можете выполнить это за один просмотр. Даже если O (2n) считается O (n), всегда лучше иметь истинное 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;
}

Это может выглядеть как старая школа, но позволяет мне делать вот так:

skuCombined = Join(option.SKUs, ',', 'SkuNum');
Хорхе Агилар
источник
-1

попробуй это

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
источник
2
Это то же самое, что и функция в вопросе, за исключением менее универсальной, поскольку вы жестко закодировали nameатрибут. ( a[i].nameЭто не использовать вашу функцию в nameаргумент, это эквивалентно a[i]['name'].)
NNNNNN