Accumulator not reset between 2 consecutive calls to R.reduce in a R.pipe

时间:2016-04-15 15:04:18

标签: ramda.js

Considering this code, using Ramda 0.21.0:

var iteratee = (acc, [k, v]) => {
  acc[k] = ++v;
  return acc
}

var foo = R.pipe(
  R.toPairs,
  R.reduce(iteratee, {})
)

console.log(foo({ a: 1, b: 2})) // { a: 2, b: 3 }
console.log(foo({ c: 3, d: 4})) // { a: 2, b: 3, c: 4, d: 5 }

Why does the second call to foo display { a: 2, b: 3, c: 4, d: 5 } instead of { c: 4, d: 5 }?

Is there some kind of memoization going on? I would expect the initial value of acc to be reset to {} each time foo is applied.

1 个答案:

答案 0 :(得分:4)

这个答案大多扩展了@iofjuupasli

的评论

问题是累加器对象的变异。你在foo的定义中创建一个,在每个调用中重复使用,然后你在iteratee更新它(可怕的名字,恕我直言。称之为bar或其他东西。:-)) 。有几种方法可以解决这个问题。一种可能是确保在每次调用foo时传递新的累加器:

var iteratee = (acc, [k, v]) => {
  acc[k] = ++v;
  return acc
}

var foo = R.pipe(
  R.toPairs,
  list => R.reduce(iteratee, {}, list)
)

foo({ a: 1, b: 2}); //=> {"a": 2, "b": 3}
foo({ c: 3, d: 4}); //=> {"c": 4, "d": 5}

这有效,但感觉不满意。也许更有帮助的是避免在每次传递时改变累加器对象。 assoc将创建一个新对象,尽可能多地重用前一个对象:

var iteratee = (acc, [k, v]) => R.assoc(k, v + 1, acc)

var foo = R.pipe(
  R.toPairs,
  R.reduce(iteratee, {})
);

foo({ a: 1, b: 2}); //=> {"a": 2, "b": 3}
foo({ c: 3, d: 4}); //=> {"c": 4, "d": 5}

这看起来更干净。但实际上Ramda有一个更简单的解决方案。 map函数将对象视为要映射的函子。将其与inc相结合,只需增加一个值,我们就可以这样做:

var foo = R.map(R.inc);

foo({ a: 1, b: 2}); //=> {"a": 2, "b": 3}
foo({ c: 3, d: 4}); //=> {"c": 4, "d": 5}

感觉非常干净!