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?


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(",")

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"

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.


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(', ')


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


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(', ');

2

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

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

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

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');

-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"

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.