20

I find myself presented with this pattern quite a bit. I have an array of objects that I get back from my api, and I need to manipulate just one of the properties in all of the objects.

Is there a way using ES6/Babel or Typescript to get that pattern to be a little more declarative?

Looking for some neat destructuring trick or something along those lines.

const data = [{ foo: 1, bar: 2}, 
              { foo: 2, bar: 3},
              { foo: 3, bar: 4}];

const increment = a => a + 1;

// Here is my typical pattern
const result = data.map(o => {
    o.foo = increment(o.foo);
    return o;
})

console.log(result);
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
Richard.Davenport
  • 1,453
  • 3
  • 16
  • 31
  • do you need a new object? – Nina Scholz Feb 17 '17 at 20:12
  • What is the point of the new array if you're modifying the objects inside the old one in place anyway? – pvg Feb 17 '17 at 20:22
  • @pvg Yeah, I could edit my question. The point is that there may be some operation that I need to perform on a property. Whether or not the array needs to be a new array or just mutated was kind of secondary, I really just wanted to see if there was a more declarative way to write what I seem to do a lot, in my current project. – Richard.Davenport Feb 17 '17 at 20:34
  • I guess I'm not entirely understanding the 'declarative' criterion. That is, what, beyond direct use of `map` and `forEach` makes this more... something? Is there a construct in another language you have in mind? – pvg Feb 17 '17 at 20:39

5 Answers5

36

Object spread (...), available in Babel using the Stage 3 preset, does the trick:

const data = [
  { foo: 1, bar: 2 }, 
  { foo: 2, bar: 3 },
  { foo: 3, bar: 4 },
];

const increment = a => a + 1;

const result = data.map(o => ({ ...o, foo: increment(o.foo) }));
console.log(result);
Jordan Running
  • 102,619
  • 17
  • 182
  • 182
8

This is a little more elegant I think - Object.assign is a good way to update an item in an object

const data = [{
  foo: 1,
  bar: 2
}, {
  foo: 2,
  bar: 3
}, {
  foo: 3,
  bar: 4
}];

const increment = a => a + 1;

// Here is my typical pattern
const result = data.map(o => Object.assign(o, {foo: increment(o.foo)}))

console.log(result);
Simon H
  • 20,332
  • 14
  • 71
  • 128
3

For a in situ version, you could use a closure over the key of the object and take the object as parameter.

const data = [{ foo: 1, bar: 2 }, { foo: 2, bar: 3 }, { foo: 3, bar: 4 }];
const increment = k => o => o[k]++;

data.forEach(increment('foo'));
console.log(data);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • I really love this technique! I'm a little worried other devs might question what's happening, but I guess it'd be a good learning session! – Richard.Davenport Feb 17 '17 at 21:01
0

what about:

data.map(d => (
  Object.assign({}, d, {foo: d.foo + 1})
));
Hitmands
  • 13,491
  • 4
  • 34
  • 69
0

Isn't all this completely equivalent to just:

const data = [{ foo: 1, bar: 2 }, { foo: 2, bar: 3 }, { foo: 3, bar: 4 }];

const result = data.slice();
result.forEach(e => e.foo++);
console.log(result);
pvg
  • 2,673
  • 4
  • 17
  • 31
  • Yep, in this exact scenario, you rewrote the code exactly, but in reality I'm not incrementing properties by 1. There could be operations for dates, strings, prototypical objects with methods on them. So while your answer is correct, it doesn't actually answer my problem. – Richard.Davenport Feb 17 '17 at 20:48
  • @Richard.Davenport Ok so this about the transformations themselves? ES6 destructurinng, computer property names and all that can probably help there but it's hard to talk about concretely without some idea of the transformations you are doing. `forEach` and `map` are already 'declarative' (in fact, in your example since you're mutating rather than mapping, you could argue that `forEach` is more 'declarative'. – pvg Feb 17 '17 at 21:02
  • I think we're talking past each other, mind you, we both have opinions and that's what most of this is. You bring up excellent points, but your code, while concise, doesn't read well. Again, opinion. Have you ever read a grammatically terrible tweet? Or a pretentious tweet with latin in it? It's like, we're both speaking english, but having a hard time with the semantics. – Richard.Davenport Feb 17 '17 at 21:16
  • Right, but I'm trying to understand the transformations you have in mind in which the in which greater reliance on composition and potentially newer syntactic features come into play. There are such things, entire APIs and libraries of them, but they tend to be constrained to specific classes of transformation. I don't think there is a general answer for the general case. (Agreed about opinions, though, for this particular transformation, my code is a vast improvement over yours :) – pvg Feb 17 '17 at 21:22
  • 1
    Hahahaha, whatever you say pvg! I think we'd work well together. – Richard.Davenport Feb 17 '17 at 21:28