Avindra Goolcharan
Avindra Goolcharan

Reputation: 4211

A more elegant way to write this .map call?

I have some code that essentially takes an array of objects and just adds an additional key to each item. I want to be able to express this as tersely as possible as an experiment.

let fruits = [
   {"type" : "orange"},
   {"type" : "apple"},
   {"type" : "banana"}
];
console.log(fruits.map((fruit) => {
         fruit.price = "$1.00";
         return fruit;
}));

Currently, this works, but it's certainly no one liner and the return statement is still in there, and I feel like there's a way to get rid of it given the fat arrow syntax.

Upvotes: 0

Views: 174

Answers (5)

CodingIntrigue
CodingIntrigue

Reputation: 78525

One approach would be to use Object.assign to extend the object and also return the resulting newly-created object:

console.log(fruits.map(fruit => Object.assign(fruit, { price: "1.00" })));

Babel REPL Example

This removes the need for the return keyword, but it's hardly the biggest space-saver. It is also equivalent to what you already have (in that the original fruit object is modified. As joews points out below, if you wanted to leave the original array in-tact you can use an empty target object like so:

Object.assign({}, fruit, { price: "1.00"});

This will ensure that your original array is unmodified (which may or may not be what you want).

Finally, combining this with the spread operator gives us:

console.log(fruits.map(fruit => ({...fruit, price: "1.00" })));

Upvotes: 3

Bergi
Bergi

Reputation: 664415

A mapping function should almost always be pure. If you are only going to modify the objects, a simple loop will do better (for (let fruit of fruits) fruit.price = …; console.log(fruits);).

So when you're returning a new object, a one-liner will be easy:

console.log(fruits.map(({type}) => ({type, price:"$1.00"})));

If you've got many properties, or properties you don't know, then Object.assign({}, …) is your friend (as in @joews' comment to @RGraham's answer).

Upvotes: 0

Sean Vieira
Sean Vieira

Reputation: 159895

There are many ways to do this:

Side effects with the comma operator

If you want to do it inline you can use the comma operator (though it's a little obscure):

fruits.map((fruit) => (fruit.price = "$1.00", fruit))

We could also use && since assignment returns the assigned value and "$1.00" is truthy but the comma operator is more general, since we can also set false or 0 and have everything continue to work.

Higher-ordered functions

It's probably better to make a helper function, however:

// We're currying manually here, but you could also make the signature
// setter(name, value) and use your function of choice to curry when you need to.
function setter(name) {
  return (value) => (obj) => {
    obj[name] = value;
    return obj;
  }
}

Then you can use:

fruits.map(setter("price")("$1.00"))

Embrace mutability

As @Suppen points out in their comment, because normal JavaScript objects are mutable you can also avoid the map and use forEach instead:

fruits.forEach(fruit => fruit.price = "$1.00");
// Each element in fruits has been modified in-place.

Upvotes: 2

fuzetsu
fuzetsu

Reputation: 173

Could do something like this, not recommended for readibility but technically one line.

fruits.map(fruit => (fruit.price = "$1.00") && fruit);

As others have mentioned this method just adds a property to the object and does not copy it. A simple way to keep this as a one liner, use a map and actually create a copy would be:

fruits.map(fruit => Object.assign({price: "$1.00"}, fruit));

Object.assign() will assign all the properties of fruit to the object { price: "$1.00" } and return it.

Live example:

"use strict";

let log = function() {
  output.textContent += [].join.call(arguments, ' ') + '\n\n';
};

log('# MAP (OR FOREACH) WITHOUT ASSIGN');

let fruits = [
   {"type" : "orange"},
   {"type" : "apple"},
   {"type" : "banana"}
];
let newfruits = fruits.map(fruit => (fruit.price = "$1.00") && fruit);

log('fruits', JSON.stringify(fruits));
log('newfruits', JSON.stringify(newfruits));
log('^-- Both are modified since newfruits its a new array with the same objects');

log('# MAP WITH ASSIGN');

fruits = [
   {"type" : "orange"},
   {"type" : "apple"},
   {"type" : "banana"}
];
newfruits = fruits.map(fruit => Object.assign({price: "$1.00"}, fruit));

log('fruits', JSON.stringify(fruits));
log('newfruits', JSON.stringify(newfruits));
log('^-- Only newfruits is modified since its a new array with new objects');
pre {
  word-wrap: break-word;
}
<pre id="output"></pre>

Upvotes: 2

Andreas
Andreas

Reputation: 21881

You can also use .forEach() instead of .map() to directly modify fruits if you don't need the original version of fruits

fruits.forEach((fruit) => fruit.price = "$1.00");

http://www.es6fiddle.net/igwdk0gk/

Upvotes: 3

Related Questions