Виконайте .join за значенням у масиві об'єктів


273

Якщо у мене є масив рядків, я можу використовувати .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(", ");
}

З цим кодом немає нічого поганого, він працює, але раптом я перейшов від простого, лаконічного коду до дуже імперативної функції. Чи є більш складний, ідеально більш функціональний спосіб написання цього?


1
Якщо використовувати одну мову як приклад, пітонічним способом цього було б" ,".join([i.name for i in a])
jackweirdy

6
У ES6 ви могли б зробити це: users.map(x => x.name).join(', ');.
totymedli

Відповіді:


496

Якщо ви хочете зіставити об'єкти з чимось (у цьому випадку властивістю). Я думаю, 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методу підкреслення :

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

1
Ось цей. Однак вам потрібно буде прошивати член прототипу .map для IE <9, якщо вам потрібно підтримати цей ретро-браузер.
Томмі

@Tommi гарний коментар. Я додав пояснення щодо полісистеми, а також пропозицію використовувати плунк при використанні підкреслення.
Бенджамін Груенбаум

@jackweirdy Я радий, що можу допомогти :) у чому варто, так само як карта / зменшення / фільтр не є "пітонічним", а розуміння - це інший спосіб. У новій специфікації JavaScript (Harmony), а також у деяких браузерах (firefox) ви вже розумієте. Ви можете це зробити [x.name for x of users](spec wiki.ecmascript.org/doku.php?id=harmony:array_comptions ). Варто зазначити, що так само, як використання карти / зменшення / фільтра, не дуже "пітонічно", інший спосіб, мабуть, є в JavaScript. CoffeeScript з ними крутий, хоча coffeescript.org .
Бенджамін Груенбаум

Просто оновлення - розуміння масиву були викинуті з ES6, можливо, ми отримаємо їх у ES7.
Бенджамін Грюнбаум

У кофескрипті: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"

2
Це досить цікавий підхід, я не розумів, що ви можете це зробити в JS. Дані, однак, надходять з API, тому, мабуть, це не підходить для мого використання, але це, безумовно, їжа для роздумів.
jackweirdy

3
Не здається
ДРУГИМ

3
@BadHorsie Ні, це НЕ СУХО. Але звичайно, ви можете визначити одну функцію поза масивом, а потім призначити цю функцію toStringметоду всіх елементів, використовуючи for-цикл. Або ви могли використовувати цей підхід, працюючи з функціями конструктора, додавши один toStringметод у prototypeвластивість для конструктора. Цей приклад, однак, повинен був показати основну концепцію.
basilikum

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завжди є об'єктом (завдяки використанню необов'язкового параметра початкового значення в reduce)

Змінити через 6 місяців: О, що я думав. "очищувач". Я розгнівав код Боги.


Дякую: D reduceне настільки широко підтримується, як map(що дивно, враховуючи, що вони є сестрами), тому я все ще використовую вашу відповідь :)
jackweirdy

Дивлячись на свою редакцію через 6 місяців, я вирішив, що не буду наймати себе ... Це хитро, але не чисто.
jackweirdy

9

Я не знаю, чи є простіший спосіб зробити це без використання зовнішньої бібліотеки, але я особисто люблю underscore.js, який має безліч утиліт для роботи з масивами, колекціями тощо.

З підкресленням ви можете це легко зробити за допомогою одного рядка коду:

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


Я раніше сумнівався в підкресленні, я сподівався, як це зробити в рідному JS - хоч трохи
сміливо задіяти

(де arr, природно, ваш масив)
Ед Хінчліфф

досить справедливо! Зараз я вважаю, що зараз підкреслюю всюди, тому це не проблема.
Ед Гінчліфф


3

скажемо, на масив об'єктів посилається змінна users

Якщо можна використовувати ES6, найпростішим рішенням буде:

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

Якщо ні, і lodash можна використовувати так:

 _.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}`);

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"

2
Це те саме, що і функція у питанні, за винятком менш універсальної, оскільки ви жорстко зашифрували nameатрибут. ( a[i].nameЦе НЕ використовувати вашу функцію в nameаргумент, це еквівалентно a[i]['name'].)
NNNNNN
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.