diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b37beeac..1fca893c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,17 @@
+11.1.0
+
+- Add `R.filterMap` - similar to Ruby `filter_map`
+
+- Add `R.mapChain` - when in `R.pipe` there are several `R.map` one after the other, then `R.mapChain` can be used instead.
+
+- Add `R.middle` - equal to `R.init` + `R.tail`
+
+- Add `R.random`, `R.shuffle`, `R.switcher`, `R.sum`, `R.delay` - imported from `Rambda`
+
+- Add index to `R.filter`/`R.reject` predicate signiture
+
+- Improve typing of `R.init`, `R.tail`
+
11.0.1
- Add missing JS change for `R.includes` and `R.excludes` methods in `11.0.0` release.
diff --git a/README.md b/README.md
index efed3f30d..d2af17fd0 100644
--- a/README.md
+++ b/README.md
@@ -215,7 +215,7 @@ export function addProp(key, value) {
Tests
```javascript
-import { addProp } from "./addProp.js"
+import { addProp } from './addProp.js'
test('happy', () => {
const result = addProp('a', 1)({ b: 2 })
@@ -325,8 +325,8 @@ export function addPropToObjects (
Tests
```javascript
-import { pipe } from "./pipe.js"
-import { addPropToObjects } from "./addPropToObjects.js"
+import { pipe } from './pipe.js'
+import { addPropToObjects } from './addPropToObjects.js'
test('R.addPropToObjects', () => {
let result = pipe(
@@ -1941,6 +1941,45 @@ describe('R.defaultTo', () => {
[](#defaultTo)
+### delay
+
+```typescript
+
+delay(ms: number): Promise<'RAMBDA_DELAY'>
+```
+
+`setTimeout` as a promise that resolves to `RAMBDA_DELAY` string after `ms` milliseconds.
+
+
+
+All TypeScript definitions
+
+```typescript
+delay(ms: number): Promise<'RAMBDA_DELAY'>;
+```
+
+
+
+
+
+R.delay source
+
+```javascript
+export const RAMBDA_DELAY = 'RAMBDA_DELAY'
+
+export function delay(ms) {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve(RAMBDA_DELAY)
+ }, ms)
+ })
+}
+```
+
+
+
+[](#delay)
+
### descend
```typescript
@@ -2120,7 +2159,7 @@ drop(howMany: number): (list: T[]) => T[];
R.drop source
```javascript
-export function drop(howManyToDrop, ) {
+export function drop(howManyToDrop) {
return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
```
@@ -3529,7 +3568,7 @@ filter(
predicate: BooleanConstructor,
): (list: T[]) => ExcludeFalsy[];
filter(
- predicate: (value: T) => boolean,
+ predicate: (value: T, index: number) => boolean,
): (list: T[]) => T[];
...
...
@@ -3597,14 +3636,26 @@ const list = [1, 2, 3]
describe('R.filter with array', () => {
it('within pipe', () => {
- const _result = pipe(
+ const result = pipe(
list,
filter(x => {
x // $ExpectType number
return x > 1
}),
)
- _result // $ExpectType number[]
+ result // $ExpectType number[]
+ })
+
+ it('with index', () => {
+ const result = pipe(
+ list,
+ filter((x: number, i: number) => {
+ x // $ExpectType number
+ i // $ExpectType number
+ return x > 1
+ }),
+ )
+ result // $ExpectType number[]
})
it('complex example', () => {
@@ -3643,8 +3694,8 @@ describe('R.filter with array', () => {
const filterBar = (x: T): x is Bar => {
return typeof (x as Bar).b === 'string'
}
- const _result = pipe(testList, filter(filterBar))
- _result // $ExpectType Bar[]
+ const result = pipe(testList, filter(filterBar))
+ result // $ExpectType Bar[]
})
it('narrowing type - readonly', () => {
@@ -3659,14 +3710,14 @@ describe('R.filter with array', () => {
const filterBar = (x: T): x is Bar => {
return typeof (x as Bar).b === 'string'
}
- const _result = pipe(testList, filter(filterBar))
- _result // $ExpectType Bar[]
+ const result = pipe(testList, filter(filterBar))
+ result // $ExpectType Bar[]
})
it('filtering NonNullable - list of objects', () => {
const testList = [{ a: 1 }, { a: 2 }, false, { a: 3 }]
- const _result = pipe(testList, filter(Boolean))
- _result // $ExpectType { a: number; }[]
+ const result = pipe(testList, filter(Boolean))
+ result // $ExpectType { a: number; }[]
})
it('filtering NonNullable - readonly', () => {
@@ -3778,6 +3829,104 @@ describe('R.filter with array', () => {
[](#filterAsync)
+### filterMap
+
+```typescript
+
+filterMap(
+ fn: (value: T[number], index: number) => U,
+): (data: T) => Mapped>
+```
+
+Same as `R.map` but it filters out `null/undefined` if returned from functor functions.
+
+> :boom: This function doesn't work with objects (use R.mapObject instead)
+
+```javascript
+const result = R.pipe(
+ [1, 2, 3],
+ R.filterMap(x => x > 1 ? x : null)
+)
+// => [2, 3]
+```
+
+Try this R.filterMap example in Rambda REPL
+
+
+
+All TypeScript definitions
+
+```typescript
+filterMap(
+ fn: (value: T[number], index: number) => U,
+): (data: T) => Mapped>;
+filterMap(
+ fn: (value: T[number]) => U,
+): (data: T) => Mapped>;
+```
+
+
+
+
+
+R.filterMap source
+
+```javascript
+import {mapFn} from './map.js'
+
+export function filterMap(fn) {
+ return list => mapFn(fn, list).filter(Boolean)
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { filterMap } from './filterMap.js'
+
+const double = x => x > 1 ? x * 2 : null
+
+it('happy', () => {
+ expect(filterMap(double)([1, 2, 3])).toEqual([4, 6])
+})
+```
+
+
+
+
+
+TypeScript test
+
+```typescript
+import { filterMap, pipe } from 'rambda'
+
+const list = [1, 2, 3]
+
+it('R.filterMap - within pipe', () => {
+ const result = pipe(
+ list,
+ x => x,
+ filterMap(x => {
+ x // $ExpectType number
+ return Math.random() > 0.5 ? String(x) : null
+ }),
+ filterMap(x => {
+ x // $ExpectType string
+ return Math.random() > 0.5 ? Number(x) : ''
+ }),
+ )
+ result // $ExpectType number[]
+})
+```
+
+
+
+[](#filterMap)
+
### filterObject
```typescript
@@ -5151,9 +5300,6 @@ indexBy(
indexBy(
property: K
): (list: T[]) => Record;
-
-// API_MARKER_END
-// ============================================
```
@@ -5327,7 +5473,9 @@ describe('R.indexOf', () => {
```typescript
-init(input: T): T extends readonly [...infer U, any] ? U : [...T]
+init(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [...infer U, any] ? U : T : T extends string ? string : never
```
It returns all but the last element of list or string `input`.
@@ -5347,8 +5495,9 @@ const result = [
All TypeScript definitions
```typescript
-init(input: T): T extends readonly [...infer U, any] ? U : [...T];
-init(input: string): string;
+init(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [...infer U, any] ? U : T : T extends string ? string : never;
```
@@ -5401,7 +5550,7 @@ test('with string', () => {
TypeScript test
```typescript
-import { init } from 'rambda'
+import { map, pipe, init } from 'rambda'
describe('R.init', () => {
it('with string', () => {
@@ -5409,13 +5558,32 @@ describe('R.init', () => {
result // $ExpectType string
})
- it('with list - one type', () => {
- const result = init([1, 2, 3])
-
- result // $ExpectType number[]
+ it('with list - using const on short array', () => {
+ const result = pipe(
+ [1] as const,
+ map(x => x * 2),
+ init,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const on empty array', () => {
+ const result = pipe(
+ [] as const,
+ map(x => x * 2),
+ init,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const', () => {
+ const result = pipe(
+ [1, 2, 3] as const,
+ map(x => x * 2),
+ init,
+ )
+ result // $ExpectType [number, number]
})
it('with list - mixed types', () => {
- const result = init([1, 2, 3, 'foo', 'bar'])
+ const result = init(['foo', 'bar', 1, 2, 3])
result // $ExpectType (string | number)[]
})
@@ -6126,16 +6294,14 @@ It returns the result of looping through `iterable` with `fn`.
> :boom: This function doesn't work with objects (use R.mapObject instead)
```javascript
-const fn = x => x * 2
-
-const iterable = [1, 2]
-const obj = {a: 1, b: 2}
-
-const result = R.map(fn)(iterable),
+const result = R.pipe(
+ [1, 2],
+ R.map(x => x * 2)
+)
// => [2, 4]
```
-Try this R.map example in Rambda REPL
+Try this R.map example in Rambda REPL
@@ -6201,7 +6367,7 @@ import { map, pipe } from 'rambda'
const list = [1, 2, 3]
-it('R.map - within pipe', () => {
+it('R.map', () => {
const result = pipe(
list,
x => x,
@@ -6213,6 +6379,19 @@ it('R.map - within pipe', () => {
result // $ExpectType string[]
})
+it('R.map - index in functor', () => {
+ const result = pipe(
+ list,
+ x => x,
+ map((x, i) => {
+ x // $ExpectType number
+ i // $ExpectType number
+ return String(x)
+ }),
+ )
+ result // $ExpectType string[]
+})
+
it('R.map - without pipe', () => {
map(x => {
x // $ExpectType unknown
@@ -6380,6 +6559,165 @@ it('R.mapAsync', async () => {
[](#mapAsync)
+### mapChain
+
+```typescript
+
+mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped
+```
+
+Chained 2 or 3 `R.map` transformations as one.
+
+```javascript
+const result = R.pipe(
+ [1, 2],
+ R.mapChain(
+ x => x * 2,
+ x => [x, x > 3],
+ )
+)
+// => [[2, false], [4, true]]
+```
+
+Try this R.mapChain example in Rambda REPL
+
+
+
+All TypeScript definitions
+
+```typescript
+mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped;
+mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U) => V,
+): (data: T) => Mapped;
+mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped;
+mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U) => V,
+): (data: T) => Mapped;
+...
+...
+```
+
+
+
+
+
+R.mapChain source
+
+```javascript
+import { mapFn } from './map.js';
+
+export function mapChain(...fns) {
+ return list => {
+ let result = list.slice()
+ fns.forEach((fn) => {
+ result = mapFn(fn, result)
+ })
+ return result
+ }
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { mapChain } from './mapChain.js'
+
+const double = x => x * 2
+
+it('happy', () => {
+ expect(mapChain(double, double, double)([1, 2, 3])).toEqual([8, 16, 24])
+})
+```
+
+
+
+
+
+TypeScript test
+
+```typescript
+import { mapChain, pipe } from 'rambda'
+
+const list = [1, 2, 3]
+
+it('R.mapChain', () => {
+ const result = pipe(
+ list,
+ mapChain(
+ x => {
+ x // $ExpectType number
+ return String(x)
+ },
+ x => {
+ x // $ExpectType string
+ return x !== 'foo'
+ },
+ ),
+ )
+ result // $ExpectType boolean[]
+})
+
+it('R.mapChain - with index', () => {
+ const result = pipe(
+ list,
+ mapChain(
+ x => {
+ x // $ExpectType number
+ return String(x)
+ },
+ (x, i) => {
+ i // $ExpectType number
+ x // $ExpectType string
+ return x !== 'foo'
+ },
+ ),
+ )
+ result // $ExpectType boolean[]
+})
+
+it('R.mapChain - 3 functions', () => {
+ const result = pipe(
+ list,
+ x => x,
+ mapChain(
+ x => {
+ x // $ExpectType number
+ return String(x)
+ },
+ x => {
+ x // $ExpectType string
+ return x !== 'foo'
+ },
+ x => {
+ x // $ExpectType boolean
+ return x ? 'foo' : 'bar'
+ },
+ ),
+ )
+ result // $ExpectType ("foo" | "bar")[]
+})
+```
+
+
+
+[](#mapChain)
+
### mapKeys
```typescript
@@ -6435,7 +6773,7 @@ export function mapKeys(fn) {
Tests
```javascript
-import { mapKeys } from "./mapKeys.js"
+import { mapKeys } from './mapKeys.js'
test('happy', () => {
const result = mapKeys((prop, x) => `${ prop }-${x}`)({a:1, b: 2 })
@@ -7248,11 +7586,130 @@ export function mergeTypes(x) {
[](#mergeTypes)
-### minBy
+### middle
```typescript
-minBy(compareFn: (input: T) => Ord, x: T): (y: T) => T
+middle(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] : T['length'] extends 2 ? [] :
+ T extends [any, ...infer U, any] ? U : T : T extends string ? string : never
+```
+
+It returns all but the first and last element of `input`.
+
+```javascript
+const result = [
+ R.middle([1, 2, 3, 4]),
+ R.middle('bar')
+]
+// => [[2, 3], 'a']
+```
+
+Try this R.middle example in Rambda REPL
+
+
+
+All TypeScript definitions
+
+```typescript
+middle(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] : T['length'] extends 2 ? [] :
+ T extends [any, ...infer U, any] ? U : T : T extends string ? string : never;
+```
+
+
+
+
+
+R.middle source
+
+```javascript
+import { init } from './init.js'
+import { tail } from './tail.js'
+
+export function middle(listOrString) {
+ return tail(init(listOrString))
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { middle } from './middle'
+
+test('middle', () => {
+ expect(middle([1, 2, 3])).toEqual([2])
+ expect(middle([1, 2])).toEqual([])
+ expect(middle([1])).toEqual([])
+ expect(middle([])).toEqual([])
+
+ expect(middle('abc')).toBe('b')
+ expect(middle('ab')).toBe('')
+ expect(middle('a')).toBe('')
+ expect(middle('')).toBe('')
+})
+```
+
+
+
+
+
+TypeScript test
+
+```typescript
+import { map, middle, pipe } from 'rambda'
+
+describe('R.middle', () => {
+ it('with string', () => {
+ const result = middle('foo')
+
+ result // $ExpectType string
+ })
+ it('with list - using const on short array', () => {
+ const result = pipe(
+ [1, 2] as const,
+ map(x => x * 2),
+ middle,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const on empty array', () => {
+ const result = pipe(
+ [] as const,
+ map(x => x * 2),
+ middle,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const', () => {
+ const result = pipe(
+ [1, 2, 3, 4] as const,
+ map(x => x * 2),
+ middle,
+ )
+ result // $ExpectType [number, number]
+ })
+ it('with list - mixed types', () => {
+ const result = middle(['foo', 'bar', 1, 2, 3])
+
+ result // $ExpectType (string | number)[]
+ })
+})
+```
+
+
+
+[](#middle)
+
+### minBy
+
+```typescript
+
+minBy(compareFn: (input: T) => Ord, x: T): (y: T) => T
```
It returns the lesser value between `x` and `y` according to `compareFn` function.
@@ -9434,7 +9891,7 @@ test('with undefined', () => {
TypeScript test
```typescript
-import { pipe, pluck } from "rambda";
+import { pipe, pluck } from 'rambda';
it("R.pluck", () => {
const input = [
@@ -9861,6 +10318,56 @@ describe('R.propSatisfies', () => {
[](#propSatisfies)
+### random
+
+```typescript
+
+random(minInclusive: number, maxInclusive: number): number
+```
+
+It returns a random number between `min` inclusive and `max` inclusive.
+
+
+
+All TypeScript definitions
+
+```typescript
+random(minInclusive: number, maxInclusive: number): number;
+```
+
+
+
+
+
+R.random source
+
+```javascript
+export function random(min, max){
+ return Math.floor(Math.random() * (max - min + 1)) + min
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { random } from './random.js'
+import { range } from './range.js'
+import { uniq } from './uniq.js'
+
+test('happy', () => {
+ const result = uniq(range(100).map(() => random(0, 3))).sort()
+ expect(result).toEqual([0,1,2,3])
+})
+```
+
+
+
+[](#random)
+
### range
```typescript
@@ -10207,6 +10714,17 @@ describe('R.reject with array', () => {
}),
)
result // $ExpectType number[]
+ })
+ it('with index', () => {
+ const result = pipe(
+ list,
+ reject((x: number, i: number) => {
+ x // $ExpectType number
+ i // $ExpectType number
+ return x > 1
+ }),
+ )
+ result // $ExpectType number[]
})
it('narrowing type', () => {
interface Foo {
@@ -10565,15 +11083,6 @@ shuffle(list: T[]): T[]
It returns a randomized copy of array.
-```javascript
-const result = R.shuffle(
- [1, 2, 3]
-)
-// => [3, 1, 2] or [2, 3, 1] or ...
-```
-
-Try this R.shuffle example in Rambda REPL
-
All TypeScript definitions
@@ -10901,7 +11410,7 @@ sortByDescending(sortFn: (x: T) => Ord): (list: T[]) => T[];
R.sortByDescending source
```javascript
-import { sortByFn } from "./sortBy.js";
+import { sortByFn } from './sortBy.js';
export function sortByDescending(sortFn) {
return list => sortByFn(sortFn, list, true)
@@ -11600,6 +12109,300 @@ describe('R.splitEvery', () => {
[](#splitEvery)
+### sum
+
+```typescript
+
+sum(list: number[]): number
+```
+
+```javascript
+const result = R.sum(
+ [1,2,3]
+)
+// => 6
+```
+
+Try this R.sum example in Rambda REPL
+
+
+
+All TypeScript definitions
+
+```typescript
+sum(list: number[]): number;
+```
+
+
+
+
+
+R.sum source
+
+```javascript
+export function sum(list){
+ return list.reduce((acc, cur) => acc + cur, 0)
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { sum } from './sum.js'
+
+test('happy', () => {
+ expect(sum([1,2,3])).toEqual(6)
+})
+```
+
+
+
+[](#sum)
+
+### switcher
+
+```typescript
+
+switcher(valueToMatch: T): Switchem
+```
+
+```javascript
+const list = [1, 2, 3]
+
+const result = switcher(list.length)
+ .is(x => x < 2, 4)
+ .is(x => x < 4, 6)
+ .default(7)
+// => 6
+```
+
+Try this R.switcher example in Rambda REPL
+
+
+
+All TypeScript definitions
+
+```typescript
+switcher(valueToMatch: T): Switchem;
+switcher(valueToMatch: T): Switchem2;
+
+// API_MARKER_END
+// ============================================
+```
+
+
+
+
+
+R.switcher source
+
+```javascript
+import { equals } from './equals.js'
+
+const NO_MATCH_FOUND = Symbol ? Symbol('NO_MATCH_FOUND') : undefined
+
+const getMatchingKeyValuePair = (
+ cases, testValue, defaultValue
+) => {
+ let iterationValue
+
+ for (let index = 0; index < cases.length; index++){
+ iterationValue = cases[ index ].test(testValue)
+
+ if (iterationValue !== NO_MATCH_FOUND){
+ return iterationValue
+ }
+ }
+
+ return defaultValue
+}
+
+const isEqual = (testValue, matchValue) => {
+ const willReturn =
+ typeof testValue === 'function' ?
+ testValue(matchValue) :
+ equals(testValue)(matchValue)
+
+ return willReturn
+}
+
+const is = (testValue, matchResult = true) => ({
+ key : testValue,
+ test : matchValue =>
+ isEqual(testValue, matchValue) ? matchResult : NO_MATCH_FOUND,
+})
+
+class Switchem{
+ constructor(
+ defaultValue, cases, willMatch
+ ){
+ if (cases === undefined && willMatch === undefined){
+ this.cases = []
+ this.defaultValue = undefined
+ this.willMatch = defaultValue
+ } else {
+ this.cases = cases
+ this.defaultValue = defaultValue
+ this.willMatch = willMatch
+ }
+
+ return this
+ }
+
+ default(defaultValue){
+ const holder = new Switchem(
+ defaultValue, this.cases, this.willMatch
+ )
+
+ return holder.match(this.willMatch)
+ }
+
+ is(testValue, matchResult){
+ return new Switchem(
+ this.defaultValue,
+ [ ...this.cases, is(testValue, matchResult) ],
+ this.willMatch
+ )
+ }
+
+ match(matchValue){
+ return getMatchingKeyValuePair(
+ this.cases, matchValue, this.defaultValue
+ )
+ }
+}
+
+export function switcher(input){
+ return new Switchem(input)
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { switcher } from './switcher.js'
+import { tap } from './tap.js'
+
+test('with undefined', () => {
+ const result = switcher(undefined)
+ .is(x => x === 0, '0')
+ .is(x => x === undefined, 'UNDEFINED')
+ .default('3')
+
+ expect(result).toBe('UNDEFINED')
+})
+
+test('happy', () => {
+ const a = true
+ const b = false
+ const result = switcher([ a, b ])
+ .is([ false, false ], '0')
+ .is([ false, true ], '1')
+ .is([ true, true ], '2')
+ .default('3')
+
+ expect(result).toBe('3')
+})
+
+test('can compare objects', () => {
+ const result = switcher({ a : 1 })
+ .is({ a : 1 }, 'it is object')
+ .is('baz', 'it is baz')
+ .default('it is default')
+
+ expect(result).toBe('it is object')
+})
+
+test('options are mixture of functions and values - input match function', () => {
+ const fn = switcher('foo').is('bar', 1)
+ .is('foo', x => x + 1)
+ .default(1000)
+
+ expect(fn(2)).toBe(3)
+})
+
+test('options are mixture of functions and values - input match value', () => {
+ const result = switcher('bar').is('bar', 1)
+ .is('foo', x => x + 1)
+ .default(1000)
+
+ expect(result).toBe(1)
+})
+
+test('return function if all options are functions', () => {
+ const fn = switcher('foo')
+ .is('bar', tap)
+ .is('foo', x => x + 1)
+ .default(9)
+
+ expect(fn(2)).toBe(3)
+})
+
+const switchFn = input =>
+ switcher(input)
+ .is(x => x.length && x.length === 7, 'has length of 7')
+ .is('baz', 'it is baz')
+ .default('it is default')
+
+test('works with function as condition', () => {
+ expect(switchFn([ 0, 1, 2, 3, 4, 5, 6 ])).toBe('has length of 7')
+})
+
+test('works with string as condition', () => {
+ expect(switchFn('baz')).toBe('it is baz')
+})
+
+test('fallback to default input when no matches', () => {
+ expect(switchFn(1)).toBe('it is default')
+})
+```
+
+
+
+
+
+TypeScript test
+
+```typescript
+import { switcher } from 'rambda'
+
+describe('R.switcher', () => {
+ it('no transformation', () => {
+ const list = [1, 2, 3]
+
+ const result = switcher(list.length)
+ .is(x => x < 2, 4)
+ .is(x => x < 4, 6)
+ .default(7)
+
+ result // $ExpectType number
+ })
+ it('with transformation', () => {
+ const list = [1, 2, 3]
+ type Stage = 'firstStage' | 'secondStage' | 'thirdStage'
+
+ const result = switcher(list.length)
+ .is(x => x < 2, 'firstStage')
+ .is(x => x < 4, 'secondStage')
+ .default('thirdStage')
+
+ result // $ExpectType Stage
+ })
+})
+```
+
+
+
+[](#switcher)
+
### symmetricDifference
```typescript
@@ -11703,7 +12506,9 @@ describe('R.symmetricDifference', () => {
```typescript
-tail(input: T): T extends [any, ...infer U] ? U : [...T]
+tail(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [any, ...infer U] ? U : T : T extends string ? string : never
```
It returns all but the first element of `input`.
@@ -11723,8 +12528,9 @@ const result = [
All TypeScript definitions
```typescript
-tail(input: T): T extends [any, ...infer U] ? U : [...T];
-tail(input: string): string;
+tail(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [any, ...infer U] ? U : T : T extends string ? string : never;
```
@@ -11770,7 +12576,7 @@ test('tail', () => {
TypeScript test
```typescript
-import { tail } from 'rambda'
+import { map, pipe, tail } from 'rambda'
describe('R.tail', () => {
it('with string', () => {
@@ -11778,10 +12584,29 @@ describe('R.tail', () => {
result // $ExpectType string
})
- it('with list - one type', () => {
- const result = tail([1, 2, 3])
-
- result // $ExpectType number[]
+ it('with list - using const on short array', () => {
+ const result = pipe(
+ [1] as const,
+ map(x => x * 2),
+ tail,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const on empty array', () => {
+ const result = pipe(
+ [] as const,
+ map(x => x * 2),
+ tail,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const', () => {
+ const result = pipe(
+ [1, 2, 3] as const,
+ map(x => x * 2),
+ tail,
+ )
+ result // $ExpectType [number, number]
})
it('with list - mixed types', () => {
const result = tail(['foo', 'bar', 1, 2, 3])
@@ -13973,6 +14798,20 @@ describe('R.zipWith', () => {
## ❯ CHANGELOG
+11.1.0
+
+- Add `R.filterMap` - similar to Ruby `filter_map`
+
+- Add `R.mapChain` - when in `R.pipe` there are several `R.map` one after the other, then `R.mapChain` can be used instead.
+
+- Add `R.middle` - equal to `R.init` + `R.tail`
+
+- Add `R.random`, `R.shuffle`, `R.switcher`, `R.sum`, `R.delay` - imported from `Rambda`
+
+- Add index to `R.filter`/`R.reject` predicate signiture
+
+- Improve typing of `R.init`, `R.tail`
+
11.0.1
- Add missing JS change for `R.includes` and `R.excludes` methods in `11.0.0` release.
diff --git a/dist/rambda.cjs b/dist/rambda.cjs
index 9e439af6a..23ee0a762 100644
--- a/dist/rambda.cjs
+++ b/dist/rambda.cjs
@@ -249,6 +249,16 @@ function defaultTo(defaultArgument) {
return input => isFalsy(input) ? defaultArgument : input
}
+const RAMBDA_DELAY = 'RAMBDA_DELAY';
+
+function delay(ms) {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve(RAMBDA_DELAY);
+ }, ms);
+ })
+}
+
function descend(getFunction) {
return (a, b) => {
const aValue = getFunction(a);
@@ -508,7 +518,7 @@ function difference(listA) {
])
}
-function drop(howManyToDrop, ) {
+function drop(howManyToDrop) {
return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
@@ -677,6 +687,10 @@ function filterAsync(predicate) {
}
}
+function filterMap(fn) {
+ return list => mapFn(fn, list).filter(Boolean)
+}
+
function filterObject(predicate) {
return obj => {
const willReturn = {};
@@ -1034,6 +1048,16 @@ function mapAsync(fn) {
}
}
+function mapChain(...fns) {
+ return list => {
+ let result = list.slice();
+ fns.forEach((fn) => {
+ result = mapFn(fn, result);
+ });
+ return result
+ }
+}
+
function mapKeys(fn) {
return obj => {
const willReturn = {};
@@ -1103,6 +1127,14 @@ function mergeTypes(x) {
return x
}
+function tail(listOrString) {
+ return drop(1)(listOrString)
+}
+
+function middle(listOrString) {
+ return tail(init(listOrString))
+}
+
function minBy(compareFn, x) {
return y => (compareFn(y) < compareFn(x) ? y : x)
}
@@ -1520,6 +1552,10 @@ function propSatisfies(predicate, property) {
return obj => predicate(obj[property])
}
+function random(min, max){
+ return Math.floor(Math.random() * (max - min + 1)) + min
+}
+
function range(a, b) {
const start = b === undefined ? 0 : a;
const end = b === undefined ? a : b;
@@ -1669,6 +1705,87 @@ function splitEvery(sliceLength) {
}
}
+function sum(list){
+ return list.reduce((acc, cur) => acc + cur, 0)
+}
+
+const NO_MATCH_FOUND = Symbol ? Symbol('NO_MATCH_FOUND') : undefined;
+
+const getMatchingKeyValuePair = (
+ cases, testValue, defaultValue
+) => {
+ let iterationValue;
+
+ for (let index = 0; index < cases.length; index++){
+ iterationValue = cases[ index ].test(testValue);
+
+ if (iterationValue !== NO_MATCH_FOUND){
+ return iterationValue
+ }
+ }
+
+ return defaultValue
+};
+
+const isEqual = (testValue, matchValue) => {
+ const willReturn =
+ typeof testValue === 'function' ?
+ testValue(matchValue) :
+ equals(testValue)(matchValue);
+
+ return willReturn
+};
+
+const is = (testValue, matchResult = true) => ({
+ key : testValue,
+ test : matchValue =>
+ isEqual(testValue, matchValue) ? matchResult : NO_MATCH_FOUND,
+});
+
+class Switchem{
+ constructor(
+ defaultValue, cases, willMatch
+ ){
+ if (cases === undefined && willMatch === undefined){
+ this.cases = [];
+ this.defaultValue = undefined;
+ this.willMatch = defaultValue;
+ } else {
+ this.cases = cases;
+ this.defaultValue = defaultValue;
+ this.willMatch = willMatch;
+ }
+
+ return this
+ }
+
+ default(defaultValue){
+ const holder = new Switchem(
+ defaultValue, this.cases, this.willMatch
+ );
+
+ return holder.match(this.willMatch)
+ }
+
+ is(testValue, matchResult){
+ return new Switchem(
+ this.defaultValue,
+ [ ...this.cases, is(testValue, matchResult) ],
+ this.willMatch
+ )
+ }
+
+ match(matchValue){
+ return getMatchingKeyValuePair(
+ this.cases, matchValue, this.defaultValue
+ )
+ }
+}
+
+function switcher(input){
+ return new Switchem(input)
+}
+
function symmetricDifference(listA) {
return listB => [
...filter(excludes(listB))(listA),
@@ -1676,10 +1793,6 @@ function symmetricDifference(listA) {
]
}
-function tail(listOrString) {
- return drop(1)(listOrString)
-}
-
function take(numberOfItems) {
return input => {
if (numberOfItems < 0) {
@@ -1890,6 +2003,7 @@ function zipWith(fn, x) {
)
}
+exports.RAMBDA_DELAY = RAMBDA_DELAY;
exports._arity = _arity;
exports._includes = _includes;
exports._indexOf = _indexOf;
@@ -1913,6 +2027,7 @@ exports.countBy = countBy;
exports.createCompareFunction = createCompareFunction;
exports.createObjectFromKeys = createObjectFromKeys;
exports.defaultTo = defaultTo;
+exports.delay = delay;
exports.descend = descend;
exports.difference = difference;
exports.drop = drop;
@@ -1929,6 +2044,7 @@ exports.excludes = excludes;
exports.exists = exists;
exports.filter = filter;
exports.filterAsync = filterAsync;
+exports.filterMap = filterMap;
exports.filterObject = filterObject;
exports.find = find;
exports.findIndex = findIndex;
@@ -1955,6 +2071,7 @@ exports.last = last;
exports.lastIndexOf = lastIndexOf;
exports.map = map;
exports.mapAsync = mapAsync;
+exports.mapChain = mapChain;
exports.mapFn = mapFn;
exports.mapKeys = mapKeys;
exports.mapObject = mapObject;
@@ -1965,6 +2082,7 @@ exports.match = match;
exports.maxBy = maxBy;
exports.merge = merge;
exports.mergeTypes = mergeTypes;
+exports.middle = middle;
exports.minBy = minBy;
exports.modifyItemAtIndex = modifyItemAtIndex;
exports.modifyPath = modifyPath;
@@ -1987,6 +2105,7 @@ exports.prop = prop;
exports.propEq = propEq;
exports.propOr = propOr;
exports.propSatisfies = propSatisfies;
+exports.random = random;
exports.range = range;
exports.rangeDescending = rangeDescending;
exports.reduce = reduce;
@@ -2005,6 +2124,8 @@ exports.sortObject = sortObject;
exports.sortWith = sortWith;
exports.split = split;
exports.splitEvery = splitEvery;
+exports.sum = sum;
+exports.switcher = switcher;
exports.symmetricDifference = symmetricDifference;
exports.tail = tail;
exports.take = take;
diff --git a/dist/rambda.js b/dist/rambda.js
index c87c3ebb9..3019bcfe2 100644
--- a/dist/rambda.js
+++ b/dist/rambda.js
@@ -247,6 +247,16 @@ function defaultTo(defaultArgument) {
return input => isFalsy(input) ? defaultArgument : input
}
+const RAMBDA_DELAY = 'RAMBDA_DELAY';
+
+function delay(ms) {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve(RAMBDA_DELAY);
+ }, ms);
+ })
+}
+
function descend(getFunction) {
return (a, b) => {
const aValue = getFunction(a);
@@ -506,7 +516,7 @@ function difference(listA) {
])
}
-function drop(howManyToDrop, ) {
+function drop(howManyToDrop) {
return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
@@ -675,6 +685,10 @@ function filterAsync(predicate) {
}
}
+function filterMap(fn) {
+ return list => mapFn(fn, list).filter(Boolean)
+}
+
function filterObject(predicate) {
return obj => {
const willReturn = {};
@@ -1032,6 +1046,16 @@ function mapAsync(fn) {
}
}
+function mapChain(...fns) {
+ return list => {
+ let result = list.slice();
+ fns.forEach((fn) => {
+ result = mapFn(fn, result);
+ });
+ return result
+ }
+}
+
function mapKeys(fn) {
return obj => {
const willReturn = {};
@@ -1101,6 +1125,14 @@ function mergeTypes(x) {
return x
}
+function tail(listOrString) {
+ return drop(1)(listOrString)
+}
+
+function middle(listOrString) {
+ return tail(init(listOrString))
+}
+
function minBy(compareFn, x) {
return y => (compareFn(y) < compareFn(x) ? y : x)
}
@@ -1518,6 +1550,10 @@ function propSatisfies(predicate, property) {
return obj => predicate(obj[property])
}
+function random(min, max){
+ return Math.floor(Math.random() * (max - min + 1)) + min
+}
+
function range(a, b) {
const start = b === undefined ? 0 : a;
const end = b === undefined ? a : b;
@@ -1667,6 +1703,87 @@ function splitEvery(sliceLength) {
}
}
+function sum(list){
+ return list.reduce((acc, cur) => acc + cur, 0)
+}
+
+const NO_MATCH_FOUND = Symbol ? Symbol('NO_MATCH_FOUND') : undefined;
+
+const getMatchingKeyValuePair = (
+ cases, testValue, defaultValue
+) => {
+ let iterationValue;
+
+ for (let index = 0; index < cases.length; index++){
+ iterationValue = cases[ index ].test(testValue);
+
+ if (iterationValue !== NO_MATCH_FOUND){
+ return iterationValue
+ }
+ }
+
+ return defaultValue
+};
+
+const isEqual = (testValue, matchValue) => {
+ const willReturn =
+ typeof testValue === 'function' ?
+ testValue(matchValue) :
+ equals(testValue)(matchValue);
+
+ return willReturn
+};
+
+const is = (testValue, matchResult = true) => ({
+ key : testValue,
+ test : matchValue =>
+ isEqual(testValue, matchValue) ? matchResult : NO_MATCH_FOUND,
+});
+
+class Switchem{
+ constructor(
+ defaultValue, cases, willMatch
+ ){
+ if (cases === undefined && willMatch === undefined){
+ this.cases = [];
+ this.defaultValue = undefined;
+ this.willMatch = defaultValue;
+ } else {
+ this.cases = cases;
+ this.defaultValue = defaultValue;
+ this.willMatch = willMatch;
+ }
+
+ return this
+ }
+
+ default(defaultValue){
+ const holder = new Switchem(
+ defaultValue, this.cases, this.willMatch
+ );
+
+ return holder.match(this.willMatch)
+ }
+
+ is(testValue, matchResult){
+ return new Switchem(
+ this.defaultValue,
+ [ ...this.cases, is(testValue, matchResult) ],
+ this.willMatch
+ )
+ }
+
+ match(matchValue){
+ return getMatchingKeyValuePair(
+ this.cases, matchValue, this.defaultValue
+ )
+ }
+}
+
+function switcher(input){
+ return new Switchem(input)
+}
+
function symmetricDifference(listA) {
return listB => [
...filter(excludes(listB))(listA),
@@ -1674,10 +1791,6 @@ function symmetricDifference(listA) {
]
}
-function tail(listOrString) {
- return drop(1)(listOrString)
-}
-
function take(numberOfItems) {
return input => {
if (numberOfItems < 0) {
@@ -1888,4 +2001,4 @@ function zipWith(fn, x) {
)
}
-export { _arity, _includes, _indexOf, _lastIndexOf, addProp, addPropToObjects, all, allPass, any, anyPass, append, ascend, assertType, checkObjectWithSpec, compact, complement, concat, convertToType, count, countBy, createCompareFunction, createObjectFromKeys, defaultTo, descend, difference, drop, dropLast, dropLastWhile, dropWhile, duplicateBy, eqBy, eqProps, equals, equalsFn, evolve, excludes, exists, filter, filterAsync, filterObject, find, findIndex, findLast, findLastIndex, findNth, flatMap, flatten, flattenObject, flattenObjectHelper, groupBy, groupByFallback, head, includes, indexBy, indexOf, init, interpolate, intersection, intersectionWith, intersperse, join, last, lastIndexOf, map, mapAsync, mapFn, mapKeys, mapObject, mapObjectAsync, mapParallelAsync, mapPropObject, match, maxBy, merge, mergeTypes, minBy, modifyItemAtIndex, modifyPath, modifyProp, none, objOf, objectIncludes, omit, partition, partitionObject, path, pathSatisfies, permutations, pick, pipe, pipeAsync, pluck, prepend, prop, propEq, propOr, propSatisfies, range, rangeDescending, reduce, reject, rejectObject, replace, replaceAll, shuffle, sort, sortBy, sortByDescending, sortByFn, sortByPath, sortByPathDescending, sortObject, sortWith, split, splitEvery, symmetricDifference, tail, take, takeLast, takeLastWhile, takeWhile, tap, test, transformFlatObject, tryCatch, type, union, unionWith, uniq, uniqBy, uniqWith, unless, unwind, update, when, zip, zipWith };
+export { RAMBDA_DELAY, _arity, _includes, _indexOf, _lastIndexOf, addProp, addPropToObjects, all, allPass, any, anyPass, append, ascend, assertType, checkObjectWithSpec, compact, complement, concat, convertToType, count, countBy, createCompareFunction, createObjectFromKeys, defaultTo, delay, descend, difference, drop, dropLast, dropLastWhile, dropWhile, duplicateBy, eqBy, eqProps, equals, equalsFn, evolve, excludes, exists, filter, filterAsync, filterMap, filterObject, find, findIndex, findLast, findLastIndex, findNth, flatMap, flatten, flattenObject, flattenObjectHelper, groupBy, groupByFallback, head, includes, indexBy, indexOf, init, interpolate, intersection, intersectionWith, intersperse, join, last, lastIndexOf, map, mapAsync, mapChain, mapFn, mapKeys, mapObject, mapObjectAsync, mapParallelAsync, mapPropObject, match, maxBy, merge, mergeTypes, middle, minBy, modifyItemAtIndex, modifyPath, modifyProp, none, objOf, objectIncludes, omit, partition, partitionObject, path, pathSatisfies, permutations, pick, pipe, pipeAsync, pluck, prepend, prop, propEq, propOr, propSatisfies, random, range, rangeDescending, reduce, reject, rejectObject, replace, replaceAll, shuffle, sort, sortBy, sortByDescending, sortByFn, sortByPath, sortByPathDescending, sortObject, sortWith, split, splitEvery, sum, switcher, symmetricDifference, tail, take, takeLast, takeLastWhile, takeWhile, tap, test, transformFlatObject, tryCatch, type, union, unionWith, uniq, uniqBy, uniqWith, unless, unwind, update, when, zip, zipWith };
diff --git a/dist/rambda.umd.js b/dist/rambda.umd.js
index 8b9326144..23636fe1a 100644
--- a/dist/rambda.umd.js
+++ b/dist/rambda.umd.js
@@ -253,6 +253,16 @@
return input => isFalsy(input) ? defaultArgument : input
}
+ const RAMBDA_DELAY = 'RAMBDA_DELAY';
+
+ function delay(ms) {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve(RAMBDA_DELAY);
+ }, ms);
+ })
+ }
+
function descend(getFunction) {
return (a, b) => {
const aValue = getFunction(a);
@@ -512,7 +522,7 @@
])
}
- function drop(howManyToDrop, ) {
+ function drop(howManyToDrop) {
return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
@@ -681,6 +691,10 @@
}
}
+ function filterMap(fn) {
+ return list => mapFn(fn, list).filter(Boolean)
+ }
+
function filterObject(predicate) {
return obj => {
const willReturn = {};
@@ -1038,6 +1052,16 @@
}
}
+ function mapChain(...fns) {
+ return list => {
+ let result = list.slice();
+ fns.forEach((fn) => {
+ result = mapFn(fn, result);
+ });
+ return result
+ }
+ }
+
function mapKeys(fn) {
return obj => {
const willReturn = {};
@@ -1107,6 +1131,14 @@
return x
}
+ function tail(listOrString) {
+ return drop(1)(listOrString)
+ }
+
+ function middle(listOrString) {
+ return tail(init(listOrString))
+ }
+
function minBy(compareFn, x) {
return y => (compareFn(y) < compareFn(x) ? y : x)
}
@@ -1524,6 +1556,10 @@
return obj => predicate(obj[property])
}
+ function random(min, max){
+ return Math.floor(Math.random() * (max - min + 1)) + min
+ }
+
function range(a, b) {
const start = b === undefined ? 0 : a;
const end = b === undefined ? a : b;
@@ -1673,6 +1709,87 @@
}
}
+ function sum(list){
+ return list.reduce((acc, cur) => acc + cur, 0)
+ }
+
+ const NO_MATCH_FOUND = Symbol ? Symbol('NO_MATCH_FOUND') : undefined;
+
+ const getMatchingKeyValuePair = (
+ cases, testValue, defaultValue
+ ) => {
+ let iterationValue;
+
+ for (let index = 0; index < cases.length; index++){
+ iterationValue = cases[ index ].test(testValue);
+
+ if (iterationValue !== NO_MATCH_FOUND){
+ return iterationValue
+ }
+ }
+
+ return defaultValue
+ };
+
+ const isEqual = (testValue, matchValue) => {
+ const willReturn =
+ typeof testValue === 'function' ?
+ testValue(matchValue) :
+ equals(testValue)(matchValue);
+
+ return willReturn
+ };
+
+ const is = (testValue, matchResult = true) => ({
+ key : testValue,
+ test : matchValue =>
+ isEqual(testValue, matchValue) ? matchResult : NO_MATCH_FOUND,
+ });
+
+ class Switchem{
+ constructor(
+ defaultValue, cases, willMatch
+ ){
+ if (cases === undefined && willMatch === undefined){
+ this.cases = [];
+ this.defaultValue = undefined;
+ this.willMatch = defaultValue;
+ } else {
+ this.cases = cases;
+ this.defaultValue = defaultValue;
+ this.willMatch = willMatch;
+ }
+
+ return this
+ }
+
+ default(defaultValue){
+ const holder = new Switchem(
+ defaultValue, this.cases, this.willMatch
+ );
+
+ return holder.match(this.willMatch)
+ }
+
+ is(testValue, matchResult){
+ return new Switchem(
+ this.defaultValue,
+ [ ...this.cases, is(testValue, matchResult) ],
+ this.willMatch
+ )
+ }
+
+ match(matchValue){
+ return getMatchingKeyValuePair(
+ this.cases, matchValue, this.defaultValue
+ )
+ }
+ }
+
+ function switcher(input){
+ return new Switchem(input)
+ }
+
function symmetricDifference(listA) {
return listB => [
...filter(excludes(listB))(listA),
@@ -1680,10 +1797,6 @@
]
}
- function tail(listOrString) {
- return drop(1)(listOrString)
- }
-
function take(numberOfItems) {
return input => {
if (numberOfItems < 0) {
@@ -1894,6 +2007,7 @@
)
}
+ exports.RAMBDA_DELAY = RAMBDA_DELAY;
exports._arity = _arity;
exports._includes = _includes;
exports._indexOf = _indexOf;
@@ -1917,6 +2031,7 @@
exports.createCompareFunction = createCompareFunction;
exports.createObjectFromKeys = createObjectFromKeys;
exports.defaultTo = defaultTo;
+ exports.delay = delay;
exports.descend = descend;
exports.difference = difference;
exports.drop = drop;
@@ -1933,6 +2048,7 @@
exports.exists = exists;
exports.filter = filter;
exports.filterAsync = filterAsync;
+ exports.filterMap = filterMap;
exports.filterObject = filterObject;
exports.find = find;
exports.findIndex = findIndex;
@@ -1959,6 +2075,7 @@
exports.lastIndexOf = lastIndexOf;
exports.map = map;
exports.mapAsync = mapAsync;
+ exports.mapChain = mapChain;
exports.mapFn = mapFn;
exports.mapKeys = mapKeys;
exports.mapObject = mapObject;
@@ -1969,6 +2086,7 @@
exports.maxBy = maxBy;
exports.merge = merge;
exports.mergeTypes = mergeTypes;
+ exports.middle = middle;
exports.minBy = minBy;
exports.modifyItemAtIndex = modifyItemAtIndex;
exports.modifyPath = modifyPath;
@@ -1991,6 +2109,7 @@
exports.propEq = propEq;
exports.propOr = propOr;
exports.propSatisfies = propSatisfies;
+ exports.random = random;
exports.range = range;
exports.rangeDescending = rangeDescending;
exports.reduce = reduce;
@@ -2009,6 +2128,8 @@
exports.sortWith = sortWith;
exports.split = split;
exports.splitEvery = splitEvery;
+ exports.sum = sum;
+ exports.switcher = switcher;
exports.symmetricDifference = symmetricDifference;
exports.tail = tail;
exports.take = take;
diff --git a/docs/README.md b/docs/README.md
index efed3f30d..d2af17fd0 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -215,7 +215,7 @@ export function addProp(key, value) {
Tests
```javascript
-import { addProp } from "./addProp.js"
+import { addProp } from './addProp.js'
test('happy', () => {
const result = addProp('a', 1)({ b: 2 })
@@ -325,8 +325,8 @@ export function addPropToObjects (
Tests
```javascript
-import { pipe } from "./pipe.js"
-import { addPropToObjects } from "./addPropToObjects.js"
+import { pipe } from './pipe.js'
+import { addPropToObjects } from './addPropToObjects.js'
test('R.addPropToObjects', () => {
let result = pipe(
@@ -1941,6 +1941,45 @@ describe('R.defaultTo', () => {
[](#defaultTo)
+### delay
+
+```typescript
+
+delay(ms: number): Promise<'RAMBDA_DELAY'>
+```
+
+`setTimeout` as a promise that resolves to `RAMBDA_DELAY` string after `ms` milliseconds.
+
+
+
+All TypeScript definitions
+
+```typescript
+delay(ms: number): Promise<'RAMBDA_DELAY'>;
+```
+
+
+
+
+
+R.delay source
+
+```javascript
+export const RAMBDA_DELAY = 'RAMBDA_DELAY'
+
+export function delay(ms) {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve(RAMBDA_DELAY)
+ }, ms)
+ })
+}
+```
+
+
+
+[](#delay)
+
### descend
```typescript
@@ -2120,7 +2159,7 @@ drop(howMany: number): (list: T[]) => T[];
R.drop source
```javascript
-export function drop(howManyToDrop, ) {
+export function drop(howManyToDrop) {
return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
```
@@ -3529,7 +3568,7 @@ filter(
predicate: BooleanConstructor,
): (list: T[]) => ExcludeFalsy[];
filter(
- predicate: (value: T) => boolean,
+ predicate: (value: T, index: number) => boolean,
): (list: T[]) => T[];
...
...
@@ -3597,14 +3636,26 @@ const list = [1, 2, 3]
describe('R.filter with array', () => {
it('within pipe', () => {
- const _result = pipe(
+ const result = pipe(
list,
filter(x => {
x // $ExpectType number
return x > 1
}),
)
- _result // $ExpectType number[]
+ result // $ExpectType number[]
+ })
+
+ it('with index', () => {
+ const result = pipe(
+ list,
+ filter((x: number, i: number) => {
+ x // $ExpectType number
+ i // $ExpectType number
+ return x > 1
+ }),
+ )
+ result // $ExpectType number[]
})
it('complex example', () => {
@@ -3643,8 +3694,8 @@ describe('R.filter with array', () => {
const filterBar = (x: T): x is Bar => {
return typeof (x as Bar).b === 'string'
}
- const _result = pipe(testList, filter(filterBar))
- _result // $ExpectType Bar[]
+ const result = pipe(testList, filter(filterBar))
+ result // $ExpectType Bar[]
})
it('narrowing type - readonly', () => {
@@ -3659,14 +3710,14 @@ describe('R.filter with array', () => {
const filterBar = (x: T): x is Bar => {
return typeof (x as Bar).b === 'string'
}
- const _result = pipe(testList, filter(filterBar))
- _result // $ExpectType Bar[]
+ const result = pipe(testList, filter(filterBar))
+ result // $ExpectType Bar[]
})
it('filtering NonNullable - list of objects', () => {
const testList = [{ a: 1 }, { a: 2 }, false, { a: 3 }]
- const _result = pipe(testList, filter(Boolean))
- _result // $ExpectType { a: number; }[]
+ const result = pipe(testList, filter(Boolean))
+ result // $ExpectType { a: number; }[]
})
it('filtering NonNullable - readonly', () => {
@@ -3778,6 +3829,104 @@ describe('R.filter with array', () => {
[](#filterAsync)
+### filterMap
+
+```typescript
+
+filterMap(
+ fn: (value: T[number], index: number) => U,
+): (data: T) => Mapped>
+```
+
+Same as `R.map` but it filters out `null/undefined` if returned from functor functions.
+
+> :boom: This function doesn't work with objects (use R.mapObject instead)
+
+```javascript
+const result = R.pipe(
+ [1, 2, 3],
+ R.filterMap(x => x > 1 ? x : null)
+)
+// => [2, 3]
+```
+
+Try this R.filterMap example in Rambda REPL
+
+
+
+All TypeScript definitions
+
+```typescript
+filterMap(
+ fn: (value: T[number], index: number) => U,
+): (data: T) => Mapped>;
+filterMap(
+ fn: (value: T[number]) => U,
+): (data: T) => Mapped>;
+```
+
+
+
+
+
+R.filterMap source
+
+```javascript
+import {mapFn} from './map.js'
+
+export function filterMap(fn) {
+ return list => mapFn(fn, list).filter(Boolean)
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { filterMap } from './filterMap.js'
+
+const double = x => x > 1 ? x * 2 : null
+
+it('happy', () => {
+ expect(filterMap(double)([1, 2, 3])).toEqual([4, 6])
+})
+```
+
+
+
+
+
+TypeScript test
+
+```typescript
+import { filterMap, pipe } from 'rambda'
+
+const list = [1, 2, 3]
+
+it('R.filterMap - within pipe', () => {
+ const result = pipe(
+ list,
+ x => x,
+ filterMap(x => {
+ x // $ExpectType number
+ return Math.random() > 0.5 ? String(x) : null
+ }),
+ filterMap(x => {
+ x // $ExpectType string
+ return Math.random() > 0.5 ? Number(x) : ''
+ }),
+ )
+ result // $ExpectType number[]
+})
+```
+
+
+
+[](#filterMap)
+
### filterObject
```typescript
@@ -5151,9 +5300,6 @@ indexBy(
indexBy(
property: K
): (list: T[]) => Record;
-
-// API_MARKER_END
-// ============================================
```
@@ -5327,7 +5473,9 @@ describe('R.indexOf', () => {
```typescript
-init(input: T): T extends readonly [...infer U, any] ? U : [...T]
+init(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [...infer U, any] ? U : T : T extends string ? string : never
```
It returns all but the last element of list or string `input`.
@@ -5347,8 +5495,9 @@ const result = [
All TypeScript definitions
```typescript
-init(input: T): T extends readonly [...infer U, any] ? U : [...T];
-init(input: string): string;
+init(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [...infer U, any] ? U : T : T extends string ? string : never;
```
@@ -5401,7 +5550,7 @@ test('with string', () => {
TypeScript test
```typescript
-import { init } from 'rambda'
+import { map, pipe, init } from 'rambda'
describe('R.init', () => {
it('with string', () => {
@@ -5409,13 +5558,32 @@ describe('R.init', () => {
result // $ExpectType string
})
- it('with list - one type', () => {
- const result = init([1, 2, 3])
-
- result // $ExpectType number[]
+ it('with list - using const on short array', () => {
+ const result = pipe(
+ [1] as const,
+ map(x => x * 2),
+ init,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const on empty array', () => {
+ const result = pipe(
+ [] as const,
+ map(x => x * 2),
+ init,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const', () => {
+ const result = pipe(
+ [1, 2, 3] as const,
+ map(x => x * 2),
+ init,
+ )
+ result // $ExpectType [number, number]
})
it('with list - mixed types', () => {
- const result = init([1, 2, 3, 'foo', 'bar'])
+ const result = init(['foo', 'bar', 1, 2, 3])
result // $ExpectType (string | number)[]
})
@@ -6126,16 +6294,14 @@ It returns the result of looping through `iterable` with `fn`.
> :boom: This function doesn't work with objects (use R.mapObject instead)
```javascript
-const fn = x => x * 2
-
-const iterable = [1, 2]
-const obj = {a: 1, b: 2}
-
-const result = R.map(fn)(iterable),
+const result = R.pipe(
+ [1, 2],
+ R.map(x => x * 2)
+)
// => [2, 4]
```
-Try this R.map example in Rambda REPL
+Try this R.map example in Rambda REPL
@@ -6201,7 +6367,7 @@ import { map, pipe } from 'rambda'
const list = [1, 2, 3]
-it('R.map - within pipe', () => {
+it('R.map', () => {
const result = pipe(
list,
x => x,
@@ -6213,6 +6379,19 @@ it('R.map - within pipe', () => {
result // $ExpectType string[]
})
+it('R.map - index in functor', () => {
+ const result = pipe(
+ list,
+ x => x,
+ map((x, i) => {
+ x // $ExpectType number
+ i // $ExpectType number
+ return String(x)
+ }),
+ )
+ result // $ExpectType string[]
+})
+
it('R.map - without pipe', () => {
map(x => {
x // $ExpectType unknown
@@ -6380,6 +6559,165 @@ it('R.mapAsync', async () => {
[](#mapAsync)
+### mapChain
+
+```typescript
+
+mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped
+```
+
+Chained 2 or 3 `R.map` transformations as one.
+
+```javascript
+const result = R.pipe(
+ [1, 2],
+ R.mapChain(
+ x => x * 2,
+ x => [x, x > 3],
+ )
+)
+// => [[2, false], [4, true]]
+```
+
+Try this R.mapChain example in Rambda REPL
+
+
+
+All TypeScript definitions
+
+```typescript
+mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped;
+mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U) => V,
+): (data: T) => Mapped;
+mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped;
+mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U) => V,
+): (data: T) => Mapped;
+...
+...
+```
+
+
+
+
+
+R.mapChain source
+
+```javascript
+import { mapFn } from './map.js';
+
+export function mapChain(...fns) {
+ return list => {
+ let result = list.slice()
+ fns.forEach((fn) => {
+ result = mapFn(fn, result)
+ })
+ return result
+ }
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { mapChain } from './mapChain.js'
+
+const double = x => x * 2
+
+it('happy', () => {
+ expect(mapChain(double, double, double)([1, 2, 3])).toEqual([8, 16, 24])
+})
+```
+
+
+
+
+
+TypeScript test
+
+```typescript
+import { mapChain, pipe } from 'rambda'
+
+const list = [1, 2, 3]
+
+it('R.mapChain', () => {
+ const result = pipe(
+ list,
+ mapChain(
+ x => {
+ x // $ExpectType number
+ return String(x)
+ },
+ x => {
+ x // $ExpectType string
+ return x !== 'foo'
+ },
+ ),
+ )
+ result // $ExpectType boolean[]
+})
+
+it('R.mapChain - with index', () => {
+ const result = pipe(
+ list,
+ mapChain(
+ x => {
+ x // $ExpectType number
+ return String(x)
+ },
+ (x, i) => {
+ i // $ExpectType number
+ x // $ExpectType string
+ return x !== 'foo'
+ },
+ ),
+ )
+ result // $ExpectType boolean[]
+})
+
+it('R.mapChain - 3 functions', () => {
+ const result = pipe(
+ list,
+ x => x,
+ mapChain(
+ x => {
+ x // $ExpectType number
+ return String(x)
+ },
+ x => {
+ x // $ExpectType string
+ return x !== 'foo'
+ },
+ x => {
+ x // $ExpectType boolean
+ return x ? 'foo' : 'bar'
+ },
+ ),
+ )
+ result // $ExpectType ("foo" | "bar")[]
+})
+```
+
+
+
+[](#mapChain)
+
### mapKeys
```typescript
@@ -6435,7 +6773,7 @@ export function mapKeys(fn) {
Tests
```javascript
-import { mapKeys } from "./mapKeys.js"
+import { mapKeys } from './mapKeys.js'
test('happy', () => {
const result = mapKeys((prop, x) => `${ prop }-${x}`)({a:1, b: 2 })
@@ -7248,11 +7586,130 @@ export function mergeTypes(x) {
[](#mergeTypes)
-### minBy
+### middle
```typescript
-minBy(compareFn: (input: T) => Ord, x: T): (y: T) => T
+middle(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] : T['length'] extends 2 ? [] :
+ T extends [any, ...infer U, any] ? U : T : T extends string ? string : never
+```
+
+It returns all but the first and last element of `input`.
+
+```javascript
+const result = [
+ R.middle([1, 2, 3, 4]),
+ R.middle('bar')
+]
+// => [[2, 3], 'a']
+```
+
+Try this R.middle example in Rambda REPL
+
+
+
+All TypeScript definitions
+
+```typescript
+middle(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] : T['length'] extends 2 ? [] :
+ T extends [any, ...infer U, any] ? U : T : T extends string ? string : never;
+```
+
+
+
+
+
+R.middle source
+
+```javascript
+import { init } from './init.js'
+import { tail } from './tail.js'
+
+export function middle(listOrString) {
+ return tail(init(listOrString))
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { middle } from './middle'
+
+test('middle', () => {
+ expect(middle([1, 2, 3])).toEqual([2])
+ expect(middle([1, 2])).toEqual([])
+ expect(middle([1])).toEqual([])
+ expect(middle([])).toEqual([])
+
+ expect(middle('abc')).toBe('b')
+ expect(middle('ab')).toBe('')
+ expect(middle('a')).toBe('')
+ expect(middle('')).toBe('')
+})
+```
+
+
+
+
+
+TypeScript test
+
+```typescript
+import { map, middle, pipe } from 'rambda'
+
+describe('R.middle', () => {
+ it('with string', () => {
+ const result = middle('foo')
+
+ result // $ExpectType string
+ })
+ it('with list - using const on short array', () => {
+ const result = pipe(
+ [1, 2] as const,
+ map(x => x * 2),
+ middle,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const on empty array', () => {
+ const result = pipe(
+ [] as const,
+ map(x => x * 2),
+ middle,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const', () => {
+ const result = pipe(
+ [1, 2, 3, 4] as const,
+ map(x => x * 2),
+ middle,
+ )
+ result // $ExpectType [number, number]
+ })
+ it('with list - mixed types', () => {
+ const result = middle(['foo', 'bar', 1, 2, 3])
+
+ result // $ExpectType (string | number)[]
+ })
+})
+```
+
+
+
+[](#middle)
+
+### minBy
+
+```typescript
+
+minBy(compareFn: (input: T) => Ord, x: T): (y: T) => T
```
It returns the lesser value between `x` and `y` according to `compareFn` function.
@@ -9434,7 +9891,7 @@ test('with undefined', () => {
TypeScript test
```typescript
-import { pipe, pluck } from "rambda";
+import { pipe, pluck } from 'rambda';
it("R.pluck", () => {
const input = [
@@ -9861,6 +10318,56 @@ describe('R.propSatisfies', () => {
[](#propSatisfies)
+### random
+
+```typescript
+
+random(minInclusive: number, maxInclusive: number): number
+```
+
+It returns a random number between `min` inclusive and `max` inclusive.
+
+
+
+All TypeScript definitions
+
+```typescript
+random(minInclusive: number, maxInclusive: number): number;
+```
+
+
+
+
+
+R.random source
+
+```javascript
+export function random(min, max){
+ return Math.floor(Math.random() * (max - min + 1)) + min
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { random } from './random.js'
+import { range } from './range.js'
+import { uniq } from './uniq.js'
+
+test('happy', () => {
+ const result = uniq(range(100).map(() => random(0, 3))).sort()
+ expect(result).toEqual([0,1,2,3])
+})
+```
+
+
+
+[](#random)
+
### range
```typescript
@@ -10207,6 +10714,17 @@ describe('R.reject with array', () => {
}),
)
result // $ExpectType number[]
+ })
+ it('with index', () => {
+ const result = pipe(
+ list,
+ reject((x: number, i: number) => {
+ x // $ExpectType number
+ i // $ExpectType number
+ return x > 1
+ }),
+ )
+ result // $ExpectType number[]
})
it('narrowing type', () => {
interface Foo {
@@ -10565,15 +11083,6 @@ shuffle(list: T[]): T[]
It returns a randomized copy of array.
-```javascript
-const result = R.shuffle(
- [1, 2, 3]
-)
-// => [3, 1, 2] or [2, 3, 1] or ...
-```
-
-Try this R.shuffle example in Rambda REPL
-
All TypeScript definitions
@@ -10901,7 +11410,7 @@ sortByDescending(sortFn: (x: T) => Ord): (list: T[]) => T[];
R.sortByDescending source
```javascript
-import { sortByFn } from "./sortBy.js";
+import { sortByFn } from './sortBy.js';
export function sortByDescending(sortFn) {
return list => sortByFn(sortFn, list, true)
@@ -11600,6 +12109,300 @@ describe('R.splitEvery', () => {
[](#splitEvery)
+### sum
+
+```typescript
+
+sum(list: number[]): number
+```
+
+```javascript
+const result = R.sum(
+ [1,2,3]
+)
+// => 6
+```
+
+Try this R.sum example in Rambda REPL
+
+
+
+All TypeScript definitions
+
+```typescript
+sum(list: number[]): number;
+```
+
+
+
+
+
+R.sum source
+
+```javascript
+export function sum(list){
+ return list.reduce((acc, cur) => acc + cur, 0)
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { sum } from './sum.js'
+
+test('happy', () => {
+ expect(sum([1,2,3])).toEqual(6)
+})
+```
+
+
+
+[](#sum)
+
+### switcher
+
+```typescript
+
+switcher(valueToMatch: T): Switchem
+```
+
+```javascript
+const list = [1, 2, 3]
+
+const result = switcher(list.length)
+ .is(x => x < 2, 4)
+ .is(x => x < 4, 6)
+ .default(7)
+// => 6
+```
+
+Try this R.switcher example in Rambda REPL
+
+
+
+All TypeScript definitions
+
+```typescript
+switcher(valueToMatch: T): Switchem;
+switcher(valueToMatch: T): Switchem2;
+
+// API_MARKER_END
+// ============================================
+```
+
+
+
+
+
+R.switcher source
+
+```javascript
+import { equals } from './equals.js'
+
+const NO_MATCH_FOUND = Symbol ? Symbol('NO_MATCH_FOUND') : undefined
+
+const getMatchingKeyValuePair = (
+ cases, testValue, defaultValue
+) => {
+ let iterationValue
+
+ for (let index = 0; index < cases.length; index++){
+ iterationValue = cases[ index ].test(testValue)
+
+ if (iterationValue !== NO_MATCH_FOUND){
+ return iterationValue
+ }
+ }
+
+ return defaultValue
+}
+
+const isEqual = (testValue, matchValue) => {
+ const willReturn =
+ typeof testValue === 'function' ?
+ testValue(matchValue) :
+ equals(testValue)(matchValue)
+
+ return willReturn
+}
+
+const is = (testValue, matchResult = true) => ({
+ key : testValue,
+ test : matchValue =>
+ isEqual(testValue, matchValue) ? matchResult : NO_MATCH_FOUND,
+})
+
+class Switchem{
+ constructor(
+ defaultValue, cases, willMatch
+ ){
+ if (cases === undefined && willMatch === undefined){
+ this.cases = []
+ this.defaultValue = undefined
+ this.willMatch = defaultValue
+ } else {
+ this.cases = cases
+ this.defaultValue = defaultValue
+ this.willMatch = willMatch
+ }
+
+ return this
+ }
+
+ default(defaultValue){
+ const holder = new Switchem(
+ defaultValue, this.cases, this.willMatch
+ )
+
+ return holder.match(this.willMatch)
+ }
+
+ is(testValue, matchResult){
+ return new Switchem(
+ this.defaultValue,
+ [ ...this.cases, is(testValue, matchResult) ],
+ this.willMatch
+ )
+ }
+
+ match(matchValue){
+ return getMatchingKeyValuePair(
+ this.cases, matchValue, this.defaultValue
+ )
+ }
+}
+
+export function switcher(input){
+ return new Switchem(input)
+}
+```
+
+
+
+
+
+Tests
+
+```javascript
+import { switcher } from './switcher.js'
+import { tap } from './tap.js'
+
+test('with undefined', () => {
+ const result = switcher(undefined)
+ .is(x => x === 0, '0')
+ .is(x => x === undefined, 'UNDEFINED')
+ .default('3')
+
+ expect(result).toBe('UNDEFINED')
+})
+
+test('happy', () => {
+ const a = true
+ const b = false
+ const result = switcher([ a, b ])
+ .is([ false, false ], '0')
+ .is([ false, true ], '1')
+ .is([ true, true ], '2')
+ .default('3')
+
+ expect(result).toBe('3')
+})
+
+test('can compare objects', () => {
+ const result = switcher({ a : 1 })
+ .is({ a : 1 }, 'it is object')
+ .is('baz', 'it is baz')
+ .default('it is default')
+
+ expect(result).toBe('it is object')
+})
+
+test('options are mixture of functions and values - input match function', () => {
+ const fn = switcher('foo').is('bar', 1)
+ .is('foo', x => x + 1)
+ .default(1000)
+
+ expect(fn(2)).toBe(3)
+})
+
+test('options are mixture of functions and values - input match value', () => {
+ const result = switcher('bar').is('bar', 1)
+ .is('foo', x => x + 1)
+ .default(1000)
+
+ expect(result).toBe(1)
+})
+
+test('return function if all options are functions', () => {
+ const fn = switcher('foo')
+ .is('bar', tap)
+ .is('foo', x => x + 1)
+ .default(9)
+
+ expect(fn(2)).toBe(3)
+})
+
+const switchFn = input =>
+ switcher(input)
+ .is(x => x.length && x.length === 7, 'has length of 7')
+ .is('baz', 'it is baz')
+ .default('it is default')
+
+test('works with function as condition', () => {
+ expect(switchFn([ 0, 1, 2, 3, 4, 5, 6 ])).toBe('has length of 7')
+})
+
+test('works with string as condition', () => {
+ expect(switchFn('baz')).toBe('it is baz')
+})
+
+test('fallback to default input when no matches', () => {
+ expect(switchFn(1)).toBe('it is default')
+})
+```
+
+
+
+
+
+TypeScript test
+
+```typescript
+import { switcher } from 'rambda'
+
+describe('R.switcher', () => {
+ it('no transformation', () => {
+ const list = [1, 2, 3]
+
+ const result = switcher(list.length)
+ .is(x => x < 2, 4)
+ .is(x => x < 4, 6)
+ .default(7)
+
+ result // $ExpectType number
+ })
+ it('with transformation', () => {
+ const list = [1, 2, 3]
+ type Stage = 'firstStage' | 'secondStage' | 'thirdStage'
+
+ const result = switcher(list.length)
+ .is(x => x < 2, 'firstStage')
+ .is(x => x < 4, 'secondStage')
+ .default('thirdStage')
+
+ result // $ExpectType Stage
+ })
+})
+```
+
+
+
+[](#switcher)
+
### symmetricDifference
```typescript
@@ -11703,7 +12506,9 @@ describe('R.symmetricDifference', () => {
```typescript
-tail(input: T): T extends [any, ...infer U] ? U : [...T]
+tail(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [any, ...infer U] ? U : T : T extends string ? string : never
```
It returns all but the first element of `input`.
@@ -11723,8 +12528,9 @@ const result = [
All TypeScript definitions
```typescript
-tail(input: T): T extends [any, ...infer U] ? U : [...T];
-tail(input: string): string;
+tail(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [any, ...infer U] ? U : T : T extends string ? string : never;
```
@@ -11770,7 +12576,7 @@ test('tail', () => {
TypeScript test
```typescript
-import { tail } from 'rambda'
+import { map, pipe, tail } from 'rambda'
describe('R.tail', () => {
it('with string', () => {
@@ -11778,10 +12584,29 @@ describe('R.tail', () => {
result // $ExpectType string
})
- it('with list - one type', () => {
- const result = tail([1, 2, 3])
-
- result // $ExpectType number[]
+ it('with list - using const on short array', () => {
+ const result = pipe(
+ [1] as const,
+ map(x => x * 2),
+ tail,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const on empty array', () => {
+ const result = pipe(
+ [] as const,
+ map(x => x * 2),
+ tail,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const', () => {
+ const result = pipe(
+ [1, 2, 3] as const,
+ map(x => x * 2),
+ tail,
+ )
+ result // $ExpectType [number, number]
})
it('with list - mixed types', () => {
const result = tail(['foo', 'bar', 1, 2, 3])
@@ -13973,6 +14798,20 @@ describe('R.zipWith', () => {
## ❯ CHANGELOG
+11.1.0
+
+- Add `R.filterMap` - similar to Ruby `filter_map`
+
+- Add `R.mapChain` - when in `R.pipe` there are several `R.map` one after the other, then `R.mapChain` can be used instead.
+
+- Add `R.middle` - equal to `R.init` + `R.tail`
+
+- Add `R.random`, `R.shuffle`, `R.switcher`, `R.sum`, `R.delay` - imported from `Rambda`
+
+- Add index to `R.filter`/`R.reject` predicate signiture
+
+- Improve typing of `R.init`, `R.tail`
+
11.0.1
- Add missing JS change for `R.includes` and `R.excludes` methods in `11.0.0` release.
diff --git a/files/index.d.ts b/files/index.d.ts
index 26ac4d31b..c672f0bb0 100644
--- a/files/index.d.ts
+++ b/files/index.d.ts
@@ -123,14 +123,25 @@ export type FlattenObject = object extends T
} extends Record void>
? O
: never;
+
+type isfn = (fn: (x: T) => boolean, y: T) => U;
+type isfn2 = (fn: (x: T) => boolean, y: V) => U;
+
+interface Switchem {
+ is: isfn>;
+ default: (x: T) => T;
+}
+interface Switchem2 {
+ is: isfn2>;
+ default: (x: U) => U;
+}
+
// API_MARKER
/*
Method: modifyItemAtIndex
-Explanation:
-
-It replaces `index` in array `list` with the result of `replaceFn(list[i])`.
+Explanation: It replaces `index` in array `list` with the result of `replaceFn(list[i])`.
Example:
@@ -486,7 +497,7 @@ export function filter(
predicate: BooleanConstructor,
): (list: T[]) => ExcludeFalsy[];
export function filter(
- predicate: (value: T) => boolean,
+ predicate: (value: T, index: number) => boolean,
): (list: T[]) => T[];
/*
@@ -519,7 +530,7 @@ export function reject(
predicate: BooleanConstructor,
): (list: T[]) => (null | undefined)[];
export function reject(
- predicate: (value: T) => boolean,
+ predicate: (value: T, index: number) => boolean,
): (list: T[]) => T[];
/*
@@ -862,8 +873,9 @@ Notes:
*/
// @SINGLE_MARKER
-export function init(input: T): T extends readonly [...infer U, any] ? U : [...T];
-export function init(input: string): string;
+export function init(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [...infer U, any] ? U : T : T extends string ? string : never;
/*
Method: intersection
@@ -995,12 +1007,10 @@ Explanation: It returns the result of looping through `iterable` with `fn`.
Example:
```
-const fn = x => x * 2
-
-const iterable = [1, 2]
-const obj = {a: 1, b: 2}
-
-const result = R.map(fn)(iterable),
+const result = R.pipe(
+ [1, 2],
+ R.map(x => x * 2)
+)
// => [2, 4]
```
@@ -1017,6 +1027,85 @@ export function map(
fn: (value: T[number]) => U,
): (data: T) => Mapped;
+/*
+Method: mapChain
+
+Explanation: Chained 2 or 3 `R.map` transformations as one.
+
+Example:
+
+```
+const result = R.pipe(
+ [1, 2],
+ R.mapChain(
+ x => x * 2,
+ x => [x, x > 3],
+ )
+)
+// => [[2, false], [4, true]]
+```
+
+Categories: List
+
+Notes:
+
+*/
+// @SINGLE_MARKER
+export function mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U, index: number) => V,
+ fn3: (value: V, index: number) => Y,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U) => V,
+ fn3: (value: V) => Y,
+): (data: T) => Mapped;
+
+/*
+Method: filterMap
+
+Explanation: Same as `R.map` but it filters out `null/undefined` if returned from functor functions.
+
+Example:
+
+```
+const result = R.pipe(
+ [1, 2, 3],
+ R.filterMap(x => x > 1 ? x : null)
+)
+// => [2, 3]
+```
+
+Categories: List
+
+Notes: This function doesn't work with objects (use R.mapObject instead)
+
+*/
+// @SINGLE_MARKER
+export function filterMap(
+ fn: (value: T[number], index: number) => U,
+): (data: T) => Mapped>;
+export function filterMap(
+ fn: (value: T[number]) => U,
+): (data: T) => Mapped>;
+
/*
Method: mapObject
@@ -3072,6 +3161,31 @@ Notes:
// @SINGLE_MARKER
export function symmetricDifference(x: T[]): (y: T[]) => T[];
+/*
+Method: middle
+
+Explanation: It returns all but the first and last element of `input`.
+
+Example:
+
+```
+const result = [
+ R.middle([1, 2, 3, 4]),
+ R.middle('bar')
+]
+// => [[2, 3], 'a']
+```
+
+Categories: List, String
+
+Notes:
+
+*/
+// @SINGLE_MARKER
+export function middle(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] : T['length'] extends 2 ? [] :
+ T extends [any, ...infer U, any] ? U : T : T extends string ? string : never;
+
/*
Method: tail
@@ -3093,8 +3207,9 @@ Notes:
*/
// @SINGLE_MARKER
-export function tail(input: T): T extends [any, ...infer U] ? U : [...T];
-export function tail(input: string): string;
+export function tail(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [any, ...infer U] ? U : T : T extends string ? string : never;
/*
Method: take
@@ -4881,5 +4996,107 @@ export function indexBy(
property: K
): (list: T[]) => Record;
+/*
+Method: sum
+
+Explanation:
+
+Example:
+
+```
+const result = R.sum(
+ [1,2,3]
+)
+// => 6
+```
+
+Categories: List
+
+Notes:
+
+*/
+// @SINGLE_MARKER
+export function sum(list: number[]): number;
+
+/*
+Method: delay
+
+Explanation: `setTimeout` as a promise that resolves to `RAMBDA_DELAY` string after `ms` milliseconds.
+
+Example:
+
+```
+```
+
+Categories:
+
+Notes:
+
+*/
+// @SINGLE_MARKER
+export function delay(ms: number): Promise<'RAMBDA_DELAY'>;
+
+/*
+Method: shuffle
+
+Explanation: It returns a randomized copy of array.
+
+Example:
+
+```
+```
+
+Categories: List
+
+Notes:
+
+*/
+// @SINGLE_MARKER
+export function shuffle(list: T[]): T[];
+
+/*
+Method: random
+
+Explanation: It returns a random number between `min` inclusive and `max` inclusive.
+
+Example:
+
+```
+```
+
+Categories: List
+
+Notes:
+
+*/
+// @SINGLE_MARKER
+export function random(minInclusive: number, maxInclusive: number): number;
+
+/*
+Method: switcher
+
+Explanation:
+
+Example:
+
+```
+const list = [1, 2, 3]
+
+const result = switcher(list.length)
+ .is(x => x < 2, 4)
+ .is(x => x < 4, 6)
+ .default(7)
+// => 6
+```
+
+Categories: Logic
+
+Notes:
+
+*/
+// @SINGLE_MARKER
+export function switcher(valueToMatch: T): Switchem;
+export function switcher(valueToMatch: T): Switchem2;
+
// API_MARKER_END
// ============================================
diff --git a/index.d.cts b/index.d.cts
index 15ca919e3..f346e9437 100644
--- a/index.d.cts
+++ b/index.d.cts
@@ -124,6 +124,19 @@ export type FlattenObject = object extends T
? O
: never;
+type isfn = (fn: (x: T) => boolean, y: T) => U;
+type isfn2 = (fn: (x: T) => boolean, y: V) => U;
+
+interface Switchem {
+ is: isfn>;
+ default: (x: T) => T;
+}
+interface Switchem2 {
+ is: isfn2>;
+ default: (x: U) => U;
+}
+
+
/**
* It adds new key-value pair to the object.
*/
@@ -272,6 +285,11 @@ export function createObjectFromKeys(
*/
export function defaultTo(defaultValue: T): (input: unknown) => T;
+/**
+ * `setTimeout` as a promise that resolves to `RAMBDA_DELAY` string after `ms` milliseconds.
+ */
+export function delay(ms: number): Promise<'RAMBDA_DELAY'>;
+
/**
* Helper function to be used with `R.sort` to sort list in descending order.
*/
@@ -353,13 +371,23 @@ export function filter(
predicate: BooleanConstructor,
): (list: T[]) => ExcludeFalsy[];
export function filter(
- predicate: (value: T) => boolean,
+ predicate: (value: T, index: number) => boolean,
): (list: T[]) => T[];
export function filterAsync(
predicate: (value: T) => Promise,
): (list: T[]) => Promise;
+/**
+ * Same as `R.map` but it filters out `null/undefined` if returned from functor functions.
+ */
+export function filterMap(
+ fn: (value: T[number], index: number) => U,
+): (data: T) => Mapped>;
+export function filterMap(
+ fn: (value: T[number]) => U,
+): (data: T) => Mapped>;
+
/**
* It loops over each property of `obj` and returns a new object with only those properties that satisfy the `predicate`.
*/
@@ -455,9 +483,6 @@ export function indexBy(
property: K
): (list: T[]) => Record;
-// API_MARKER_END
-// ============================================
-
/**
* It uses `R.equals` for list of objects/arrays or native `indexOf` for any other case.
*/
@@ -466,8 +491,9 @@ export function indexOf(valueToFind: T): (list: T[]) => number;
/**
* It returns all but the last element of list or string `input`.
*/
-export function init(input: T): T extends readonly [...infer U, any] ? U : [...T];
-export function init(input: string): string;
+export function init(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [...infer U, any] ? U : T : T extends string ? string : never;
/**
* It generates a new string from `inputWithTags` by replacing all `{{x}}` occurrences with values provided by `templateArguments`.
@@ -538,6 +564,36 @@ export function mapAsync(
fn: (value: T[number]) => Promise,
): (data: T) => Promise>;
+/**
+ * Chained 2 or 3 `R.map` transformations as one.
+ */
+export function mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U, index: number) => V,
+ fn3: (value: V, index: number) => Y,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U) => V,
+ fn3: (value: V) => Y,
+): (data: T) => Mapped;
+
/**
* It returns a copy of `obj` with keys transformed by `fn`.
*/
@@ -610,6 +666,13 @@ export function merge(source: Source): (data: T) => Merge;
*/
export function mergeTypes(x: T): MergeTypes;
+/**
+ * It returns all but the first and last element of `input`.
+ */
+export function middle(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] : T['length'] extends 2 ? [] :
+ T extends [any, ...infer U, any] ? U : T : T extends string ? string : never;
+
/**
* It returns the lesser value between `x` and `y` according to `compareFn` function.
*/
@@ -1791,6 +1854,11 @@ export function propOr(property: P, defaultValue: T): (obj:
*/
export function propSatisfies(predicate: (x: T) => boolean, property: string): (obj: Record) => boolean;
+/**
+ * It returns a random number between `min` inclusive and `max` inclusive.
+ */
+export function random(minInclusive: number, maxInclusive: number): number;
+
/**
* It returns list of numbers between `startInclusive` to `endInclusive` markers.
*/
@@ -2208,6 +2276,14 @@ export function split(separator: string | RegExp): (str: string) => string[];
*/
export function splitEvery(sliceLength: number): (input: T[]) => (T[])[];
+export function sum(list: number[]): number;
+
+export function switcher(valueToMatch: T): Switchem;
+export function switcher(valueToMatch: T): Switchem2;
+
+// API_MARKER_END
+// ============================================
+
/**
* It returns all items that are in either of the lists, but not in both.
*
@@ -2218,8 +2294,9 @@ export function symmetricDifference(x: T[]): (y: T[]) => T[];
/**
* It returns all but the first element of `input`.
*/
-export function tail(input: T): T extends [any, ...infer U] ? U : [...T];
-export function tail(input: string): string;
+export function tail(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [any, ...infer U] ? U : T : T extends string ? string : never;
/**
* It returns the first `howMany` elements of `input`.
diff --git a/index.d.ts b/index.d.ts
index 15ca919e3..f346e9437 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -124,6 +124,19 @@ export type FlattenObject = object extends T
? O
: never;
+type isfn = (fn: (x: T) => boolean, y: T) => U;
+type isfn2 = (fn: (x: T) => boolean, y: V) => U;
+
+interface Switchem {
+ is: isfn>;
+ default: (x: T) => T;
+}
+interface Switchem2 {
+ is: isfn2>;
+ default: (x: U) => U;
+}
+
+
/**
* It adds new key-value pair to the object.
*/
@@ -272,6 +285,11 @@ export function createObjectFromKeys(
*/
export function defaultTo(defaultValue: T): (input: unknown) => T;
+/**
+ * `setTimeout` as a promise that resolves to `RAMBDA_DELAY` string after `ms` milliseconds.
+ */
+export function delay(ms: number): Promise<'RAMBDA_DELAY'>;
+
/**
* Helper function to be used with `R.sort` to sort list in descending order.
*/
@@ -353,13 +371,23 @@ export function filter(
predicate: BooleanConstructor,
): (list: T[]) => ExcludeFalsy[];
export function filter(
- predicate: (value: T) => boolean,
+ predicate: (value: T, index: number) => boolean,
): (list: T[]) => T[];
export function filterAsync(
predicate: (value: T) => Promise,
): (list: T[]) => Promise;
+/**
+ * Same as `R.map` but it filters out `null/undefined` if returned from functor functions.
+ */
+export function filterMap(
+ fn: (value: T[number], index: number) => U,
+): (data: T) => Mapped>;
+export function filterMap(
+ fn: (value: T[number]) => U,
+): (data: T) => Mapped>;
+
/**
* It loops over each property of `obj` and returns a new object with only those properties that satisfy the `predicate`.
*/
@@ -455,9 +483,6 @@ export function indexBy(
property: K
): (list: T[]) => Record;
-// API_MARKER_END
-// ============================================
-
/**
* It uses `R.equals` for list of objects/arrays or native `indexOf` for any other case.
*/
@@ -466,8 +491,9 @@ export function indexOf(valueToFind: T): (list: T[]) => number;
/**
* It returns all but the last element of list or string `input`.
*/
-export function init(input: T): T extends readonly [...infer U, any] ? U : [...T];
-export function init(input: string): string;
+export function init(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [...infer U, any] ? U : T : T extends string ? string : never;
/**
* It generates a new string from `inputWithTags` by replacing all `{{x}}` occurrences with values provided by `templateArguments`.
@@ -538,6 +564,36 @@ export function mapAsync(
fn: (value: T[number]) => Promise,
): (data: T) => Promise>;
+/**
+ * Chained 2 or 3 `R.map` transformations as one.
+ */
+export function mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U, index: number) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U) => V,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number], index: number) => U,
+ fn2: (value: U, index: number) => V,
+ fn3: (value: V, index: number) => Y,
+): (data: T) => Mapped;
+export function mapChain(
+ fn1: (value: T[number]) => U,
+ fn2: (value: U) => V,
+ fn3: (value: V) => Y,
+): (data: T) => Mapped;
+
/**
* It returns a copy of `obj` with keys transformed by `fn`.
*/
@@ -610,6 +666,13 @@ export function merge(source: Source): (data: T) => Merge;
*/
export function mergeTypes(x: T): MergeTypes;
+/**
+ * It returns all but the first and last element of `input`.
+ */
+export function middle(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] : T['length'] extends 2 ? [] :
+ T extends [any, ...infer U, any] ? U : T : T extends string ? string : never;
+
/**
* It returns the lesser value between `x` and `y` according to `compareFn` function.
*/
@@ -1791,6 +1854,11 @@ export function propOr(property: P, defaultValue: T): (obj:
*/
export function propSatisfies(predicate: (x: T) => boolean, property: string): (obj: Record) => boolean;
+/**
+ * It returns a random number between `min` inclusive and `max` inclusive.
+ */
+export function random(minInclusive: number, maxInclusive: number): number;
+
/**
* It returns list of numbers between `startInclusive` to `endInclusive` markers.
*/
@@ -2208,6 +2276,14 @@ export function split(separator: string | RegExp): (str: string) => string[];
*/
export function splitEvery(sliceLength: number): (input: T[]) => (T[])[];
+export function sum(list: number[]): number;
+
+export function switcher(valueToMatch: T): Switchem;
+export function switcher(valueToMatch: T): Switchem2;
+
+// API_MARKER_END
+// ============================================
+
/**
* It returns all items that are in either of the lists, but not in both.
*
@@ -2218,8 +2294,9 @@ export function symmetricDifference(x: T[]): (y: T[]) => T[];
/**
* It returns all but the first element of `input`.
*/
-export function tail(input: T): T extends [any, ...infer U] ? U : [...T];
-export function tail(input: string): string;
+export function tail(input: T): T extends unknown[] ?
+ T['length'] extends 0 ? [] : T['length'] extends 1 ? [] :
+ T extends [any, ...infer U] ? U : T : T extends string ? string : never;
/**
* It returns the first `howMany` elements of `input`.
diff --git a/rambda.js b/rambda.js
index 51adfb450..3ad5d98df 100644
--- a/rambda.js
+++ b/rambda.js
@@ -17,6 +17,7 @@ export * from './src/count.js'
export * from './src/countBy.js'
export * from './src/createObjectFromKeys.js'
export * from './src/defaultTo.js'
+export * from './src/delay.js'
export * from './src/descend.js'
export * from './src/difference.js'
export * from './src/drop.js'
@@ -32,6 +33,7 @@ export * from './src/excludes.js'
export * from './src/exists.js'
export * from './src/filter.js'
export * from './src/filterAsync.js'
+export * from './src/filterMap.js'
export * from './src/filterObject.js'
export * from './src/find.js'
export * from './src/findIndex.js'
@@ -56,6 +58,7 @@ export * from './src/last.js'
export * from './src/lastIndexOf.js'
export * from './src/map.js'
export * from './src/mapAsync.js'
+export * from './src/mapChain.js'
export * from './src/mapKeys.js'
export * from './src/mapObject.js'
export * from './src/mapObjectAsync.js'
@@ -65,6 +68,7 @@ export * from './src/match.js'
export * from './src/maxBy.js'
export * from './src/merge.js'
export * from './src/mergeTypes.js'
+export * from './src/middle.js'
export * from './src/minBy.js'
export * from './src/modifyItemAtIndex.js'
export * from './src/modifyPath.js'
@@ -87,6 +91,7 @@ export * from './src/prop.js'
export * from './src/propEq.js'
export * from './src/propOr.js'
export * from './src/propSatisfies.js'
+export * from './src/random.js'
export * from './src/range.js'
export * from './src/rangeDescending.js'
export * from './src/reduce.js'
@@ -104,6 +109,8 @@ export * from './src/sortObject.js'
export * from './src/sortWith.js'
export * from './src/split.js'
export * from './src/splitEvery.js'
+export * from './src/sum.js'
+export * from './src/switcher.js'
export * from './src/symmetricDifference.js'
export * from './src/tail.js'
export * from './src/take.js'
diff --git a/source/addProp.spec.js b/source/addProp.spec.js
index d1dd641f4..5ef8179bb 100644
--- a/source/addProp.spec.js
+++ b/source/addProp.spec.js
@@ -1,4 +1,4 @@
-import { addProp } from "./addProp.js"
+import { addProp } from './addProp.js'
test('happy', () => {
const result = addProp('a', 1)({ b: 2 })
diff --git a/source/addPropToObjects.spec.js b/source/addPropToObjects.spec.js
index 2a948808f..0a9893008 100644
--- a/source/addPropToObjects.spec.js
+++ b/source/addPropToObjects.spec.js
@@ -1,5 +1,5 @@
-import { pipe } from "./pipe.js"
-import { addPropToObjects } from "./addPropToObjects.js"
+import { pipe } from './pipe.js'
+import { addPropToObjects } from './addPropToObjects.js'
test('R.addPropToObjects', () => {
let result = pipe(
diff --git a/source/delay.js b/source/delay.js
index f7ebcb436..6276977b0 100644
--- a/source/delay.js
+++ b/source/delay.js
@@ -1,9 +1,9 @@
-export const DELAY = 'RAMBDA_DELAY'
+export const RAMBDA_DELAY = 'RAMBDA_DELAY'
export function delay(ms) {
return new Promise(resolve => {
setTimeout(() => {
- resolve(DELAY)
+ resolve(RAMBDA_DELAY)
}, ms)
})
}
diff --git a/source/delay.spec.js b/source/delay.spec.js
deleted file mode 100644
index 793513f9f..000000000
--- a/source/delay.spec.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { DELAY, delay } from './delay.js'
-
-test('usage with variables', async () => {
- await expect(delay(500)).resolves.toBe(DELAY)
-})
diff --git a/source/drop.js b/source/drop.js
index 5e1ff66c7..658791a9d 100644
--- a/source/drop.js
+++ b/source/drop.js
@@ -1,3 +1,3 @@
-export function drop(howManyToDrop, ) {
+export function drop(howManyToDrop) {
return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
diff --git a/source/filter-spec.ts b/source/filter-spec.ts
index 4013d3d4d..9c9a5a2a8 100644
--- a/source/filter-spec.ts
+++ b/source/filter-spec.ts
@@ -4,14 +4,26 @@ const list = [1, 2, 3]
describe('R.filter with array', () => {
it('within pipe', () => {
- const _result = pipe(
+ const result = pipe(
list,
filter(x => {
x // $ExpectType number
return x > 1
}),
)
- _result // $ExpectType number[]
+ result // $ExpectType number[]
+ })
+
+ it('with index', () => {
+ const result = pipe(
+ list,
+ filter((x: number, i: number) => {
+ x // $ExpectType number
+ i // $ExpectType number
+ return x > 1
+ }),
+ )
+ result // $ExpectType number[]
})
it('complex example', () => {
@@ -50,8 +62,8 @@ describe('R.filter with array', () => {
const filterBar = (x: T): x is Bar => {
return typeof (x as Bar).b === 'string'
}
- const _result = pipe(testList, filter(filterBar))
- _result // $ExpectType Bar[]
+ const result = pipe(testList, filter(filterBar))
+ result // $ExpectType Bar[]
})
it('narrowing type - readonly', () => {
@@ -66,14 +78,14 @@ describe('R.filter with array', () => {
const filterBar = (x: T): x is Bar => {
return typeof (x as Bar).b === 'string'
}
- const _result = pipe(testList, filter(filterBar))
- _result // $ExpectType Bar[]
+ const result = pipe(testList, filter(filterBar))
+ result // $ExpectType Bar[]
})
it('filtering NonNullable - list of objects', () => {
const testList = [{ a: 1 }, { a: 2 }, false, { a: 3 }]
- const _result = pipe(testList, filter(Boolean))
- _result // $ExpectType { a: number; }[]
+ const result = pipe(testList, filter(Boolean))
+ result // $ExpectType { a: number; }[]
})
it('filtering NonNullable - readonly', () => {
diff --git a/source/filterMap-spec.ts b/source/filterMap-spec.ts
new file mode 100644
index 000000000..548865071
--- /dev/null
+++ b/source/filterMap-spec.ts
@@ -0,0 +1,19 @@
+import { filterMap, pipe } from 'rambda'
+
+const list = [1, 2, 3]
+
+it('R.filterMap - within pipe', () => {
+ const result = pipe(
+ list,
+ x => x,
+ filterMap(x => {
+ x // $ExpectType number
+ return Math.random() > 0.5 ? String(x) : null
+ }),
+ filterMap(x => {
+ x // $ExpectType string
+ return Math.random() > 0.5 ? Number(x) : ''
+ }),
+ )
+ result // $ExpectType number[]
+})
diff --git a/source/filterMap.js b/source/filterMap.js
new file mode 100644
index 000000000..5e09e2346
--- /dev/null
+++ b/source/filterMap.js
@@ -0,0 +1,5 @@
+import {mapFn} from './map.js'
+
+export function filterMap(fn) {
+ return list => mapFn(fn, list).filter(Boolean)
+}
diff --git a/source/filterMap.spec.js b/source/filterMap.spec.js
new file mode 100644
index 000000000..d87a70643
--- /dev/null
+++ b/source/filterMap.spec.js
@@ -0,0 +1,7 @@
+import { filterMap } from './filterMap.js'
+
+const double = x => x > 1 ? x * 2 : null
+
+it('happy', () => {
+ expect(filterMap(double)([1, 2, 3])).toEqual([4, 6])
+})
diff --git a/source/init-spec.ts b/source/init-spec.ts
index 61f0bf8c0..b0346cdb5 100644
--- a/source/init-spec.ts
+++ b/source/init-spec.ts
@@ -1,4 +1,4 @@
-import { init } from 'rambda'
+import { map, pipe, init } from 'rambda'
describe('R.init', () => {
it('with string', () => {
@@ -6,13 +6,32 @@ describe('R.init', () => {
result // $ExpectType string
})
- it('with list - one type', () => {
- const result = init([1, 2, 3])
-
- result // $ExpectType number[]
+ it('with list - using const on short array', () => {
+ const result = pipe(
+ [1] as const,
+ map(x => x * 2),
+ init,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const on empty array', () => {
+ const result = pipe(
+ [] as const,
+ map(x => x * 2),
+ init,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const', () => {
+ const result = pipe(
+ [1, 2, 3] as const,
+ map(x => x * 2),
+ init,
+ )
+ result // $ExpectType [number, number]
})
it('with list - mixed types', () => {
- const result = init([1, 2, 3, 'foo', 'bar'])
+ const result = init(['foo', 'bar', 1, 2, 3])
result // $ExpectType (string | number)[]
})
diff --git a/source/map-spec.ts b/source/map-spec.ts
index 4152beab3..b52bab589 100644
--- a/source/map-spec.ts
+++ b/source/map-spec.ts
@@ -2,7 +2,7 @@ import { map, pipe } from 'rambda'
const list = [1, 2, 3]
-it('R.map - within pipe', () => {
+it('R.map', () => {
const result = pipe(
list,
x => x,
@@ -14,6 +14,19 @@ it('R.map - within pipe', () => {
result // $ExpectType string[]
})
+it('R.map - index in functor', () => {
+ const result = pipe(
+ list,
+ x => x,
+ map((x, i) => {
+ x // $ExpectType number
+ i // $ExpectType number
+ return String(x)
+ }),
+ )
+ result // $ExpectType string[]
+})
+
it('R.map - without pipe', () => {
map(x => {
x // $ExpectType unknown
diff --git a/source/mapChain-spec.ts b/source/mapChain-spec.ts
new file mode 100644
index 000000000..c2c2edcc2
--- /dev/null
+++ b/source/mapChain-spec.ts
@@ -0,0 +1,60 @@
+import { mapChain, pipe } from 'rambda'
+
+const list = [1, 2, 3]
+
+it('R.mapChain', () => {
+ const result = pipe(
+ list,
+ mapChain(
+ x => {
+ x // $ExpectType number
+ return String(x)
+ },
+ x => {
+ x // $ExpectType string
+ return x !== 'foo'
+ },
+ ),
+ )
+ result // $ExpectType boolean[]
+})
+
+it('R.mapChain - with index', () => {
+ const result = pipe(
+ list,
+ mapChain(
+ x => {
+ x // $ExpectType number
+ return String(x)
+ },
+ (x, i) => {
+ i // $ExpectType number
+ x // $ExpectType string
+ return x !== 'foo'
+ },
+ ),
+ )
+ result // $ExpectType boolean[]
+})
+
+it('R.mapChain - 3 functions', () => {
+ const result = pipe(
+ list,
+ x => x,
+ mapChain(
+ x => {
+ x // $ExpectType number
+ return String(x)
+ },
+ x => {
+ x // $ExpectType string
+ return x !== 'foo'
+ },
+ x => {
+ x // $ExpectType boolean
+ return x ? 'foo' : 'bar'
+ },
+ ),
+ )
+ result // $ExpectType ("foo" | "bar")[]
+})
diff --git a/source/mapChain.js b/source/mapChain.js
new file mode 100644
index 000000000..1543c9a22
--- /dev/null
+++ b/source/mapChain.js
@@ -0,0 +1,11 @@
+import { mapFn } from './map.js';
+
+export function mapChain(...fns) {
+ return list => {
+ let result = list.slice()
+ fns.forEach((fn) => {
+ result = mapFn(fn, result)
+ })
+ return result
+ }
+}
diff --git a/source/mapChain.spec.js b/source/mapChain.spec.js
new file mode 100644
index 000000000..1058b6084
--- /dev/null
+++ b/source/mapChain.spec.js
@@ -0,0 +1,7 @@
+import { mapChain } from './mapChain.js'
+
+const double = x => x * 2
+
+it('happy', () => {
+ expect(mapChain(double, double, double)([1, 2, 3])).toEqual([8, 16, 24])
+})
diff --git a/source/mapKeys.spec.js b/source/mapKeys.spec.js
index 47f443e1b..6358439b9 100644
--- a/source/mapKeys.spec.js
+++ b/source/mapKeys.spec.js
@@ -1,4 +1,4 @@
-import { mapKeys } from "./mapKeys.js"
+import { mapKeys } from './mapKeys.js'
test('happy', () => {
const result = mapKeys((prop, x) => `${ prop }-${x}`)({a:1, b: 2 })
diff --git a/source/middle-spec.ts b/source/middle-spec.ts
new file mode 100644
index 000000000..529ae45ca
--- /dev/null
+++ b/source/middle-spec.ts
@@ -0,0 +1,38 @@
+import { map, middle, pipe } from 'rambda'
+
+describe('R.middle', () => {
+ it('with string', () => {
+ const result = middle('foo')
+
+ result // $ExpectType string
+ })
+ it('with list - using const on short array', () => {
+ const result = pipe(
+ [1, 2] as const,
+ map(x => x * 2),
+ middle,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const on empty array', () => {
+ const result = pipe(
+ [] as const,
+ map(x => x * 2),
+ middle,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const', () => {
+ const result = pipe(
+ [1, 2, 3, 4] as const,
+ map(x => x * 2),
+ middle,
+ )
+ result // $ExpectType [number, number]
+ })
+ it('with list - mixed types', () => {
+ const result = middle(['foo', 'bar', 1, 2, 3])
+
+ result // $ExpectType (string | number)[]
+ })
+})
diff --git a/source/middle.js b/source/middle.js
new file mode 100644
index 000000000..be537f1bb
--- /dev/null
+++ b/source/middle.js
@@ -0,0 +1,6 @@
+import { init } from './init.js'
+import { tail } from './tail.js'
+
+export function middle(listOrString) {
+ return tail(init(listOrString))
+}
diff --git a/source/middle.spec.js b/source/middle.spec.js
new file mode 100644
index 000000000..a390be344
--- /dev/null
+++ b/source/middle.spec.js
@@ -0,0 +1,13 @@
+import { middle } from './middle'
+
+test('middle', () => {
+ expect(middle([1, 2, 3])).toEqual([2])
+ expect(middle([1, 2])).toEqual([])
+ expect(middle([1])).toEqual([])
+ expect(middle([])).toEqual([])
+
+ expect(middle('abc')).toBe('b')
+ expect(middle('ab')).toBe('')
+ expect(middle('a')).toBe('')
+ expect(middle('')).toBe('')
+})
diff --git a/source/pluck-spec.ts b/source/pluck-spec.ts
index 1d048373a..3c15710ec 100644
--- a/source/pluck-spec.ts
+++ b/source/pluck-spec.ts
@@ -1,4 +1,4 @@
-import { pipe, pluck } from "rambda";
+import { pipe, pluck } from 'rambda';
it("R.pluck", () => {
const input = [
diff --git a/source/random.js b/source/random.js
new file mode 100644
index 000000000..ebc45fbb2
--- /dev/null
+++ b/source/random.js
@@ -0,0 +1,3 @@
+export function random(min, max){
+ return Math.floor(Math.random() * (max - min + 1)) + min
+}
diff --git a/source/random.spec.js b/source/random.spec.js
new file mode 100644
index 000000000..f44184ff8
--- /dev/null
+++ b/source/random.spec.js
@@ -0,0 +1,9 @@
+import { random } from './random.js'
+import { range } from './range.js'
+import { uniq } from './uniq.js'
+
+test('happy', () => {
+ const result = uniq(range(100).map(() => random(0, 3))).sort()
+ expect(result).toEqual([0,1,2,3])
+})
+
diff --git a/source/reject-spec.ts b/source/reject-spec.ts
index f43e9e49d..6fa72992c 100644
--- a/source/reject-spec.ts
+++ b/source/reject-spec.ts
@@ -12,6 +12,17 @@ describe('R.reject with array', () => {
}),
)
result // $ExpectType number[]
+ })
+ it('with index', () => {
+ const result = pipe(
+ list,
+ reject((x: number, i: number) => {
+ x // $ExpectType number
+ i // $ExpectType number
+ return x > 1
+ }),
+ )
+ result // $ExpectType number[]
})
it('narrowing type', () => {
interface Foo {
diff --git a/source/sortByDescending.js b/source/sortByDescending.js
index 86d702d81..46522d4af 100644
--- a/source/sortByDescending.js
+++ b/source/sortByDescending.js
@@ -1,4 +1,4 @@
-import { sortByFn } from "./sortBy.js";
+import { sortByFn } from './sortBy.js';
export function sortByDescending(sortFn) {
return list => sortByFn(sortFn, list, true)
diff --git a/source/sum.js b/source/sum.js
new file mode 100644
index 000000000..fc898956a
--- /dev/null
+++ b/source/sum.js
@@ -0,0 +1,3 @@
+export function sum(list){
+ return list.reduce((acc, cur) => acc + cur, 0)
+}
diff --git a/source/sum.spec.js b/source/sum.spec.js
new file mode 100644
index 000000000..43d85963c
--- /dev/null
+++ b/source/sum.spec.js
@@ -0,0 +1,5 @@
+import { sum } from './sum.js'
+
+test('happy', () => {
+ expect(sum([1,2,3])).toEqual(6)
+})
diff --git a/source/switcher-spec.ts b/source/switcher-spec.ts
new file mode 100644
index 000000000..73b3fc80b
--- /dev/null
+++ b/source/switcher-spec.ts
@@ -0,0 +1,25 @@
+import { switcher } from 'rambda'
+
+describe('R.switcher', () => {
+ it('no transformation', () => {
+ const list = [1, 2, 3]
+
+ const result = switcher(list.length)
+ .is(x => x < 2, 4)
+ .is(x => x < 4, 6)
+ .default(7)
+
+ result // $ExpectType number
+ })
+ it('with transformation', () => {
+ const list = [1, 2, 3]
+ type Stage = 'firstStage' | 'secondStage' | 'thirdStage'
+
+ const result = switcher(list.length)
+ .is(x => x < 2, 'firstStage')
+ .is(x => x < 4, 'secondStage')
+ .default('thirdStage')
+
+ result // $ExpectType Stage
+ })
+})
diff --git a/source/switcher.js b/source/switcher.js
new file mode 100644
index 000000000..a4aa23b8e
--- /dev/null
+++ b/source/switcher.js
@@ -0,0 +1,78 @@
+import { equals } from './equals.js'
+
+const NO_MATCH_FOUND = Symbol ? Symbol('NO_MATCH_FOUND') : undefined
+
+const getMatchingKeyValuePair = (
+ cases, testValue, defaultValue
+) => {
+ let iterationValue
+
+ for (let index = 0; index < cases.length; index++){
+ iterationValue = cases[ index ].test(testValue)
+
+ if (iterationValue !== NO_MATCH_FOUND){
+ return iterationValue
+ }
+ }
+
+ return defaultValue
+}
+
+const isEqual = (testValue, matchValue) => {
+ const willReturn =
+ typeof testValue === 'function' ?
+ testValue(matchValue) :
+ equals(testValue)(matchValue)
+
+ return willReturn
+}
+
+const is = (testValue, matchResult = true) => ({
+ key : testValue,
+ test : matchValue =>
+ isEqual(testValue, matchValue) ? matchResult : NO_MATCH_FOUND,
+})
+
+class Switchem{
+ constructor(
+ defaultValue, cases, willMatch
+ ){
+ if (cases === undefined && willMatch === undefined){
+ this.cases = []
+ this.defaultValue = undefined
+ this.willMatch = defaultValue
+ } else {
+ this.cases = cases
+ this.defaultValue = defaultValue
+ this.willMatch = willMatch
+ }
+
+ return this
+ }
+
+ default(defaultValue){
+ const holder = new Switchem(
+ defaultValue, this.cases, this.willMatch
+ )
+
+ return holder.match(this.willMatch)
+ }
+
+ is(testValue, matchResult){
+ return new Switchem(
+ this.defaultValue,
+ [ ...this.cases, is(testValue, matchResult) ],
+ this.willMatch
+ )
+ }
+
+ match(matchValue){
+ return getMatchingKeyValuePair(
+ this.cases, matchValue, this.defaultValue
+ )
+ }
+}
+
+export function switcher(input){
+ return new Switchem(input)
+}
diff --git a/source/switcher.spec.js b/source/switcher.spec.js
new file mode 100644
index 000000000..ced4c07d8
--- /dev/null
+++ b/source/switcher.spec.js
@@ -0,0 +1,75 @@
+import { switcher } from './switcher.js'
+import { tap } from './tap.js'
+
+test('with undefined', () => {
+ const result = switcher(undefined)
+ .is(x => x === 0, '0')
+ .is(x => x === undefined, 'UNDEFINED')
+ .default('3')
+
+ expect(result).toBe('UNDEFINED')
+})
+
+test('happy', () => {
+ const a = true
+ const b = false
+ const result = switcher([ a, b ])
+ .is([ false, false ], '0')
+ .is([ false, true ], '1')
+ .is([ true, true ], '2')
+ .default('3')
+
+ expect(result).toBe('3')
+})
+
+test('can compare objects', () => {
+ const result = switcher({ a : 1 })
+ .is({ a : 1 }, 'it is object')
+ .is('baz', 'it is baz')
+ .default('it is default')
+
+ expect(result).toBe('it is object')
+})
+
+test('options are mixture of functions and values - input match function', () => {
+ const fn = switcher('foo').is('bar', 1)
+ .is('foo', x => x + 1)
+ .default(1000)
+
+ expect(fn(2)).toBe(3)
+})
+
+test('options are mixture of functions and values - input match value', () => {
+ const result = switcher('bar').is('bar', 1)
+ .is('foo', x => x + 1)
+ .default(1000)
+
+ expect(result).toBe(1)
+})
+
+test('return function if all options are functions', () => {
+ const fn = switcher('foo')
+ .is('bar', tap)
+ .is('foo', x => x + 1)
+ .default(9)
+
+ expect(fn(2)).toBe(3)
+})
+
+const switchFn = input =>
+ switcher(input)
+ .is(x => x.length && x.length === 7, 'has length of 7')
+ .is('baz', 'it is baz')
+ .default('it is default')
+
+test('works with function as condition', () => {
+ expect(switchFn([ 0, 1, 2, 3, 4, 5, 6 ])).toBe('has length of 7')
+})
+
+test('works with string as condition', () => {
+ expect(switchFn('baz')).toBe('it is baz')
+})
+
+test('fallback to default input when no matches', () => {
+ expect(switchFn(1)).toBe('it is default')
+})
diff --git a/source/tail-spec.ts b/source/tail-spec.ts
index 0e9e03397..902ed5189 100644
--- a/source/tail-spec.ts
+++ b/source/tail-spec.ts
@@ -1,4 +1,4 @@
-import { tail } from 'rambda'
+import { map, pipe, tail } from 'rambda'
describe('R.tail', () => {
it('with string', () => {
@@ -6,10 +6,29 @@ describe('R.tail', () => {
result // $ExpectType string
})
- it('with list - one type', () => {
- const result = tail([1, 2, 3])
-
- result // $ExpectType number[]
+ it('with list - using const on short array', () => {
+ const result = pipe(
+ [1] as const,
+ map(x => x * 2),
+ tail,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const on empty array', () => {
+ const result = pipe(
+ [] as const,
+ map(x => x * 2),
+ tail,
+ )
+ result // $ExpectType []
+ })
+ it('with list - using const', () => {
+ const result = pipe(
+ [1, 2, 3] as const,
+ map(x => x * 2),
+ tail,
+ )
+ result // $ExpectType [number, number]
})
it('with list - mixed types', () => {
const result = tail(['foo', 'bar', 1, 2, 3])
diff --git a/src/delay.js b/src/delay.js
new file mode 100644
index 000000000..6276977b0
--- /dev/null
+++ b/src/delay.js
@@ -0,0 +1,9 @@
+export const RAMBDA_DELAY = 'RAMBDA_DELAY'
+
+export function delay(ms) {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve(RAMBDA_DELAY)
+ }, ms)
+ })
+}
diff --git a/src/drop.js b/src/drop.js
index 5e1ff66c7..658791a9d 100644
--- a/src/drop.js
+++ b/src/drop.js
@@ -1,3 +1,3 @@
-export function drop(howManyToDrop, ) {
+export function drop(howManyToDrop) {
return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
diff --git a/src/filterMap.js b/src/filterMap.js
new file mode 100644
index 000000000..5e09e2346
--- /dev/null
+++ b/src/filterMap.js
@@ -0,0 +1,5 @@
+import {mapFn} from './map.js'
+
+export function filterMap(fn) {
+ return list => mapFn(fn, list).filter(Boolean)
+}
diff --git a/src/mapChain.js b/src/mapChain.js
new file mode 100644
index 000000000..1543c9a22
--- /dev/null
+++ b/src/mapChain.js
@@ -0,0 +1,11 @@
+import { mapFn } from './map.js';
+
+export function mapChain(...fns) {
+ return list => {
+ let result = list.slice()
+ fns.forEach((fn) => {
+ result = mapFn(fn, result)
+ })
+ return result
+ }
+}
diff --git a/src/middle.js b/src/middle.js
new file mode 100644
index 000000000..be537f1bb
--- /dev/null
+++ b/src/middle.js
@@ -0,0 +1,6 @@
+import { init } from './init.js'
+import { tail } from './tail.js'
+
+export function middle(listOrString) {
+ return tail(init(listOrString))
+}
diff --git a/src/random.js b/src/random.js
new file mode 100644
index 000000000..ebc45fbb2
--- /dev/null
+++ b/src/random.js
@@ -0,0 +1,3 @@
+export function random(min, max){
+ return Math.floor(Math.random() * (max - min + 1)) + min
+}
diff --git a/src/sortByDescending.js b/src/sortByDescending.js
index 86d702d81..46522d4af 100644
--- a/src/sortByDescending.js
+++ b/src/sortByDescending.js
@@ -1,4 +1,4 @@
-import { sortByFn } from "./sortBy.js";
+import { sortByFn } from './sortBy.js';
export function sortByDescending(sortFn) {
return list => sortByFn(sortFn, list, true)
diff --git a/src/sum.js b/src/sum.js
new file mode 100644
index 000000000..fc898956a
--- /dev/null
+++ b/src/sum.js
@@ -0,0 +1,3 @@
+export function sum(list){
+ return list.reduce((acc, cur) => acc + cur, 0)
+}
diff --git a/src/switcher.js b/src/switcher.js
new file mode 100644
index 000000000..a4aa23b8e
--- /dev/null
+++ b/src/switcher.js
@@ -0,0 +1,78 @@
+import { equals } from './equals.js'
+
+const NO_MATCH_FOUND = Symbol ? Symbol('NO_MATCH_FOUND') : undefined
+
+const getMatchingKeyValuePair = (
+ cases, testValue, defaultValue
+) => {
+ let iterationValue
+
+ for (let index = 0; index < cases.length; index++){
+ iterationValue = cases[ index ].test(testValue)
+
+ if (iterationValue !== NO_MATCH_FOUND){
+ return iterationValue
+ }
+ }
+
+ return defaultValue
+}
+
+const isEqual = (testValue, matchValue) => {
+ const willReturn =
+ typeof testValue === 'function' ?
+ testValue(matchValue) :
+ equals(testValue)(matchValue)
+
+ return willReturn
+}
+
+const is = (testValue, matchResult = true) => ({
+ key : testValue,
+ test : matchValue =>
+ isEqual(testValue, matchValue) ? matchResult : NO_MATCH_FOUND,
+})
+
+class Switchem{
+ constructor(
+ defaultValue, cases, willMatch
+ ){
+ if (cases === undefined && willMatch === undefined){
+ this.cases = []
+ this.defaultValue = undefined
+ this.willMatch = defaultValue
+ } else {
+ this.cases = cases
+ this.defaultValue = defaultValue
+ this.willMatch = willMatch
+ }
+
+ return this
+ }
+
+ default(defaultValue){
+ const holder = new Switchem(
+ defaultValue, this.cases, this.willMatch
+ )
+
+ return holder.match(this.willMatch)
+ }
+
+ is(testValue, matchResult){
+ return new Switchem(
+ this.defaultValue,
+ [ ...this.cases, is(testValue, matchResult) ],
+ this.willMatch
+ )
+ }
+
+ match(matchValue){
+ return getMatchingKeyValuePair(
+ this.cases, matchValue, this.defaultValue
+ )
+ }
+}
+
+export function switcher(input){
+ return new Switchem(input)
+}