Lakukan .join pada nilai dalam array objek

274

Jika saya memiliki array string, saya bisa menggunakan .join()metode ini untuk mendapatkan string tunggal, dengan setiap elemen dipisahkan dengan koma, seperti:

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

Saya memiliki berbagai objek, dan saya ingin melakukan operasi serupa pada nilai yang ada di dalamnya; jadi dari

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

melakukan joinmetode hanya pada nameatribut, untuk mencapai hasil yang sama seperti sebelumnya.

Saat ini saya memiliki fungsi berikut:

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

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

  return out.join(", ");
}

Tidak ada yang salah dengan kode itu, ia berfungsi, tetapi tiba-tiba saya beralih dari garis kode yang ringkas dan ringkas ke fungsi yang sangat penting. Apakah ada cara penulisan yang lebih ringkas dan idealnya lebih fungsional?

jackweirdy
sumber
2
Untuk menggunakan satu bahasa sebagai contoh, cara Pythonic untuk melakukan ini adalah" ,".join([i.name for i in a])
jackweirdy
6
Dalam ES6 Anda bisa melakukan ini: users.map(x => x.name).join(', ');.
totymedli

Jawaban:

497

Jika Anda ingin memetakan objek ke sesuatu (dalam hal ini properti). Saya pikir Array.prototype.mapadalah apa yang Anda cari jika Anda ingin kode secara fungsional.

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

Dalam JavaScript modern:

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

(biola)

Jika Anda ingin mendukung browser yang lebih lama, yang tidak sesuai dengan ES5, Anda dapat mengacaukannya (ada polyfill pada halaman MDN di atas). Alternatif lain adalah menggunakan pluckmetode underscorejs :

var users = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
var result = _.pluck(users,'name').join(",")
Benjamin Gruenbaum
sumber
1
Yang ini. Namun Anda harus shim .map prototype member untuk IE <9, jika Anda perlu mendukung browser retro ini.
Tommi
1
@Tommi komentar yang bagus. Saya menambahkan penjelasan tentang polyfill serta saran untuk menggunakan pemetik jika menggunakan garis bawah.
Benjamin Gruenbaum
@ jackweirdy Saya senang saya bisa membantu :) untuk apa nilainya, seperti peta / perkecil / filter tidak 'pythonic' dan pemahaman adalah cara lain berlaku. Dalam spesifikasi baru JavaScript yang sedang berjalan (Harmoni) serta beberapa browser yang sudah ada (firefox) Anda memiliki pemahaman pythonic. Anda dapat melakukannya [x.name for x of users](spec wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions ). Layak disebutkan bahwa sama seperti menggunakan peta / perkecil / filter tidak terlalu 'pythonic' cara lain mungkin berlaku dalam JavaScript. CoffeeScript keren dengan mereka meskipun coffeescript.org .
Benjamin Gruenbaum
1
Hanya pembaruan - pemahaman array telah dikeluarkan dari ES6, mungkin kita akan mendapatkannya di ES7.
Benjamin Gruenbaum
Dalam coffeescript:users.map((obj) -> obj.name).join(', ')
Kesha Antonov
71

Nah, Anda selalu dapat mengganti toStringmetode objek Anda:

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
sumber
2
Itu pendekatan yang cukup menarik, saya tidak menyadari Anda bisa melakukan ini di JS. Data berasal dari API, jadi mungkin tidak cocok untuk kasus penggunaan saya, tapi itu pasti makanan untuk dipikirkan.
jackweirdy
4
Tampaknya tidak terlalu KERING
BadHorsie
3
@BadHorsie Tidak, ini bukan KERING. Tapi tentu saja Anda bisa mendefinisikan fungsi tunggal di luar array dan kemudian menetapkan fungsi ini ke toStringmetode semua item, menggunakan for-loop. Atau Anda bisa menggunakan pendekatan ini ketika berhadapan dengan fungsi konstruktor, dengan menambahkan satu toStringmetode ke prototypeproperti untuk konstruktor. Namun contoh ini seharusnya hanya menunjukkan konsep dasar.
basilikum
13

Saya juga menemukan menggunakan reducemetode ini, seperti ini tampilannya:

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

The (a.name || a)begitu elemen pertama diperlakukan dengan benar, tapi sisa (di mana aadalah string, dan sebagainyaa.name tidak terdefinisi) tidak diperlakukan sebagai objek.

Sunting: Saya sekarang telah refactored lebih lanjut untuk ini:

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

yang saya yakini lebih bersih seperti aselalu berupa string, bselalu merupakan objek (karena penggunaan parameter initialValue opsional dalam reduce)

Sunting 6 bulan kemudian: Oh, apa yang saya pikirkan. "pembersih". Saya sudah membuat marah kode Dewa.

jackweirdy
sumber
Terima kasih: D reducetidak didukung secara luas map(yang aneh, mengingat mereka adalah saudara perempuan) jadi saya masih menggunakan jawaban Anda :)
jackweirdy
Melihat hasil edit saya 6 bulan kemudian, saya sekarang memutuskan untuk tidak menyewa diri ... Licik, tapi tidak bersih.
jackweirdy
9

Saya tidak tahu apakah ada cara yang lebih mudah untuk melakukannya tanpa menggunakan perpustakaan eksternal, tapi saya pribadi suka underscore.js yang memiliki banyak utilitas untuk berurusan dengan array, koleksi, dll.

Dengan garis bawah Anda dapat melakukan ini dengan mudah dengan satu baris kode:

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

Ed Hinchliffe
sumber
Saya telah mencoba-coba menggarisbawahi sebelumnya, saya berharap cara untuk melakukannya di JS asli - itu agak bodoh untuk menarik seluruh perpustakaan untuk menggunakan satu metode sekali :)
jackweirdy
(di mana arrarray Anda, tentu saja)
Ed Hinchliffe
cukup adil! Saya menemukan diri saya menggunakan garis bawah di semua tempat sekarang sehingga tidak menjadi masalah.
Ed Hinchliffe
5

Pada node atau ES6 +:

users.map(u => u.name).join(', ')
Ariel
sumber
3

katakanlah array objek direferensikan oleh variabel users

Jika ES6 dapat digunakan maka solusi termudah adalah:

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

Jika tidak, dan lodash dapat digunakan:

 _.map(users, function(user) {
     return user.name;
 }).join(', ');
C'estLaVie
sumber
2

Jika objek dan kunci dinamis: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}

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

Utas lama yang saya tahu tetapi masih sangat relevan bagi siapa saja yang menemukan ini.

Array.map telah disarankan di sini yang merupakan metode luar biasa yang saya gunakan sepanjang waktu. Array.reduce juga disebutkan ...

Saya pribadi akan menggunakan Array.reduce untuk use case ini. Mengapa? Meskipun kode menjadi sedikit kurang bersih / jernih. Ini jauh lebih efisien daripada memipipkan fungsi peta ke gabungan.

Alasan untuk ini adalah karena Array.map harus mengulang setiap elemen untuk mengembalikan array baru dengan semua nama objek dalam array. Array.join kemudian lewati isi array untuk melakukan join.

Anda dapat meningkatkan keterbacaan jackweirdys mengurangi jawaban dengan menggunakan literal templat untuk mendapatkan kode pada satu baris. "Didukung di semua browser modern juga"

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

tidak yakin, tetapi semua jawaban ini berfungsi tetapi tidak optimal karena melakukan dua pemindaian dan Anda dapat melakukan ini dalam satu pemindaian. Meskipun O (2n) dianggap O (n) selalu lebih baik untuk memiliki O (n) yang sebenarnya.

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

Ini mungkin terlihat seperti jadul, tetapi memungkinkan saya untuk melakukan thig seperti ini:

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

coba ini

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
sumber
2
Ini sama dengan fungsi dalam pertanyaan, kecuali kurang fleksibel karena Anda sudah mengkodekan nameatributnya. ( a[i].nameTidak tidak menggunakan fungsi ini Anda nameargumen, itu setara dengan a[i]['name'].)
nnnnnn