396

If I have an array of strings, I can use the .join() method to get a single string, with each element separated by commas, like so:

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

I have an array of objects, and I’d like to perform a similar operation on a value held within it; so from

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

perform the join method only on the name attribute, to achieve the same output as before.

Currently I have the following function:

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

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

  return out.join(", ");
}

There’s nothing wrong with that code, it works, but all of a sudden I’ve gone from a simple, succinct line of code to a very imperative function. Is there a more succinct, ideally more functional way of writing this?

Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
jackweirdy
  • 5,632
  • 4
  • 24
  • 33

14 Answers14

688

If you want to map objects to something (in this case a property). I think Array.prototype.map is what you're looking for if you want to code functionally.

(fiddle)

If you want to support older browsers, that are not ES5 compliant you can shim it (there is a polyfill on the MDN page above). Another alternative would be to use underscorejs's pluck method:

var users = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
var result = _.pluck(users,'name').join(",")
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • 1
    This one. However you'll need to shim .map prototype member for IE < 9, if you need to support this retro-browsers. – Tommi May 17 '13 at 11:15
  • 1
    @Tommi good comment. I added an explanation about a polyfill as well as a suggestion to use pluck if using underscore. – Benjamin Gruenbaum May 17 '13 at 11:20
  • @jackweirdy I'm glad I could help :) for what it's worth, just like map/reduce/filter are not 'pythonic' and comprehensions are the other way holds. In JavaScript's new spec in progress (Harmony) as well as some browsers already (firefox) you have pythonic comprehensions. You can do `[x.name for x of users]` (spec http://wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions) . Worth mentioning that just like using map/reduce/filter isn't very 'pythonic' the other way probably holds in JavaScript. CoffeeScript is cool with them though http://coffeescript.org/ . – Benjamin Gruenbaum May 17 '13 at 11:28
  • 1
    Just an update - array comprehensions have been dropped out of ES6, maybe we'll get them in ES7. – Benjamin Gruenbaum Nov 18 '14 at 13:06
  • 1
    In coffeescript: `users.map((obj) -> obj.name).join(', ')` – Kesha Antonov Dec 16 '15 at 21:04
  • Update: now you should use _.map: `var result = _.map(users,'name').join(", ");`, and if users is array of users and every user has name so: `var result = _.map(users,'user.name').join(", ");` – Edan Chetrit Mar 08 '17 at 10:22
  • update in TypeScript: var str = [{name: "Joe", age: 22}, {name: "Kevin", age: 24}, {name: "Peter", age: 21} ].map(elem => elem.name).join(", "); – Pierre Sep 28 '22 at 18:28
91

Well you can always override the toString method of your objects:

    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(", ");
    console.log(result);
LWC
  • 1,084
  • 1
  • 10
  • 28
basilikum
  • 10,378
  • 5
  • 45
  • 58
  • 2
    That's a pretty interesting approach, I didn't realise you could do this in JS. The data is coming from an API though, so it's probably not suitable for my use case, but it's definitely food for thought. – jackweirdy May 17 '13 at 11:19
  • 5
    Doesn't seem very DRY – BadHorsie Jun 24 '16 at 10:13
  • 5
    @BadHorsie No, it is not DRY. But of course you could define a single function outside of the array and then assign this function to the `toString` method of all items, using a for-loop. Or you could use this approach when dealing with constructor functions, by adding one `toString` method to the `prototype` property for the constructor. This example however was just supposed to show the basic concept. – basilikum Dec 05 '16 at 14:33
  • Truly interesting approach! Does this approach have any effects on performance on large arrays? – Parag Jadhav May 06 '21 at 09:51
36

On node or ES6+:

const users = [{
    name: "Joe",
    age: 22
  },
  {
    name: "Kevin",
    age: 24
  },
  {
    name: "Peter",
    age: 21
  }
];

console.log(
  users.map(u => u.name).join(', ')
);
double-beep
  • 5,031
  • 17
  • 33
  • 41
ariel
  • 15,620
  • 12
  • 61
  • 73
23

I've also come across using the reduce method, this is what it looks like:

console.log(
    [
        {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) is so the first element is treated correctly, but the rest (where a is a string, and so a.name is undefined) isn't treated as an object.

Edit: I've now refactored it further to this:

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

which I believe is cleaner as a is always a string, b is always an object (due to the use of the optional initialValue parameter in reduce)

Edit 6 months later: Oh what was I thinking. "cleaner". I've angered the code Gods.

JustCarty
  • 3,839
  • 5
  • 31
  • 51
jackweirdy
  • 5,632
  • 4
  • 24
  • 33
  • Thanks :D `reduce` isn't as widely supported as `map` (which is weird, given they're kind of sisters) so I'm still using your answer :) – jackweirdy Jun 11 '13 at 22:38
  • 2
    Looking at my edit 6 months later, I have now decided I wouldn't hire myself... It's crafty, but not clean. – jackweirdy Nov 22 '13 at 15:56
  • I believe this is the best answer, because the reduce method iterates only one time O(n), and map+join iterates 2 times O(2n). – YouneL Oct 09 '20 at 13:54
  • Small nitpick for students reading this. Big O drops constants. O(2n) is actually O(n), making the two "equal" where Big O is concerned. Readability is (almost) always more important than performance, especially if it's only a constant factor difference. – CyberEd Jun 21 '21 at 14:53
12

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

const joinedStr = lists.map((list) => list.name).join(" ")

console.log('joined',joinedStr)

This should do it since map will return an array of strings and you can join it then

Prajwal Panta
  • 121
  • 1
  • 4
9

I don't know if there's an easier way to do it without using an external library, but I personally love underscore.js which has tons of utilities for dealing with arrays, collections etc.

With underscore you could do this easily with one line of code:

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

Ed_
  • 18,798
  • 8
  • 45
  • 71
  • I've dabbled underscore before, I was hoping for a way to do it in native JS though - it's a bit daft to pull in a whole library to use one method once :) – jackweirdy May 17 '13 at 11:20
  • (where `arr` is your array, naturally) – Ed_ May 17 '13 at 11:20
  • fair enough! I find myself using underscore all over the place now so it's not an issue. – Ed_ May 17 '13 at 11:21
6

lets say the objects array is referenced by the variable users

If ES6 can be used then the easiest solution will be:

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

If not, and lodash can be used so :

 _.map(users, function(user) {
     return user.name;
 }).join(', ');
C'estLaVie
  • 263
  • 1
  • 3
  • 9
3

If object and dynamical keys: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}

Object.keys(myObject).map(function (key, index) {
    return myObject[key]
}).join(', ')
Davron Achilov
  • 536
  • 4
  • 14
1

An old thread I know but still super relevant to anyone coming across this.

Array.map has been suggested here which is an awesome method that I use all the time. Array.reduce was also mentioned...

I would personally use an Array.reduce for this use case. Why? Despite the code being slightly less clean/clear. It is a much more efficient than piping the map function to a join.

The reason for this is because Array.map has to loop over each element to return a new array with all of the names of the object in the array. Array.join then loops over the contents of array to perform the join.

You can improve the readability of jackweirdys reduce answer by using template literals to get the code on to a single line. "Supported in all modern browsers too"

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

not sure, but all this answers tho they work but are not optiomal since the are performing two scans and you can perform this in a single scan. Even though O(2n) is considered O(n) is always better to have a true 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;
}

This might look like old school, but allows me to do thig like this:

skuCombined = Join(option.SKUs, ',', 'SkuNum');
0

you can convert to array so get object name

var objs = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
document.body.innerHTML = Object.values(objs).map(function(obj){
   return obj.name;
});
SAWA4D
  • 21
  • 2
0

This worked for me:

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

 return users.map((user: { name: string; }) => user.name).join(", ");
CHEK MYMA
  • 31
  • 5
-1

try this

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
  • 109
  • 1
  • 2
  • 8
  • 2
    This is the same as the function in the question, except less versatile because you've hard-coded the `name` attribute. (`a[i].name` does _not_ use your function's `name` argument, it is equivalent to `a[i]['name']`.) – nnnnnn May 17 '13 at 11:35
-1

Easiest way:

const oldArrayOfObjects = [
{name: "Bob", age:40}, 
{name: "Andrew", age:25}, 
{name: "Peter", age:30}];
const newArrayOfStringNames = oldArrayOfObjects.map((OA) => OA.name);
const newArrayOfAges = oldArrayOfObjects.map((OA) => OA.age);
console.log({newArrayOfStringNames, newArrayOfAges})
JPMC
  • 1
  • Though your answer uses the map feature to help solve the original question, it does not solve the original problem, i:e a clean method/utility. Your code also depends on the structure of the data to construct the join, so it's also not functional and not scalable. Imagine a utility function like this `join(array, objPath)` or `join(objPath)(array)`. Try to implement this join function. – Vijay Dev May 22 '21 at 05:01