Lodash _.reduce on Plain Object Without Accumulator
When you execute Lodash's _.reduce method on a plain
JavaScript object without passing an initial accumulator value, Lodash
automatically assigns the value of the object's first enumerable
property as the initial accumulator. It then begins iterating through
the remaining properties starting from the second entry, passing the
running accumulator, the current property value, the current key, and
the original object into your callback function. If the provided object
is empty, the function immediately returns undefined.
The Core Mechanism
Lodash normalizes collections across arrays and objects. When
accumulator is omitted from
_.reduce(collection, iteratee, [accumulator]), the method
defaults to extracting the first element:
- Initial Accumulator Assignment: Lodash resolves the object's own enumerable string keys. The value corresponding to the first resolved key is set as the accumulator.
- Iteration Offset: The first key-value pair is skipped in the iteration loop because its value is already assigned as the base accumulator.
- Iteratee Invocation: For every subsequent property,
the iteratee callback is invoked with four arguments:
(accumulator, value, key, collection). - Final Return Value: Once all remaining properties are processed, the final resolved accumulator is returned.
Code Example
const _ = require('lodash');
const scores = {
math: 85,
science: 90,
history: 75
};
const total = _.reduce(scores, (acc, value, key) => {
return acc + value;
});
console.log(total); // Output: 250In this execution:
- The initial accumulator becomes
85(the value ofmath). - The first iteratee call receives
(85, 90, 'science', scores). The return value becomes175. - The second iteratee call receives
(175, 75, 'history', scores). The return value becomes250.
Handling Empty Objects
If the plain object contains no own enumerable properties, Lodash cannot extract an initial value.
const emptyObj = {};
const result = _.reduce(emptyObj, (acc, value) => {
return acc + value;
});
console.log(result); // Output: undefinedBecause the iteratee is never called on an empty collection without
an initial accumulator, _.reduce evaluates to
undefined.
Important Considerations and Edge Cases
- Property Enumeration Order: JavaScript determines
key traversal order by numeric keys in ascending order first, followed
by remaining string keys in insertion order. If your object contains
mixed or numeric keys (e.g.,
{ 2: 'b', 1: 'a' }), the initial value will correspond to the first enumerated key (1), not necessarily the first key defined visually. - Accidental Type Coercion: If the object contains heterogeneous values (for instance, a mix of strings, numbers, or sub-objects), omitting the accumulator can lead to unexpected type coercion, such as string concatenation instead of addition.
- Transformations to Different Types: If your
intention is to transform an object into an array, a Map, or a newly
formatted object, you must explicitly supply an initial accumulator
(such as
[]or{}). Omitting it locks the accumulator's initial type to the data type of the first property value.