JavaScript has multiple array iterators that allow you to transform an existing array into new one. The iterator methods are very attractive. They allow writing very short code that reads naturally. The iterators also eliminate 'off by one' errors common in manual loops.
1 | // does given array have an even number? |
Array.prototype.some
computes a boolean value by executing the given callback
over every item. As soon as callback returns a truthy value, the iteration stops.
Thus we are computing single value from the array, while leaving the array unchanged.
There are other array iterators that compute a single result without modifying the
input array: Array.prototype.every
and Array.prototype.reduce
.
There are iterators that also leave the original array unchanged, but return a new array. Each item in the new array is an item from the input array passed through callback function.
1 | function mul2(x) { return x * 2; } |
Another great example is Array.prototype.filter
that creates new array, but only
from items that when passed through callback function return truthy value
1 | [1, 2, 3].filter(isEven); //=> [2] |
Finally, there is forEach
iterator. It executes the given callback for each item.
Typically, I use forEach
to modify array's elements in place.
1 | var numbers = [1, 2, 3]; |
So far we have 3 iterator types: compute a predicate, create new array based on the old one,
and generic forEach
iterator. There are no equivalent iterators to do the same with
JavaScript objects. Luckily, a library lodash has
multiple functions that operate over collections. A collection could be an array or an
object. In this blog post I will show examples of _ iterators that manipulate objects.
Predicates
Let us start with Array.prototype.some
equivalent. To check if there is some property
inside an object that has even value we could write the code in two ways.
1 | function isEven(x) { return x % 2 === 0; } |
Each iteration of _.some
runs the callback function passing value and key arguments.
This is very similar to Array.prototype.some
callback signature that passes
item and index arguments. In fact you can think of an index into array as being an integer
key (instead of string keys allowed in objects). Sidenote: do not get burned by the
second argument to the callback.
Use unary adaptor to avoid madness.
_.some
eliminates a lot of the boilerplate code. Entire Object.keys(obj)
extraction
is gone (it is inside _.some
function). Plus the value is passed directly to the callback,
without needing the manual obj[key]
lookup.
There is _.every
predicate that iterates over a collection
1 | function isThreeLetter(value, key) { |
Filtering (picking)
Let us now construct new objects based on existing ones. First, let us filter properties. When dealing with objects we can filter by key, by value or by a combination of both.
The _.filter
constructs a new array instead of the object, and thus we cannot use it
1 | function startsWithB(s) { |
When dealing with objects, the filtering function is called _.pick
.
1 | // filtering by key |
Mapping (transforming)
_.transform
provides generic method for constructing an object from an existing one.
1 | _.transform(obj, function (result, value, key) { |
Notice that the first callback argument is the new object, and we are free to set or skip properties.
forEach
Finally, _.forEach
function is for general iteration over the object's properties.
The callback arguments are value, key and the object itself, so you can modify the existing object.
1 | _.forEach(obj, function (value, key, o) { |
Conclusion
I love iterators. They are slower than iterating over an array using integer indices, and sometimes callback gets unexpected arguments due to index / key. Yet, the code is much much nicer when using iterators. Using lodash allows using almost the same functions to iterate over objects as over arrays.