-
-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes #114
- Loading branch information
Showing
4 changed files
with
242 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { curryN, reduceRight, length } from 'ramda'; | ||
|
||
import isUndefined from './isUndefined'; | ||
|
||
/* eslint-disable max-len */ | ||
/** | ||
* Given an `Iterable`(arrays are `Iterable`), or a promise of an `Iterable`, | ||
* which produces promises (or a mix of promises and values), | ||
* iterate over all the values in the `Iterable` into an array and | ||
* reduce the array to a value using the given iterator function. | ||
* | ||
* Similar to {@link RA.reduceP|reduceP} except moves through the input list from the right to the left. | ||
* The iterator function receives two values: (value, acc), | ||
* while the arguments' order of reduceP's iterator function is (acc, value). | ||
* | ||
* @func reduceRightP | ||
* @memberOf RA | ||
* @since {@link https://char0n.github.io/ramda-adjunct/1.13.0|v1.13.0} | ||
* @category List | ||
* @sig | ||
* | ||
* ((MaybePromise b, Promise a) -> Promise a) -> MaybePromise a -> MaybePromise [MaybePromise b] -> Promise a | ||
* MaybePromise = Promise.<*> | * | ||
* | ||
* @param {Function} fn The iterator function. Receives two values, the current element from the list and the accumulator | ||
* @param {*|Promise.<*>} acc The accumulator value | ||
* @param {Array.<*>|Promise.<Array<*|Promise.<*>>>} list The list to iterate over | ||
* @return {Promise} The final, accumulated value | ||
* @see {@link RA.reduceP|reduceP}, {@link http://bluebirdjs.com/docs/api/promise.reduce.html|bluebird.reduce} | ||
* @example | ||
* | ||
* RA.reduceRightP( | ||
* (fileName, total) => fs | ||
* .readFileAsync(fileName, 'utf8') | ||
* .then(contents => total + parseInt(contents, 10)), | ||
* 0, | ||
* ['file1.txt', 'file2.txt', 'file3.txt'] | ||
* ); // => Promise(10) | ||
* | ||
* RA.reduceRightP( | ||
* (fileName, total) => fs | ||
* .readFileAsync(fileName, 'utf8') | ||
* .then(contents => total + parseInt(contents, 10)), | ||
* Promise.resolve(0), | ||
* ['file1.txt', 'file2.txt', 'file3.txt'] | ||
* ); // => Promise(10) | ||
* | ||
* RA.reduceRightP( | ||
* (fileName, total) => fs | ||
* .readFileAsync(fileName, 'utf8') | ||
* .then(contents => total + parseInt(contents, 10)), | ||
* 0, | ||
* [Promise.resolve('file1.txt'), 'file2.txt', 'file3.txt'] | ||
* ); // => Promise(10) | ||
* | ||
* RA.reduceRightP( | ||
* (fileName, total) => fs | ||
* .readFileAsync(fileName, 'utf8') | ||
* .then(contents => total + parseInt(contents, 10)), | ||
* 0, | ||
* Promise.resolve([Promise.resolve('file1.txt'), 'file2.txt', 'file3.txt']) | ||
* ); // => Promise(10) | ||
* | ||
*/ | ||
/* esline-enable max-len */ | ||
const reduceRightP = curryN(3, (fn, acc, list) => { | ||
const originalAccP = Promise.resolve(acc); | ||
const listLength = length(list); | ||
|
||
if (listLength === 0) { | ||
return originalAccP; | ||
} | ||
|
||
return Promise.resolve(list).then((iterable) => { | ||
const reducer = reduceRight((currentValueP, accP) => | ||
accP | ||
.then(previousValue => Promise.all([previousValue, currentValueP])) | ||
.then(([previousValue, currentValue]) => { | ||
if (isUndefined(previousValue) && listLength === 1) { | ||
return currentValue; | ||
} | ||
|
||
return fn(currentValue, previousValue); | ||
}) | ||
); | ||
|
||
return reducer(originalAccP, iterable); | ||
}); | ||
}); | ||
|
||
|
||
export default reduceRightP; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import R from 'ramda'; | ||
import sinon from 'sinon'; | ||
|
||
import RA from '../src/index'; | ||
import eq from './shared/eq'; | ||
|
||
|
||
describe.only('reduceRightP', function() { | ||
it('folds simple functions over arrays with the supplied accumulator', function() { | ||
const testAdd = RA.reduceRightP(R.add, 0, [1, 2, 3, 4]).then(actual => eq(actual, 10)); | ||
const testMultiply = RA.reduceRightP(R.multiply, 1, [1, 2, 3, 4]) | ||
.then(actual => eq(actual, 24)); | ||
|
||
return Promise.all([testAdd, testMultiply]); | ||
}); | ||
|
||
it('should not dispatch to objects that implement `reduce`', function() { | ||
const obj = { x: [1, 2, 3], reduce() { return 'override' } }; | ||
const test1 = RA.reduceRightP(R.add, 0, obj).then(actual => eq(actual, 0)); | ||
const test2 = RA.reduceRightP(R.add, 10, obj).then(actual => eq(actual, 10)); | ||
|
||
return Promise.all([test1, test2]); | ||
}); | ||
|
||
it('returns the accumulator for an empty array', function() { | ||
const testAdd = RA.reduceRightP(R.add, 0, []).then(actual => eq(actual, 0)); | ||
const testMultiply = RA.reduceRightP(R.multiply, 1, []).then(actual => eq(actual, 1)); | ||
const testConcat = RA.reduceRightP(R.concat, [], []).then(actual => eq(actual, [])); | ||
|
||
return Promise.all([testAdd, testMultiply, testConcat]); | ||
}); | ||
|
||
it('is curried', function() { | ||
const sum = RA.reduceRightP(R.add)(0); | ||
const cat = RA.reduceRightP(R.concat)(''); | ||
|
||
const testSum = sum([1, 2, 3, 4]).then(actual => eq(actual, 10)); | ||
const testConcat = cat(['1', '2', '3', '4']).then(actual => eq(actual, '1234')); | ||
|
||
return Promise.all([testSum, testConcat]); | ||
}); | ||
|
||
it('correctly reports the arity of curried versions', function() { | ||
const sum = RA.reduceRightP(R.add, 0); | ||
eq(sum.length, 1); | ||
}); | ||
|
||
it('tests initial value for promise', function() { | ||
const testAdd = RA.reduceRightP(R.add, Promise.resolve(0), [1, 2, 3, 4]) | ||
.then(actual => eq(actual, 10)); | ||
const testMultiply = RA.reduceRightP(R.multiply, Promise.resolve(1), [1, 2, 3, 4]) | ||
.then(actual => eq(actual, 24)); | ||
|
||
return Promise.all([testAdd, testMultiply]); | ||
}); | ||
|
||
it('tests returning initial value when iterable is empty', function() { | ||
const add = sinon.spy(); | ||
|
||
return RA.reduceRightP(add, 0, []) | ||
.then(actual => eq(actual, 0)) | ||
.then(() => eq(add.called, false)); | ||
}); | ||
|
||
it('tests returning initial value when iterable is empty (promise version)', function() { | ||
const add = sinon.spy(); | ||
|
||
return RA.reduceRightP(add, Promise.resolve(0), []) | ||
.then(actual => eq(actual, 0)) | ||
.then(() => eq(add.called, false)); | ||
}); | ||
|
||
it('tests if initial value is undefined', function() { | ||
const add = sinon.spy(); | ||
|
||
return RA.reduceRightP(add, undefined, [1]) | ||
.then(actual => eq(actual, 1)) | ||
.then(() => eq(add.called, false)); | ||
}); | ||
|
||
it('tests if initial value is undefined (promise version)', function() { | ||
const add = sinon.spy(); | ||
|
||
return RA.reduceRightP(add, Promise.resolve(), [1]) | ||
.then(actual => eq(actual, 1)) | ||
.then(() => eq(add.called, false)); | ||
}); | ||
|
||
it('tests iterator wrapped in the promise', function() { | ||
return RA.reduceRightP(R.add, 0, Promise.resolve([1, 2, 3])).then(actual => eq(actual, 6)); | ||
}); | ||
|
||
it('tests iterator containing values and promises', function() { | ||
return RA.reduceRightP(R.add, 0, [1, Promise.resolve(2), 3]).then(actual => eq(actual, 6)); | ||
}); | ||
|
||
it('tests iterator wrapped in promise containing values and promises', function() { | ||
return RA.reduceRightP(R.add, 0, Promise.resolve([1, Promise.resolve(2), 3])) | ||
.then(actual => eq(actual, 6)); | ||
}); | ||
|
||
it('tests iterator function returning promises', function() { | ||
return RA.reduceRightP( | ||
R.pipe(R.add, Promise.resolve.bind(Promise)), | ||
0, | ||
Promise.resolve([1, Promise.resolve(2), 3]) | ||
) | ||
.then(actual => eq(actual, 6)); | ||
}); | ||
|
||
it('tests difference between reduceRightP reduceP', function() { | ||
const cat = RA.reduceP(R.concat)(''); | ||
const catRight = RA.reduceRightP(R.concat)(''); | ||
const catRightFlipped = RA.reduceRightP(R.flip(R.concat))(''); | ||
|
||
const testCat = cat(['1', '2', '3', '4']).then(actual => eq(actual, '1234')); | ||
const testCatRight = catRight(['1', '2', '3', '4']).then(actual => eq(actual, '1234')); | ||
const testCatRightFlipped = catRightFlipped(['1', '2', '3', '4']).then(actual => eq(actual, '4321')); | ||
|
||
return Promise.all([testCat, testCatRight, testCatRightFlipped]); | ||
}); | ||
}); |