0

In a chain of array manipulation methods, I'd like to alter a property of each item in the array.

Each of the available methods has a problem when used this way:

  • Array.prototype.forEach doesn't return the array
  • Array.prototype.map requires that each item is returned
  • Array.prototype.filter requires that a "true" value is returned for each item

Does Array have a method that allows each item in an array to be manipulated, and returns the array?

I've resorted to using either map or filter, but that feels like a workaround which shouldn't be necessary.

A contrived example:

var items = [
   { name: 'Smith' }, 
   { name: 'Jones' }, 
   { name: 'Simpson' }
];

filter:

return items.filter(function(item) {
   item.fullname = 'Professor ' + item.name;
   return true;
});

map:

return items.map(function(item) {
   item.fullname = 'Professor ' + item.name;
   return item;
});

forEach:

items.forEach(function(item) {
   item.fullname = 'Professor ' + item.name;
});

return items;

??:

return items.someMethod(function(item) {
   item.fullname = 'Professor ' + item.name;
});
Alf Eaton
  • 5,226
  • 4
  • 45
  • 50
  • `forEach` would probably be most correct here, because it's saying "for each item, set its fullname property to 'Professor' name". – Niet the Dark Absol Jun 24 '14 at 15:19
  • Generally speaking, though, chaining stuff usually means you're trying to do too much at once. In my opinion, anyway. – Niet the Dark Absol Jun 24 '14 at 15:20
  • 1
    Since you want a method _"that allows each item in an array to be manipulated, and returns the array?"_, isn't `map` the obvious one? Or am I not sure what the problem is? – Andy Jun 24 '14 at 15:20
  • 1
    `Array.prototype.each = function(){ this.forEach.apply(this, arguments); return this; };` might do – Bergi Jun 24 '14 at 15:41
  • "item.fullname = 'Professor ' + item.name;" is not all that much shorter than "return item.fullname = 'Professor ' + item.name, 1;", which makes your "abuse" of filter easier... – dandavis Jun 24 '14 at 16:52

2 Answers2

3

No, there is no such method. Common helper libraries (Underscore, jQuery, Prototype, …) do have each iteration methods that return the array, though.

The forEach method does not return the original array because it is not made for functional chaining, but for executing side effects.

The map method is the tool of choice here. It comes from functional programming, and its purpose is to create a new array of transformed objects, which is naturally returned from the function. By mutating your objects instead of creating new ones, you were disregarding this paradigm, but you can still use it when you return the (mutated) objects.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks for the clear statement and recommendation. Do you have a link to anything that expands on "forEach is not made for functional chaining, but for executing side effects"? – Alf Eaton Jul 06 '14 at 13:47
  • Such a function simply doesn't exist in a [pure (side-effect free) functional language](https://en.wikipedia.org/wiki/Purely_functional). I don't have any links at hand that discuss this extensively, but I remember the statement to appear in [this presentation](http://scott.sauyet.com/Javascript/Talk/FunctionalProgramming/#slide-135). It is a mere [loop statement](https://en.wikipedia.org/wiki/Foreach). – Bergi Jul 06 '14 at 15:05
1

Have you considered reduce?

The reduce() method applies a function against an accumulator and each value of the array (from left-to-right) has to reduce it to a single value.

var items = [
   { name: 'Smith' }, 
   { name: 'Jones' }, 
   { name: 'Simpson' }
];

items.reduce(function (memo, item) {

    item = 'Sir ' + item + ', the first.';

    if (somePredicate(item)) {
        memo.push(item);
    }

    return memo;

}, []);
pdoherty926
  • 9,895
  • 4
  • 37
  • 68