Почему оболочка Lodash не работает с forEach?

Я столкнулся с этим при обновлении манипуляций с цепочкой lodash и не понимаю, почему это будет работать по-другому.

Я сузил его до цепочки forEach на обертке lodash.

let tab = [{a:1, b:1}, {a:1, b:2}, {a:2, b:1},{a:2, b:2}]
let res = _(tab).forEach(el => {el.c = 3; return el;}).groupBy('a')
console.log(res)

https://jsbin.com/wahokezeja/edit?js,console

Это вызовет ошибку:

"TypeError: _(...).forEach(...).groupBy не является функцией

Однако при подключении к карте все работает нормально

let tab = [{a:1, b:1}, {a:1, b:2}, {a:2, b:1},{a:2, b:2}]
let res = _(tab).map(el => {el.c = 3; return el;}).groupBy('a').value()
console.log(res)

https://jsbin.com/mofusel/1/edit?js,console

Это как-то связано с тем, что _(tab).forEach возвращает массив, а не оболочку lodash, но не понимаю, почему это происходит.


person Jason Rogers    schedule 04.10.2019    source источник


Ответы (1)


Lodash forEach не ведет себя точно так же, как JS forEach, но и не совсем как карта. _.map([1,2,3],a=>2*a) вернет массив [2,4,6], как и следовало ожидать, однако _.forEach([1,2,3],a=>a*2) вернет [1,2,3]. Он предназначен для выполнения операции над элементом массива (например, JS [].forEach), но он не возвращает измененный массив, а вместо этого возвращает исходный массив для удобства. Если вы хотите изменить массив, используйте map.

Попробуйте следующее:

var arr=[1,2,3]; 
var rslt=_(arr).forEach(v=>v*2); 
console.log(arr===rslt);

и вы можете видеть, что он возвращает true - «результат» forEach, завернутый или нет, является просто исходным массивом.

После изучения кода lodash и некоторых полезных комментариев от @VLAZ ключом к пониманию этого является ленивая оценка, связанная с цепочкой (что является точкой переноса массива, упомянутой выше). Связанные (обернутые) методы допускают ленивую оценку. Если вы вызываете _(array).map(fn1).map(fn2).value(), то оценка не выполняется до тех пор, пока не будет вызван метод значения. Это хорошее повышение эффективности, поскольку (в этом случае) генерируется только один выходной массив (в отличие от стандартного метода карты JS, который вызывает создание промежуточных массивов после каждого шага). Однако forEach отличается - требует немедленного выполнения, поэтому цепочка заканчивается.

person Euan Smith    schedule 04.10.2019
comment
Это кажется преднамеренным - это задокументировано: Методы оболочки, которые не цепочка по умолчанию: [...] forEach [...]. На самом деле, я был удивлен, что _.forEach возвращает коллекцию, я предполагал, что это не так. Итак, для меня было совершенно логичным, что forEach завершит цепочку, поскольку обычно это была бы терминальная операция, аналогичная reduce. Хотя я предполагаю, что даже если _.forEach возвращает коллекцию нормально, вы не можете эффективно связать ее, поскольку операции обычно ленивы, но forEach не подходит. - person VLAZ; 04.10.2019
comment
@VLAZ Да, только что пришел к такому же выводу, покопавшись в коде подчеркивания. Цепочка предназначена для ленивой оценки, и по причине ожидаемого поведения forEach должна завершить эту цепочку. - person Euan Smith; 04.10.2019