Lodash sumBy String Concatenation on Nested Keys
While the Lodash _.sumBy function is primarily intended
to compute numerical sums across collections, passing a string path
pointing to nested string values causes it to return a concatenated
string instead of a number. This behavior is the direct result of how
Lodash resolves nested object paths combined with the implicit type
coercion rules of JavaScript's addition operator (+) within
its internal iteration loop.
When invoking
_.sumBy(collection, 'deeply.nested.property'), Lodash first
transforms the string path into an iteratee function. Internally, Lodash
delegates this to baseIteratee, which creates a property
accessor via basePropertyDeep and baseGet. As
Lodash iterates over the collection, it traverses each object along the
dot-notation path, safely extracting the deeply nested value even if
intermediate parent keys are missing.
Once the nested value is extracted, execution shifts to Lodash's
internal baseSum algorithm. The implementation of
baseSum follows a straightforward accumulator pattern:
function baseSum(array, iteratee) {
var result,
index = -1,
length = array.length;
while (++index < length) {
var current = iteratee(array[index]);
if (current !== undefined) {
result = result === undefined ? current : (result + current);
}
}
return result;
}The string concatenation occurs because of two key steps inside this loop:
- Accumulator Initialization: The variable
resultbegins asundefined. On the first iteration where the nested lookup returns a valid (non-undefined) string, the ternary conditionresult === undefined ? current : (result + current)assigns that initial string directly toresult. - Evaluation via the
+Operator: On subsequent iterations,resultalready holds a string primitive. When the loop executesresult + current, the native JavaScript addition operator encounters at least one string operand. Rather than performing mathematical addition, JavaScript defaults to string concatenation, joining the values sequentially.
Because baseSum does not validate that
current is a number, nor does it convert inputs via
Number() or parseFloat(), the accumulation
purely reflects native JavaScript coercion. If intermediate properties
are undefined, they are skipped; however, if deeply nested
properties resolve to null, numbers, or booleans in a mixed
collection, JavaScript coerces those values into string representations
during the addition phase (for example, "text" + null
becomes "textnull").