Wykonaj .join na wartości w tablicy obiektów

273

Jeśli mam tablicę ciągów, mogę użyć .join()metody, aby uzyskać pojedynczy ciąg, z każdym elementem oddzielonym przecinkami, tak jak poniżej:

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

Mam tablicę obiektów i chciałbym wykonać podobną operację na wartości w niej zawartej; więc od

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

wykonaj joinmetodę tylko dla nameatrybutu, aby uzyskać takie same wyniki jak poprzednio.

Obecnie mam następującą funkcję:

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

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

  return out.join(", ");
}

Nie ma nic złego w tym kodzie, działa, ale nagle przeszedłem z prostej, zwięzłej linii kodu do bardzo wymagającej funkcji. Czy istnieje bardziej zwięzły, idealnie bardziej funkcjonalny sposób pisania tego?

jackweirdy
źródło
1
Jako przykład posłużyłby się jednym językiem, pythonicznym sposobem na zrobienie tego byłoby" ,".join([i.name for i in a])
jackweirdy
6
W ES6 można to zrobić: users.map(x => x.name).join(', ');.
totymedli

Odpowiedzi:

496

Jeśli chcesz mapować obiekty na coś (w tym przypadku właściwość). Myślę, że Array.prototype.maptego szukasz, jeśli chcesz funkcjonalnie kodować.

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

W nowoczesnym JavaScript:

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

(skrzypce)

Jeśli chcesz obsługiwać starsze przeglądarki, które nie są zgodne z ES5 , możesz je przeskoczyć (na stronie MDN powyżej znajduje się wypełnienie). Inną alternatywą byłoby użycie pluckmetody underscorejs :

var users = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
var result = _.pluck(users,'name').join(",")
Benjamin Gruenbaum
źródło
1
Ten. Musisz jednak odsunąć element prototypowy .map dla IE <9, jeśli chcesz obsługiwać te przeglądarki retro.
Tommi
@Tommi dobry komentarz. Dodałem wyjaśnienie na temat wypełnienia, a także sugestię, aby użyć podciągnięcia, jeśli używasz podkreślenia.
Benjamin Gruenbaum,
@jackweirdy Cieszę się, że mogłem pomóc :) za to, co jest warte, podobnie jak mapa / redukcja / filtr nie są „pytoniczne”, a rozumienie jest inne. W nowej specyfikacji JavaScript w toku (Harmony), a także w niektórych przeglądarkach (firefox), masz pytania w języku Python. Możesz to zrobić [x.name for x of users](spec wiki.ecmascript.org/doku.php?id=harmony:array_compstandingions ). Warto wspomnieć, że podobnie jak użycie mapy / zmniejszania / filtrowania nie jest zbyt „pytoniczne”, w inny sposób prawdopodobnie zachowuje się w JavaScript. CoffeeScript jest z nimi spoko, choć coffeescript.org .
Benjamin Gruenbaum,
Po prostu aktualizacja - z ES6 usunięto wyjaśnienia tablicowe, być może dostaniemy je w ES7.
Benjamin Gruenbaum,
W coffeescript:users.map((obj) -> obj.name).join(', ')
Kesha Antonov
71

Zawsze możesz zastąpić toStringmetodę swoich obiektów:

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"
bazylia
źródło
2
To dość interesujące podejście, nie zdawałem sobie sprawy, że możesz to zrobić w JS. Dane pochodzą jednak z interfejsu API, więc prawdopodobnie nie nadaje się do mojego przypadku użycia, ale zdecydowanie jest to przemyślenie.
jackweirdy
3
Nie wydaje się bardzo SUCHY
BadHorsie
3
@BadHorsie Nie, to nie jest SUCHE. Ale oczywiście można zdefiniować pojedynczą funkcję poza tablicą, a następnie przypisać tę funkcję do toStringmetody wszystkich elementów za pomocą pętli for. Lub możesz użyć tego podejścia do obsługi funkcji konstruktora, dodając jedną toStringmetodę do prototypewłaściwości konstruktora. Ten przykład miał jednak pokazać podstawową koncepcję.
basilikum
13

Natknąłem się również na tę reducemetodę, tak to wygląda:

[
  {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)Jest więc pierwszy element jest odpowiednio traktowane, reszta (gdzie ajest ciągiem znaków, i tak a.namejest nieokreślona) nie jest traktowana jak obiekt.

Edycja: Teraz zmieniłem to na następujące:

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

który, moim zdaniem, jest czystszy, ponieważ azawsze jest łańcuchem, bzawsze jest obiektem (ze względu na użycie opcjonalnego parametru initialValue w reduce)

Edytuj 6 miesięcy później: Och, o czym myślałem. "odkurzacz". Rozgniewałem kod Bogów.

jackweirdy
źródło
Dzięki: D reducenie jest tak szeroko wspierane jak map(co jest dziwne, biorąc pod uwagę, że są rodzeństwem), więc wciąż używam twojej odpowiedzi :)
jackweirdy
Patrząc na moją edycję 6 miesięcy później, zdecydowałem, że nie zatrudnię się ... Jest przebiegły, ale nie czysty.
jackweirdy
9

Nie wiem, czy jest to łatwiejszy sposób bez korzystania z zewnętrznej biblioteki, ale osobiście uwielbiam underscore.js, który ma mnóstwo narzędzi do zarządzania tablicami, kolekcjami itp.

Dzięki podkreśleniu możesz to łatwo zrobić za pomocą jednego wiersza kodu:

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

Ed Hinchliffe
źródło
Wcześniej naciągałem podkreślenie, miałem jednak nadzieję, że uda mi się to zrobić w natywnym JS - trochę głupio jest wciągnąć całą bibliotekę, by użyć jednej metody raz :)
jackweirdy
(gdzie arroczywiście jest twoja tablica)
Ed Hinchliffe
Słusznie! Używam teraz podkreślenia wszędzie, więc to nie jest problem.
Ed Hinchliffe
5

W węźle lub ES6 +:

users.map(u => u.name).join(', ')
ariel
źródło
3

Powiedzmy, że zmienna odwołuje się do tablicy obiektów users

Jeśli można użyć ES6, najłatwiejszym rozwiązaniem będzie:

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

Jeśli nie, można użyć lodash :

 _.map(users, function(user) {
     return user.name;
 }).join(', ');
C'estLaVie
źródło
2

Jeśli klucze obiektowe i dynamiczne: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}

Object.keys(myObject).map(function (key, index) {
    return myObject[key]
}).join(', ')
Davron Achilov
źródło
0

Stary wątek, który znam, ale nadal jest bardzo odpowiedni dla każdego, kto go napotka.

Zasugerowano tutaj Array.map, co jest niesamowitą metodą, której używam cały czas. Wspomniano także o Array.reduce ...

Osobiście użyłbym Array.reduce dla tego przypadku użycia. Czemu? Pomimo tego, że kod jest nieco mniej czysty / wyraźny. Jest to o wiele bardziej wydajne niż połączenie funkcji mapy z łączeniem.

Powodem tego jest to, że Array.map musi zapętlić każdy element, aby zwrócić nową tablicę ze wszystkimi nazwami obiektu w tablicy. Array.join następnie zapętla zawartość tablicy, aby wykonać łączenie.

Możesz poprawić czytelność jackweirdys, zmniejszając odpowiedź, używając literałów szablonów, aby wprowadzić kod do jednego wiersza. „Obsługiwane również we wszystkich nowoczesnych przeglądarkach”

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

nie jestem pewien, ale wszystkie te odpowiedzi działają, ale nie są optiomalne, ponieważ wykonują dwa skany i można to zrobić za jednym razem. Mimo że O (2n) jest uważane za O (n), zawsze lepiej jest mieć prawdziwe 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;
}

Może to wyglądać jak stara szkoła, ale pozwala mi to robić tak:

skuCombined = Join(option.SKUs, ',', 'SkuNum');
Jorge Aguilar
źródło
-1

Spróbuj tego

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
źródło
2
Jest to to samo co funkcja w pytaniu, z wyjątkiem mniej wszechstronnej, ponieważ nameatrybut został zakodowany na stałe. ( a[i].nameNie nie używać funkcja jest nameargumentem, jest to równoważne a[i]['name'].)
nnnnnn