Skip to content

Commit a657064

Browse files
Merge pull request #28 from Jozty/dev-ss
add function - andThen
2 parents 5f648af + e8b0607 commit a657064

15 files changed

+396
-5
lines changed

andThen.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import curryN from "./utils/curry_n.ts"
2+
import { Func, Curry2 } from "./utils/types.ts"
3+
import { assertPromise } from "./utils/assert.ts"
4+
5+
function _andThen(f: Func, p: any) {
6+
assertPromise('andThen', p)
7+
return p.then(f)
8+
}
9+
10+
/**
11+
* Returns the result of applying the onSuccess function to the value inside
12+
* a successfully resolved promise. This is useful for working with promises
13+
* inside function compositions.
14+
*/
15+
export const andThen: Curry2<Func, any, Promise<any>> = curryN(2, _andThen)

groupWith.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Predicate2, Curry2 } from "./utils/types.ts"
2+
import curryN from "./utils/curry_n.ts"
3+
import { slice } from "./slice.ts"
4+
5+
function _groupWith<T>(predicate: Predicate2<T | string>, functor: T[] | string) {
6+
const result: T[][] | string[] = []
7+
const len = functor.length
8+
let i = 0
9+
while(i < len) {
10+
let j = i + 1
11+
while(j < len && predicate(functor[j - 1], functor[j])) j++
12+
result.push(slice(i, j, functor) as (T[] & string))
13+
i = j
14+
}
15+
return result
16+
}
17+
18+
export const groupWith: Curry2<Predicate2, any[] | string, any[][] | string[]> = curryN(2, _groupWith)

head.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { nth } from './nth.ts'
2+
3+
/**
4+
* Returns the first element of the given list or string. In some libraries
5+
* this function is named `first`.
6+
*
7+
* Fae.head(['fi', 'fo', 'fum']); //=> 'fi'
8+
* Fae.head([]); //=> undefined
9+
*
10+
* Fae.head('abc'); //=> 'a'
11+
* Fae.head(''); //=> ''
12+
*/
13+
export const head = nth(0)

indexOf.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { isNumber } from './utils/is.ts'
2+
import curryN from "./utils/curry_n.ts"
3+
import { Curry2 } from "./utils/types.ts"
4+
import { equals } from './equals.ts'
5+
6+
function _indexOf<T>(value: T, list: T[]) {
7+
switch(typeof value) {
8+
case 'number': {
9+
if(value === 0) {
10+
// handles +0 and -0
11+
const inf = 1 / value
12+
for (let i = 0; i < list.length; i++) {
13+
const x: any = list[i]
14+
if(x === 0 && 1 / x === inf) return i
15+
}
16+
return -1
17+
}
18+
else if(value !== value) {
19+
// handles NaN
20+
for (let i = 0; i < list.length; i++) {
21+
const x: any = list[i]
22+
if(isNaN(x)) return i
23+
}
24+
return -1
25+
}
26+
}
27+
case 'string':
28+
case 'boolean':
29+
case 'function':
30+
case 'undefined':
31+
return list.indexOf(value)
32+
33+
case 'object':
34+
if (value === null) {
35+
return list.indexOf(value)
36+
}
37+
}
38+
39+
40+
let idx = -1
41+
list.forEach((a, i) => idx = equals(value, a) ? i : idx)
42+
return idx
43+
}
44+
45+
/**
46+
* Returns the position of the first occurrence of `value` in `list`, or -1
47+
* if the item is not included in the array. [`Fae.equals`](#equals) is used to
48+
* determine equality.
49+
*
50+
* Fae.indexOf(3, [1,2,3,4]); //=> 2
51+
* Fae.indexOf(10, [1,2,3,4]); //=> -1
52+
* Fae.indexOf(0, [1, 2, 3, 0, -0, NaN]); //=> 3
53+
* Fae.indexOf(-0, [1, 2, 3, 0, -0, NaN]); //=> 4
54+
* Fae.indexOf(NaN, [1, 2, 3, 0, -0, NaN]); //=> 5
55+
*/
56+
export const indexOf: Curry2<any, any[], number> = curryN(2, _indexOf)

mod.ts

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export { all } from './all.ts'
2121
export { allPass } from './allPass.ts'
2222
export { always } from './always.ts'
2323
export { and } from './and.ts'
24+
export { andThen } from './andThen.ts'
2425
export { any } from './any.ts'
2526
export { anyPass } from './anyPass.ts'
2627
export { ap } from './ap.ts'
@@ -61,8 +62,11 @@ export { findLast } from './findLast.ts'
6162
export { findLastIndex } from './findLastIndex.ts'
6263
export { flip } from './flip.ts'
6364
export { Pair, fromPairs } from './fromPairs.ts'
65+
export { groupWith } from './groupWith.ts'
66+
export { head } from './head.ts'
6467
export { identity } from './identity.ts'
6568
export { inc } from './inc.ts'
69+
export { indexOf } from './indexOf.ts'
6670
export { insert } from './insert.ts'
6771
export { isEmpty } from './isEmpty.ts'
6872
export { join } from './join.ts'

nth.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@ function _nth<T>(index: number, functor: FunctorWithArLk<T> | string) {
1313
else throwFunctorError()
1414

1515
index = index < 0 ? index + f.length : index
16-
return f[index]
16+
return(
17+
f[index]
18+
? f[index]
19+
: isString(functor)
20+
? ''
21+
: f[index]
22+
)
1723
}
1824

1925
/**

specs/_async.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
export function it(fun: Function) {
3+
return async function() {
4+
let done: Function = () => void 0
5+
const p = new Promise(resolve => {
6+
let d = () => resolve()
7+
done = d
8+
})
9+
await fun(done)
10+
await p
11+
}
12+
}
13+
14+
export type Tests = {
15+
[desc: string]: () => Promise<void>
16+
}

specs/_run.ts

+6
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ async function run() {
7171
}
7272
}
7373
showResults(start, Date.now())
74+
75+
const p = Deno.run({
76+
cmd: ["bash", "./specs/deno_test.sh"],
77+
})
78+
79+
await p.status()
7480
}
7581

7682
await run()

specs/andThen.spec.ts

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { it, Tests } from './_async.ts'
2+
import { describe, it as itS} from "./_describe.ts"
3+
import {
4+
andThen,
5+
compose,
6+
pipe,
7+
inc,
8+
} from '../mod.ts'
9+
import { eq, thr } from "./utils/utils.ts"
10+
11+
const tests: Tests = {
12+
"should invoke then on the promise with the function passed to it": it(
13+
async (done: Function) => {
14+
const p = new Promise(resolve => {
15+
setTimeout(() => {
16+
resolve(1)
17+
}, 100)
18+
})
19+
andThen(
20+
function (n) {
21+
eq(n, 1)
22+
done()
23+
},
24+
p
25+
)
26+
}
27+
),
28+
29+
"should flatten promise returning functions": it(
30+
async (done: Function) => {
31+
const incAndWrap = compose(Promise.resolve.bind(Promise), inc)
32+
const asyncAddThree = pipe(incAndWrap, andThen(incAndWrap), andThen(incAndWrap))
33+
34+
andThen((result) => {
35+
eq(result, 4)
36+
done()
37+
})(asyncAddThree(1))
38+
}
39+
),
40+
41+
"should not dependent on a particular promise implementation": it(
42+
async (done: Function) => {
43+
const thennable = {
44+
then: function(f: Function) {
45+
return f(42)
46+
}
47+
}
48+
49+
const f = function(n: number) {
50+
eq(n, 42)
51+
done()
52+
}
53+
54+
andThen(f, thennable)
55+
}
56+
),
57+
}
58+
59+
describe('andThen', () => {
60+
itS('throws a typeError if the then method does not exist', () => {
61+
thr(() => andThen(inc, 1), '`andThen` expected a Promise, received 1')
62+
63+
})
64+
})
65+
66+
for(let desc in tests) {
67+
Deno.test(desc, tests[desc])
68+
}

specs/deno_test.sh

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
deno test specs/andThen.spec.ts

specs/groupWith.spec.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { describe, it } from "./_describe.ts"
2+
import { groupWith, equals } from '../mod.ts'
3+
import { eq } from "./utils/utils.ts"
4+
5+
describe('groupWith', () => {
6+
7+
it('should split the list into groups according to the grouping function', () => {
8+
eq(groupWith(equals, [1, 2, 2, 3]), [[1], [2, 2], [3]])
9+
eq(groupWith(equals, [1, 1, 1, 1]), [[1, 1, 1, 1]])
10+
eq(groupWith(equals, [1, 2, 3, 4]), [[1], [2], [3], [4]])
11+
})
12+
13+
it('should split the list into "streaks" testing adjacent elements', () => {
14+
// @ts-ignore
15+
const isConsecutive = function(a, b) { return a + 1 === b; }
16+
eq(groupWith(isConsecutive, []), [])
17+
eq(groupWith(isConsecutive, [4, 3, 2, 1]), [[4], [3], [2], [1]])
18+
eq(groupWith(isConsecutive, [1, 2, 3, 4]), [[1, 2, 3, 4]])
19+
eq(groupWith(isConsecutive, [1, 2, 2, 3]), [[1, 2], [2, 3]])
20+
eq(groupWith(isConsecutive, [1, 2, 9, 3, 4]), [[1, 2], [9], [3, 4]])
21+
})
22+
23+
it('should return an empty array if given an empty array', () => {
24+
eq(groupWith(equals, []), [])
25+
})
26+
27+
// TODO:
28+
// it('can be turned into the original list through concatenation', () => {
29+
// var list = [1, 1, 2, 3, 4, 4, 5, 5]
30+
// eq(R.unnest(groupWith(equals, list)), list)
31+
// eq(R.unnest(groupWith(R.complement(equals), list)), list)
32+
// eq(R.unnest(groupWith(R.T, list)), list)
33+
// eq(R.unnest(groupWith(R.F, list)), list)
34+
// })
35+
36+
it('should also work on strings', () => {
37+
eq(groupWith(equals)('Mississippi'), ['M','i','ss','i','ss','i','pp','i'])
38+
})
39+
40+
})

specs/head.spec.ts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { describe, it } from "./_describe.ts"
2+
import { head } from '../mod.ts'
3+
import { eq, thr } from "./utils/utils.ts"
4+
5+
describe('head', () => {
6+
7+
it('should return the first element of an ordered collection', () => {
8+
eq(head([1, 2, 3]), 1)
9+
eq(head([2, 3]), 2)
10+
eq(head([3]), 3)
11+
eq(head([]), undefined)
12+
13+
eq(head('abc'), 'a')
14+
eq(head('bc'), 'b')
15+
eq(head('c'), 'c')
16+
eq(head(''), '')
17+
})
18+
19+
it('should throw if applied to null or undefined', () => {
20+
// @ts-ignore
21+
thr(() => head(null), 'The functor should be an array like or iterable/iterator')
22+
// @ts-ignore
23+
thr(() => head(undefined), 'The functor should be an array like or iterable/iterator')
24+
})
25+
26+
})

0 commit comments

Comments
 (0)