CoffeeScript
lodash

_.forEachをCoffeeScriptで使うときの注意点

More than 3 years have passed since last update.


tl;dr

もったいぶって書いてますが、CoffeeScriptの関数の最後の値がなんであってもreturnされます。

_.forEachであればコールバックでreturn falseされると巡回が終わってしまう。これに最後の行にbool = false的なものを書くとreturn bool = falseとなり、意図せずfalsereturnされてしまうので、気を付けましょう。


lodashの_.forEachがすべての要素を巡回しない問題

例えば全部のactiveプロパティをfalseにしようとします。


example.coffee

describe '_.forEach', ->

objects = null

beforeEach ->
objects = [
id: '1'
active: true
,
id: '2'
active: true
,
id: '3'
active: true
]



case1


example.coffee

    it 'should make all inactive', ->

_.forEach objects, (obj)->
obj.active = false

expect object[0].active
.toBe false
expect object[1].active
.toBe false
expect object[2].active
.toBe false


あれ?失敗するぞ。


case2


example.coffee

    it 'should sweep all element', ->

count = 0
_.forEach objects, (obj)->
count = count + 1
obj.active = false

expect count
.toBe 3


あれれ?また失敗するぞ。


case2を修正


example.coffee

    it 'should sweep all element', ->

count = 0
_.forEach objects, (obj)->
obj.active = false
count = count + 1

expect count
.toBe 3


おっ、成功した。

count = count + 1obj.active = falseを入れ替えると結果が変わる。

ああ、いつでも忘れてはいけないこの仕様……


JavaScriptを見てみる


example.js

  it('should sweep all element', function() {

var count;
count = 0;
_.forEach(objects, function(obj) {
count = count + 1;
return obj.active = false;
});
return expect(count).toBe(3);
});


example.js

  it('should sweep all element', function() {

var count;
count = 0;
_.forEach(objects, function(obj) {
obj.active = false;
return count = count + 1;
});
return expect(count).toBe(3);
});

そう、犯人はreturn obj.active = false;です。


lodashのドキュメントに書いてあります


Iterates over elements of collection invoking iteratee for each element. The iteratee is bound to thisArg and invoked with three arguments; (value, index|key, collection). Iterator functions may exit iteration early by explicitly returning false.


https://lodash.com/docs#forEach

_.forEachのコールバックでreturn falseすると巡回を止めてしまいます。


example.coffee

    it 'should make all inactive', ->

_.forEach objects, (obj)->
obj.active = false
true

expect objects[0].active
.toBe false
expect objects[1].active
.toBe false
expect objects[2].active
.toBe false


これでOK。


ちなみに

jQuery$.eachも同じ仕様のようですが、AngularJSangular.forEachは巡回をやめないようです(!!)