From d02c241701ba9abaf263c6e1672df18a251d1e1f Mon Sep 17 00:00:00 2001 From: Dejan Toteff Date: Thu, 16 Jan 2025 18:08:46 +0200 Subject: [PATCH] ready --- .github/README.md | 47 +- CHANGELOG.md | 19 +- README.md | 27908 +++++++++++--------------------------- docs/README.md | 47 +- files/index.d.ts | 11 +- immutable.d.ts | 10 +- index.d.ts | 10 +- source/take-spec.ts | 4 +- source/takeLast-spec.ts | 10 +- 9 files changed, 8050 insertions(+), 20016 deletions(-) diff --git a/.github/README.md b/.github/README.md index c4cc80a0a..03c4c918a 100644 --- a/.github/README.md +++ b/.github/README.md @@ -15391,7 +15391,7 @@ describe('R.tail', () => { ```typescript -take(howMany: number, input: T[]): T[] +take(howMany: number, input: T): T extends string ? string : T ``` It returns the first `howMany` elements of `input`. @@ -15413,9 +15413,8 @@ const result = [ All TypeScript definitions ```typescript -take(howMany: number, input: T[]): T[]; -take(howMany: number, input: string): string; -take(howMany: number) : (input: T[]) => T[]; +take(howMany: number, input: T): T extends string ? string : T; +take(howMany: number) : (input: T) => T extends string ? string : T; ``` @@ -15491,7 +15490,7 @@ describe('R.take - array', () => { result // $ExpectType number[] }) it('curried', () => { - const result = take(howMany)(list) + const result = take(howMany)(list) result // $ExpectType number[] }) @@ -15504,7 +15503,7 @@ describe('R.take - string', () => { result // $ExpectType string }) it('curried', () => { - const result = take(howMany)(str) + const result = take(howMany)(str) result // $ExpectType string }) @@ -15519,7 +15518,7 @@ describe('R.take - string', () => { ```typescript -takeLast(howMany: number, input: T[]): T[] +takeLast(howMany: number, input: T): T extends string ? string : T ``` It returns the last `howMany` elements of `input`. @@ -15541,9 +15540,8 @@ const result = [ All TypeScript definitions ```typescript -takeLast(howMany: number, input: T[]): T[]; -takeLast(howMany: number, input: string): string; -takeLast(howMany: number) : (input: T[]) => T[]; +takeLast(howMany: number, input: T): T extends string ? string : T; +takeLast(howMany: number) : (input: T) => T extends string ? string : T; ``` @@ -15627,18 +15625,18 @@ describe('R.takeLast - array', () => { result // $ExpectType number[] }) it('curried', () => { - const result = takeLast(howMany)(list) + const result = takeLast(howMany)(list) result // $ExpectType number[] }) it('real case', () => { - let data = ['foo'] - let result = piped( + const data = ['foo', 'bar', 'baz', 'qux'] + const result = piped( data, filter( x => x.length >= 100 ), - takeLast(5), + takeLast(2), ) result // $ExpectType string[] }) @@ -15651,7 +15649,7 @@ describe('R.takeLast - string', () => { result // $ExpectType string }) it('curried', () => { - const result = takeLast(howMany)(str) + const result = takeLast(howMany)(str) result // $ExpectType string }) @@ -18558,12 +18556,27 @@ describe('R.zipWith', () => { ## ❯ CHANGELOG -9.4.2 +9.4.2 -- Fix bug with `R.differenceWith` when two arrays has same length - [Issue #750](https://github.com/selfrefactor/rambda/issues/757) +- Fix TS issue when `R.take` is used as part of `R.pipe`. + +Moving away from `Ramda` types which are problematic in this case: + +```typescript +const data = ['foo', 'bar', 'baz', 'qux'] +const result = piped( + data, + filter( + x => x.length >= 2 + ), + takeLast(2), +) +``` 9.4.1 +- Fix bug with `R.differenceWith` when two arrays has same length - [Issue #750](https://github.com/selfrefactor/rambda/issues/757) + - Allow path input to not be transformed when string numbers are there - [Issue #750](https://github.com/selfrefactor/rambda/issues/750) 9.4.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eda17ee5..d4987c36f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,24 @@ -9.4.2 +9.4.2 -- Fix bug with `R.differenceWith` when two arrays has same length - [Issue #750](https://github.com/selfrefactor/rambda/issues/757) +- Fix TS issue when `R.take` is used as part of `R.pipe`. + +Moving away from `Ramda` types which are problematic in this case: + +```typescript +const data = ['foo', 'bar', 'baz', 'qux'] +const result = piped( + data, + filter( + x => x.length >= 2 + ), + takeLast(2), +) +``` 9.4.1 +- Fix bug with `R.differenceWith` when two arrays has same length - [Issue #750](https://github.com/selfrefactor/rambda/issues/757) + - Allow path input to not be transformed when string numbers are there - [Issue #750](https://github.com/selfrefactor/rambda/issues/750) 9.4.0 diff --git a/README.md b/README.md index 31ec05e3e..ad6403e33 100644 --- a/README.md +++ b/README.md @@ -314,369 +314,22 @@ method | Rambda | Ramda | Lodash ### add -```typescript - -add(a: number, b: number): number -``` - It adds `a` and `b`. -> :boom: It doesn't work with strings, as the inputs are parsed to numbers before calculation. - -```javascript -R.add(2, 3) // => 5 -``` - Try this R.add example in Rambda REPL -
- -All TypeScript definitions - -```typescript -add(a: number, b: number): number; -add(a: number): (b: number) => number; -``` - -
- -
- -R.add source - -```javascript -export function add(a, b){ - if (arguments.length === 1) return _b => add(a, _b) - - return Number(a) + Number(b) -} -``` - -
- -
- -Tests - -```javascript -import { add as addRamda } from 'ramda' - -import { compareCombinations } from './_internals/testUtils.js' -import { add } from './add.js' - -test('with number', () => { - expect(add(2, 3)).toBe(5) - expect(add(7)(10)).toBe(17) -}) - -test('string is bad input', () => { - expect(add('foo', 'bar')).toBeNaN() -}) - -test('ramda specs', () => { - expect(add('1', '2')).toBe(3) - expect(add(1, '2')).toBe(3) - expect(add(true, false)).toBe(1) - expect(add(null, null)).toBe(0) - expect(add(undefined, undefined)).toBeNaN() - expect(add(new Date(1), new Date(2))).toBe(3) -}) - -const possibleInputs = [ - /foo/, - 'foo', - true, - 3, - NaN, - 4, - [], - Promise.resolve(1), -] - -describe('brute force', () => { - compareCombinations({ - fn : add, - fnRamda : addRamda, - firstInput : possibleInputs, - secondInput : possibleInputs, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 0, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 0, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 64, - } - `) - }, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {add} from 'rambda' - -describe('R.add', () => { - it('happy', () => { - const result = add(4, 1) - - result // $ExpectType number - }) - it('curried', () => { - const result = add(4)(1) - - result // $ExpectType number - }) -}) -``` - -
- [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#add) ### addIndex -```typescript - -addIndex(originalFn: any): (fn: any) => (list: any[]) => any[] -``` - -```javascript -const result = R.addIndex(R.map)((val, idx) => val + idx + 1, [1, 2, 3]) -// => [2, 4, 6] -``` - Try this R.addIndex example in Rambda REPL -
- -All TypeScript definitions - -```typescript -addIndex(originalFn: any): (fn: any) => (list: any[]) => any[]; -addIndex(originalFn: any): (fn: any, list: any[]) => any[]; -``` - -
- -
- -R.addIndex source - -```javascript -import { _concat } from './_internals/utils.js' -import { curryN } from './curryN.js' - -export function addIndex( - originalFunction, - initialIndexFn = () => 0, - loopIndexChange = x => x + 1 -){ - return curryN(originalFunction.length, function (){ - const origFn = arguments[ 0 ] - const list = arguments[ arguments.length - 1 ] - let idx = initialIndexFn(list.length) - const args = Array.prototype.slice.call(arguments, 0) - args[ 0 ] = function (){ - const result = origFn.apply(this, _concat(arguments, [ idx, list ])) - idx = loopIndexChange(idx) - - return result - } - - return originalFunction.apply(this, args) - }) -} -``` - -
- -
- -Tests - -```javascript -import * as R from 'ramda' - -import { addIndex } from './addIndex.js' -import { map } from './map.js' - -test('with R.pipe', () => { - const result = R.pipe(R.addIndex(R.map)((x, i) => x + i))([ 1, 2, 3 ]) - expect(result).toEqual([ 1, 3, 5 ]) -}) - -test('happy', () => { - function mapFn(fn, list){ - const willReturn = [] - list.forEach(item => { - willReturn.push(fn(item)) - }) - - return willReturn - } - const mapIndexed = addIndex(mapFn) - const fn2 = (val, idx) => val + idx + 5 - const result = mapIndexed(fn2, [ 1, 2, 3 ]) - expect(result).toEqual([ 6, 8, 10 ]) -}) - -describe('unary functions like `map`', () => { - const times2 = function (x){ - return x * 2 - } - const addIndexParam = function (x, idx){ - return x + idx - } - const squareEnds = function ( - x, idx, list - ){ - return idx === 0 || idx === list.length - 1 ? x * x : x - } - const mapIndexed = addIndex(map) - - it('works just like a normal map', () => { - expect(mapIndexed(times2, [ 1, 2, 3, 4 ])).toEqual([ 2, 4, 6, 8 ]) - }) - - it('passes the index as a second parameter to the callback', () => { - expect(mapIndexed(addIndexParam, [ 8, 6, 7, 5, 3, 0, 9 ])).toEqual([ - 8, 7, 9, 8, 7, 5, 15, - ]) - }) - - it('passes the entire list as a third parameter to the callback', () => { - expect(mapIndexed(squareEnds, [ 8, 6, 7, 5, 3, 0, 9 ])).toEqual([ - 64, 6, 7, 5, 3, 0, 81, - ]) - }) - - it('acts as a curried function', () => { - const makeSquareEnds = mapIndexed(squareEnds) - expect(makeSquareEnds([ 8, 6, 7, 5, 3, 0, 9 ])).toEqual([ - 64, 6, 7, 5, 3, 0, 81, - ]) - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {addIndex, map, pipe} from 'rambda' - -describe('R.addIndex', () => { - it('happy', () => { - function mapFn(fn: (x: T) => T, list: T[]) { - const willReturn: T[] = [] - list.forEach(item => { - willReturn.push(fn(item)) - }) - - return willReturn - } - const mapIndexed = addIndex(mapFn) - const fn = (val: number, idx: number, list: number[]) => - val + idx + 5 + list[0] - const result = mapIndexed(fn)([1, 2, 3]) - result // $ExpectType any[] - }) - it('with pipe', () => { - const result = pipe( - addIndex(map)((x: number, i: number) => { - return x + i - }) - )([1, 2, 3]) - result // $ExpectType any[] - }) -}) -``` - -
- [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#addIndex) ### addIndexRight -```typescript - -addIndexRight(originalFn: any): (fn: any) => (list: any[]) => any[] -``` - Same as `R.addIndex`, but it will passed indexes are decreasing, instead of increasing. -
- -All TypeScript definitions - -```typescript -addIndexRight(originalFn: any): (fn: any) => (list: any[]) => any[]; -addIndexRight(originalFn: any): (fn: any, list: any[]) => any[]; -``` - -
- -
- -R.addIndexRight source - -```javascript -import { addIndex } from './addIndex.js' - -export function addIndexRight(originalFunction){ - return addIndex( - originalFunction, - listLength => listLength - 1, - x => x - 1 - ) -} -``` - -
- -
- -Tests - -```javascript -import { addIndexRight } from './addIndexRight.js' -import { map } from './map.js' - -test('happy', () => { - function mapFn(fn, list){ - const willReturn = [] - list.forEach(item => { - willReturn.push(fn(item)) - }) - - return willReturn - } - const mapIndexed = addIndexRight(mapFn) - const fn2 = (val, idx) => val + idx + 5 - expect(mapIndexed(fn2, [ 1, 2, 3 ])).toEqual([ 8, 8, 8 ]) - const revmap = (fn, ary) => map(fn, ary) - const revmapIndexed = addIndexRight(revmap) - const result = revmapIndexed((val, idx) => idx + '-' + val, - [ 'f', 'o', 'o', 'b', 'a', 'r' ]) - - expect(result).toEqual([ '5-f', '4-o', '3-o', '2-b', '1-a', '0-r' ]) -}) -``` - -
- [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#addIndexRight) ### adjust @@ -688,14 +341,6 @@ adjust(index: number, replaceFn: (x: T) => T, list: T[]): T[] It replaces `index` in array `list` with the result of `replaceFn(list[i])`. -```javascript -const result = R.adjust( - 0, - a => a + 1, - [0, 100] -) // => [1, 100] -``` - Try this R.adjust example in Rambda REPL
@@ -796,14 +441,6 @@ all(predicate: (x: T) => boolean, list: T[]): boolean It returns `true`, if all members of array `list` returns `true`, when applied as argument to `predicate` function. -```javascript -const list = [ 0, 1, 2, 3, 4 ] -const predicate = x => x > -1 - -const result = R.all(predicate, list) -// => true -``` - Try this R.all example in Rambda REPL
@@ -900,18 +537,6 @@ allPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean It returns `true`, if all functions of `predicates` return `true`, when `input` is their argument. -```javascript -const input = { - a : 1, - b : 2, -} -const predicates = [ - x => x.a === 1, - x => x.b === 2, -] -const result = R.allPass(predicates)(input) // => true -``` - Try this R.allPass example in Rambda REPL
@@ -1042,204 +667,45 @@ describe('allPass', () => { ### always -```typescript +It returns function that always returns `x`. -always(x: T): (...args: unknown[]) => T -``` +Try this R.always example in Rambda REPL -It returns function that always returns `x`. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#always) -```javascript -const fn = R.always(7) +### and -const result = fn() -// => 7 -``` +Logical AND -Try this R.always example in Rambda REPL +Try this R.and example in Rambda REPL -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#and) -All TypeScript definitions +### any ```typescript -always(x: T): (...args: unknown[]) => T; + +any(predicate: (x: T) => boolean, list: T[]): boolean ``` -
+It returns `true`, if at least one member of `list` returns true, when passed to a `predicate` function. + +Try this R.any example in Rambda REPL
-R.always source +All TypeScript definitions -```javascript -export function always(x){ - return _ => x -} +```typescript +any(predicate: (x: T) => boolean, list: T[]): boolean; +any(predicate: (x: T) => boolean): (list: T[]) => boolean; ```
-Tests - -```javascript -import { always } from './always.js' -import { applySpec } from './applySpec.js' - -test('happy', () => { - const fn = always(7) - - expect(fn()).toBe(7) - expect(fn()).toBe(7) -}) - -test('compatibility with applySpec', () => { - const spec = applySpec({ x : always('foo') }) - expect(spec({})).toEqual({ x : 'foo' }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {always} from 'rambda' - -describe('R.always', () => { - it('happy', () => { - const fn = always('foo') - fn // $ExpectType (...args: unknown[]) => string - const result = fn() - result // $ExpectType string - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#always) - -### and - -```typescript - -and(x: T, y: U): T | U -``` - -Logical AND - -```javascript -R.and(true, true); // => true -R.and(false, true); // => false -R.and(true, 'foo'); // => 'foo' -``` - -Try this R.and example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -and(x: T, y: U): T | U; -and(x: T): (y: U) => T | U; -``` - -
- -
- -R.and source - -```javascript -export function and(a, b){ - if (arguments.length === 1) return _b => and(a, _b) - - return a && b -} -``` - -
- -
- -Tests - -```javascript -import { and } from './and.js' - -test('happy', () => { - expect(and(1, 'foo')).toBe('foo') - expect(and(true, true)).toBeTrue() - expect(and(true)(true)).toBeTrue() - expect(and(true, false)).toBeFalse() - expect(and(false, true)).toBeFalse() - expect(and(false, false)).toBeFalse() -}) -``` - -
- -
- -TypeScript test - -```typescript -import {and} from 'rambda' - -describe('R.and', () => { - it('happy', () => { - const result = and(true, false) - result // $ExpectType boolean - }) - it('curried', () => { - const result = and('foo')(1) - result // $ExpectType string | 1 - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#and) - -### any - -```typescript - -any(predicate: (x: T) => boolean, list: T[]): boolean -``` - -It returns `true`, if at least one member of `list` returns true, when passed to a `predicate` function. - -```javascript -const list = [1, 2, 3] -const predicate = x => x * x > 8 -R.any(fn, list) -// => true -``` - -Try this R.any example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -any(predicate: (x: T) => boolean, list: T[]): boolean; -any(predicate: (x: T) => boolean): (list: T[]) => boolean; -``` - -
- -
- -R.any source +R.any source ```javascript export function any(predicate, list){ @@ -1321,19 +787,6 @@ anyPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean It accepts list of `predicates` and returns a function. This function with its `input` will return `true`, if any of `predicates` returns `true` for this `input`. -```javascript -const isBig = x => x > 20 -const isOdd = x => x % 2 === 1 -const input = 11 - -const fn = R.anyPass( - [isBig, isOdd] -) - -const result = fn(input) -// => true -``` - Try this R.anyPass example in Rambda REPL
@@ -1499,17 +952,6 @@ ap(fns: Array<(a: T) => U>[], vs: T[]): U[] It takes a list of functions and a list of values. Then it returns a list of values obtained by applying each function to each value. -```javascript -const result = R.ap( - [ - x => x + 1, - x => x + 2, - ], - [1, 2, 3] -) -// => [2, 3, 4, 3, 4, 5] -``` - Try this R.ap example in Rambda REPL
@@ -1572,11 +1014,6 @@ aperture(n: N, list: T[]): Array> | [] It returns a new list, composed of consecutive `n`-tuples from a `list`. -```javascript -const result = R.aperture(2, [1, 2, 3, 4]) -// => [[1, 2], [2, 3], [3, 4]] -``` - Try this R.aperture example in Rambda REPL
@@ -1657,13 +1094,6 @@ append(xToAppend: T, iterable: T[]): T[] It adds element `x` at the end of `iterable`. -```javascript -const x = 'foo' - -const result = R.append(x, ['bar', 'baz']) -// => ['bar', 'baz', 'foo'] -``` - Try this R.append example in Rambda REPL
@@ -1806,11 +1236,6 @@ It applies function `fn` to the list of arguments. This is useful for creating a fixed-arity function from a variadic function. `fn` should be a bound function if context is significant. -```javascript -const result = R.apply(Math.max, [42, -Infinity, 1337]) -// => 1337 -``` - Try this R.apply example in Rambda REPL
@@ -1907,17 +1332,6 @@ applySpec>( ) => { [Key in keyof Spec]: ReturnType } ``` -> :boom: The currying in this function works best with functions with 4 arguments or less. (arity of 4) - -```javascript -const fn = R.applySpec({ - sum: R.add, - nested: { mul: R.multiply } -}) -const result = fn(2, 4) -// => { sum: 6, nested: { mul: 8 } } -``` - Try this R.applySpec example in Rambda REPL
@@ -2351,118 +1765,98 @@ describe('applySpec', () => { ### applyTo -```typescript - -applyTo(el: T, fn: (t: T) => U): U -``` - -```javascript -const result = R.applyTo( - 1, - x => x + 1 -) -// => 2 -``` - Try this R.applyTo example in Rambda REPL -
- -All TypeScript definitions - -```typescript -applyTo(el: T, fn: (t: T) => U): U; -applyTo(el: T): (fn: (t: T) => U) => U; -``` - -
- -
- -R.applyTo source - -```javascript -export function applyTo(input, fn){ - if (arguments.length === 1){ - return _fn => applyTo(input, _fn) - } - - return fn(input) -} -``` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#applyTo) -
+### ascend -
+Try this R.ascend example in Rambda REPL -Tests +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#ascend) -```javascript -import { applyTo } from './applyTo.js' -import { multiply } from './multiply.js' +### assoc -test('happy', () => { - expect(applyTo(21, multiply(2))).toBe(42) -}) -``` +It makes a shallow clone of `obj` with setting or overriding the property `prop` with `newValue`. -
+Try this R.assoc example in Rambda REPL -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#applyTo) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#assoc) -### ascend +### assocPath ```typescript -ascend(fn: (obj: T) => Ord, a: T, b: T): Ordering +assocPath(path: Path, newValue: any, obj: object): Output ``` -```javascript -const result = R.sort(R.descend(x => x), [2, 1]) -// => [1, 2] -``` +It makes a shallow clone of `obj` with setting or overriding with `newValue` the property found with `path`. -Try this R.ascend example in Rambda REPL +Try this R.assocPath example in Rambda REPL
All TypeScript definitions ```typescript -ascend(fn: (obj: T) => Ord, a: T, b: T): Ordering; -ascend(fn: (obj: T) => Ord): (a: T, b: T) => Ordering; +assocPath(path: Path, newValue: any, obj: object): Output; +assocPath(path: Path, newValue: any): (obj: object) => Output; +assocPath(path: Path): (newValue: any) => (obj: object) => Output; ```
-R.ascend source +R.assocPath source ```javascript -export function createCompareFunction( - a, b, winner, loser +import { cloneList } from './_internals/cloneList.js' +import { createPath } from './_internals/createPath.js' +import { isArray } from './_internals/isArray.js' +import { isIndexInteger } from './_internals/isInteger.js' +import { assocFn } from './assoc.js' +import { curry } from './curry.js' + +export function assocPathFn( + path, newValue, input ){ - if (a === b) return 0 + const pathArrValue = createPath(path) + if (pathArrValue.length === 0) return newValue - return a < b ? winner : loser -} + const index = pathArrValue[ 0 ] + if (pathArrValue.length > 1){ + const condition = + typeof input !== 'object' || + input === null || + !input.hasOwnProperty(index) -export function ascend( - getFunction, a, b -){ - if (arguments.length === 1){ - return (_a, _b) => ascend( - getFunction, _a, _b + const nextInput = condition ? + isIndexInteger(pathArrValue[ 1 ]) ? + [] : + {} : + input[ index ] + + newValue = assocPathFn( + Array.prototype.slice.call(pathArrValue, 1), + newValue, + nextInput ) } - const aValue = getFunction(a) - const bValue = getFunction(b) - return createCompareFunction( - aValue, bValue, -1, 1 + if (isIndexInteger(index) && isArray(input)){ + const arr = cloneList(input) + arr[ index ] = newValue + + return arr + } + + return assocFn( + index, newValue, input ) } + +export const assocPath = curry(assocPathFn) ```
@@ -2472,144 +1866,71 @@ export function ascend( Tests ```javascript -import { ascend } from './ascend.js' -import { descend } from './descend.js' -import { sort } from './sort.js' - -const people = [ - { - name : 'Emma', - age : 70, - }, - { - name : 'Peter', - age : 78, - }, - { - name : 'Mikhail', - age : 62, - }, -] +import { assocPathFn } from './assocPath.js' -test('ascend', () => { - const result = sort(ascend(x => x?.age), - people) - const expected = [ - { - name : 'Mikhail', - age : 62, - }, - { - name : 'Emma', - age : 70, +test('happy', () => { + const path = 'a.c.1' + const input = { + a : { + b : 1, + c : [ 1, 2 ], }, - { - name : 'Peter', - age : 78, + } + assocPathFn( + path, 3, input + ) + expect(input).toEqual({ + a : { + b : 1, + c : [ 1, 2 ], }, - ] - expect(result).toEqual(expected) + }) }) -test('descend', () => { - const result = sort(descend(x => x?.age), - people) - const expected = [ - { - name : 'Peter', - age : 78, - }, - { - name : 'Emma', - age : 70, - }, - { - name : 'Mikhail', - age : 62, - }, - ] +test('string can be used as path input', () => { + const testObj = { + a : [ { b : 1 }, { b : 2 } ], + d : 3, + } + const result1 = assocPathFn( + [ 'a', 0, 'b' ], 10, testObj + ) + const result2 = assocPathFn( + 'a.0.b', 10, testObj + ) - expect(result).toEqual(expected) + const expected = { + a : [ { b : 10 }, { b : 2 } ], + d : 3, + } + expect(result1).toEqual(expected) + expect(result2).toEqual(expected) }) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#ascend) - -### assoc - -```typescript - -assoc(prop: K): { - (val: T): >(obj: U) => U -``` - -It makes a shallow clone of `obj` with setting or overriding the property `prop` with `newValue`. - -> :boom: This copies and flattens prototype properties -onto the new object as well. All non-primitive properties are copied by -reference. - -```javascript -R.assoc('c', 3, {a: 1, b: 2}) -// => {a: 1, b: 2, c: 3} -``` - -Try this R.assoc example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -assoc(prop: K): { - (val: T): >(obj: U) => U; - , T>(val: T, obj: U): U; -}; -assoc(prop: K, val: T): { - (obj: U): U extends Record ? U[K] extends T ? U : Record & Omit : U & Record; -}; -assoc(prop: K, val: T, obj: U): U; -``` - -
- -
- -R.assoc source - -```javascript -import { curry } from './curry.js' -export function assocFn( - prop, newValue, obj -){ - return Object.assign( - {}, obj, { [ prop ] : newValue } +test('difference with ramda - doesn\'t overwrite primitive values with keys in the path', () => { + const obj = { a : 'str' } + const result = assocPathFn( + [ 'a', 'b' ], 42, obj ) -} - -export const assoc = curry(assocFn) -``` - -
- -
- -Tests -```javascript -import { assoc } from './assoc.js' + expect(result).toEqual({ + a : { + 0 : 's', + 1 : 't', + 2 : 'r', + b : 42, + }, + }) +}) test('adds a key to an empty object', () => { - expect(assoc( - 'a', 1, {} + expect(assocPathFn( + [ 'a' ], 1, {} )).toEqual({ a : 1 }) }) test('adds a key to a non-empty object', () => { - expect(assoc( + expect(assocPathFn( 'b', 2, { a : 1 } )).toEqual({ a : 1, @@ -2617,64 +1938,96 @@ test('adds a key to a non-empty object', () => { }) }) -test('adds a key to a non-empty object - curry case 1', () => { - expect(assoc('b', 2)({ a : 1 })).toEqual({ +test('adds a nested key to a non-empty object', () => { + expect(assocPathFn( + 'b.c', 2, { a : 1 } + )).toEqual({ a : 1, - b : 2, + b : { c : 2 }, }) }) -test('adds a key to a non-empty object - curry case 2', () => { - expect(assoc('b')(2, { a : 1 })).toEqual({ +test('adds a nested key to a nested non-empty object', () => { + expect(assocPathFn('b.d', + 3,{ a : 1, - b : 2, + b : { c : 2 }, + })).toEqual({ + a : 1, + b : { + c : 2, + d : 3, + }, }) }) -test('adds a key to a non-empty object - curry case 3', () => { - const result = assoc('b')(2)({ a : 1 }) - - expect(result).toEqual({ +test('adds a key to a non-empty object', () => { + expect(assocPathFn('b', 2, { a : 1 })).toEqual({ a : 1, b : 2, }) }) +test('adds a nested key to a non-empty object', () => { + expect(assocPathFn('b.c', 2, { a : 1 })).toEqual({ + a : 1, + b : { c : 2 }, + }) +}) + test('changes an existing key', () => { - expect(assoc( + expect(assocPathFn( 'a', 2, { a : 1 } )).toEqual({ a : 2 }) }) test('undefined is considered an empty object', () => { - expect(assoc( + expect(assocPathFn( 'a', 1, undefined )).toEqual({ a : 1 }) }) test('null is considered an empty object', () => { - expect(assoc( + expect(assocPathFn( 'a', 1, null )).toEqual({ a : 1 }) }) test('value can be null', () => { - expect(assoc( + expect(assocPathFn( 'a', null, null )).toEqual({ a : null }) }) test('value can be undefined', () => { - expect(assoc( + expect(assocPathFn( 'a', undefined, null )).toEqual({ a : undefined }) }) test('assignment is shallow', () => { - expect(assoc( + expect(assocPathFn( 'a', { b : 2 }, { a : { c : 3 } } )).toEqual({ a : { b : 2 } }) }) + +test('empty array as path', () => { + const result = assocPathFn( + [], 3, { + a : 1, + b : 2, + } + ) + expect(result).toBe(3) +}) + +test('happy', () => { + const expected = { foo : { bar : { baz : 42 } } } + const result = assocPathFn( + [ 'foo', 'bar', 'baz' ], 42, { foo : null } + ) + expect(result).toEqual(expected) +}) ```
@@ -2684,9952 +2037,181 @@ test('assignment is shallow', () => { TypeScript test ```typescript -import { assoc } from 'rambda' +import {assocPath} from 'rambda' -type Obj = { - str: string - num: number +interface Output { + a: number, + foo: {bar: number}, } -const obj: Obj = { str: 'foo', num: 1 } -const newValue = 2 -const newProp = 'num' - -describe('R.assoc', () => { - it('happy', () => { - const result = assoc(newProp, newValue, obj) - - result.num // $ExpectType number - result.str // $ExpectType string - }) - it('curried 1', () => { - const result = assoc(newProp, newValue)(obj) +describe('R.assocPath - user must explicitly set type of output', () => { + it('with array as path input', () => { + const result = assocPath(['foo', 'bar'], 2, {a: 1}) - result.num // $ExpectType number - result.str // $ExpectType string + result // $ExpectType Output }) - it('curried 2', () => { - const result = assoc(newProp)(newValue)(obj) + it('with string as path input', () => { + const result = assocPath('foo.bar', 2, {a: 1}) - result.num // $ExpectType number - result.str // $ExpectType string + result // $ExpectType Output }) - it('from @types/ramda', () => { - // @ts-expect-error - assoc('str')(2, obj) - // @ts-expect-error - assoc('what')('bar', obj) - - const result1 = assoc('what')(2, {} as Record) - result1.what // $ExpectType number - - const result2 = assoc('str')('bar')(obj) - result2.str // $ExpectType string - result2.num // $ExpectType number - - // @ts-expect-error - assoc('str')(2)(obj) - // @ts-expect-error - assoc('what')('foo')(obj) - - const result3 = assoc('what')(2)({} as Record) - result3.what // $ExpectType number - - const result4 = assoc('str', 'bar')(obj) - result4.str // $ExpectType string - result4.num // $ExpectType number - - assoc('str', 2)(obj) - assoc('what', 'bar')(obj) - - const result5 = assoc('str', 2)({} as Record) - result5.str // $ExpectType number - - const result6 = assoc('str', 'bar', obj) - result6.str // $ExpectType string - result6.num // $ExpectType number +}) - // @ts-expect-error - assoc('str', 2, obj) - // @ts-expect-error +describe('R.assocPath - curried', () => { + it('with array as path input', () => { + const result = assocPath(['foo', 'bar'], 2)({a: 1}) - assoc('what', 'bar', obj) + result // $ExpectType Output + }) + it('with string as path input', () => { + const result = assocPath('foo.bar', 2)({a: 1}) - const result7 = assoc('str', 2, {} as Record) - result7.str // $ExpectType number + result // $ExpectType Output }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#assoc) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#assocPath) -### assocPath +### binary -```typescript +Try this R.binary example in Rambda REPL -assocPath(path: Path, newValue: any, obj: object): Output -``` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#binary) -It makes a shallow clone of `obj` with setting or overriding with `newValue` the property found with `path`. +### bind -```javascript -const path = 'b.c' -const newValue = 2 -const obj = { a: 1 } +```typescript -const result = R.assocPath(path, newValue, obj) -// => { a : 1, b : { c : 2 }} +bind(fn: F, thisObj: T): (...args: Parameters) => ReturnType ``` -Try this R.assocPath example in Rambda REPL +Creates a function that is bound to a context. + +Try this R.bind example in Rambda REPL
All TypeScript definitions ```typescript -assocPath(path: Path, newValue: any, obj: object): Output; -assocPath(path: Path, newValue: any): (obj: object) => Output; -assocPath(path: Path): (newValue: any) => (obj: object) => Output; +bind(fn: F, thisObj: T): (...args: Parameters) => ReturnType; +bind(fn: F): (thisObj: T) => (...args: Parameters) => ReturnType; ```
-R.assocPath source +R.bind source ```javascript -import { cloneList } from './_internals/cloneList.js' -import { createPath } from './_internals/createPath.js' -import { isArray } from './_internals/isArray.js' -import { isIndexInteger } from './_internals/isInteger.js' -import { assocFn } from './assoc.js' -import { curry } from './curry.js' +import { curryN } from './curryN.js' -export function assocPathFn( - path, newValue, input -){ - const pathArrValue = createPath(path) - if (pathArrValue.length === 0) return newValue +export function bind(fn, thisObj){ + if (arguments.length === 1){ + return _thisObj => bind(fn, _thisObj) + } - const index = pathArrValue[ 0 ] - if (pathArrValue.length > 1){ - const condition = - typeof input !== 'object' || - input === null || - !input.hasOwnProperty(index) - - const nextInput = condition ? - isIndexInteger(pathArrValue[ 1 ]) ? - [] : - {} : - input[ index ] - - newValue = assocPathFn( - Array.prototype.slice.call(pathArrValue, 1), - newValue, - nextInput - ) - } - - if (isIndexInteger(index) && isArray(input)){ - const arr = cloneList(input) - arr[ index ] = newValue - - return arr - } - - return assocFn( - index, newValue, input - ) -} - -export const assocPath = curry(assocPathFn) -``` - -
- -
- -Tests - -```javascript -import { assocPathFn } from './assocPath.js' - -test('happy', () => { - const path = 'a.c.1' - const input = { - a : { - b : 1, - c : [ 1, 2 ], - }, - } - assocPathFn( - path, 3, input - ) - expect(input).toEqual({ - a : { - b : 1, - c : [ 1, 2 ], - }, - }) -}) - -test('string can be used as path input', () => { - const testObj = { - a : [ { b : 1 }, { b : 2 } ], - d : 3, - } - const result1 = assocPathFn( - [ 'a', 0, 'b' ], 10, testObj - ) - const result2 = assocPathFn( - 'a.0.b', 10, testObj - ) - - const expected = { - a : [ { b : 10 }, { b : 2 } ], - d : 3, - } - expect(result1).toEqual(expected) - expect(result2).toEqual(expected) -}) - -test('difference with ramda - doesn\'t overwrite primitive values with keys in the path', () => { - const obj = { a : 'str' } - const result = assocPathFn( - [ 'a', 'b' ], 42, obj - ) - - expect(result).toEqual({ - a : { - 0 : 's', - 1 : 't', - 2 : 'r', - b : 42, - }, - }) -}) - -test('adds a key to an empty object', () => { - expect(assocPathFn( - [ 'a' ], 1, {} - )).toEqual({ a : 1 }) -}) - -test('adds a key to a non-empty object', () => { - expect(assocPathFn( - 'b', 2, { a : 1 } - )).toEqual({ - a : 1, - b : 2, - }) -}) - -test('adds a nested key to a non-empty object', () => { - expect(assocPathFn( - 'b.c', 2, { a : 1 } - )).toEqual({ - a : 1, - b : { c : 2 }, - }) -}) - -test('adds a nested key to a nested non-empty object', () => { - expect(assocPathFn('b.d', - 3,{ - a : 1, - b : { c : 2 }, - })).toEqual({ - a : 1, - b : { - c : 2, - d : 3, - }, - }) -}) - -test('adds a key to a non-empty object', () => { - expect(assocPathFn('b', 2, { a : 1 })).toEqual({ - a : 1, - b : 2, - }) -}) - -test('adds a nested key to a non-empty object', () => { - expect(assocPathFn('b.c', 2, { a : 1 })).toEqual({ - a : 1, - b : { c : 2 }, - }) -}) - -test('changes an existing key', () => { - expect(assocPathFn( - 'a', 2, { a : 1 } - )).toEqual({ a : 2 }) -}) - -test('undefined is considered an empty object', () => { - expect(assocPathFn( - 'a', 1, undefined - )).toEqual({ a : 1 }) -}) - -test('null is considered an empty object', () => { - expect(assocPathFn( - 'a', 1, null - )).toEqual({ a : 1 }) -}) - -test('value can be null', () => { - expect(assocPathFn( - 'a', null, null - )).toEqual({ a : null }) -}) - -test('value can be undefined', () => { - expect(assocPathFn( - 'a', undefined, null - )).toEqual({ a : undefined }) -}) - -test('assignment is shallow', () => { - expect(assocPathFn( - 'a', { b : 2 }, { a : { c : 3 } } - )).toEqual({ a : { b : 2 } }) -}) - -test('empty array as path', () => { - const result = assocPathFn( - [], 3, { - a : 1, - b : 2, - } - ) - expect(result).toBe(3) -}) - -test('happy', () => { - const expected = { foo : { bar : { baz : 42 } } } - const result = assocPathFn( - [ 'foo', 'bar', 'baz' ], 42, { foo : null } - ) - expect(result).toEqual(expected) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {assocPath} from 'rambda' - -interface Output { - a: number, - foo: {bar: number}, -} - -describe('R.assocPath - user must explicitly set type of output', () => { - it('with array as path input', () => { - const result = assocPath(['foo', 'bar'], 2, {a: 1}) - - result // $ExpectType Output - }) - it('with string as path input', () => { - const result = assocPath('foo.bar', 2, {a: 1}) - - result // $ExpectType Output - }) -}) - -describe('R.assocPath - curried', () => { - it('with array as path input', () => { - const result = assocPath(['foo', 'bar'], 2)({a: 1}) - - result // $ExpectType Output - }) - it('with string as path input', () => { - const result = assocPath('foo.bar', 2)({a: 1}) - - result // $ExpectType Output - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#assocPath) - -### binary - -```typescript - -binary any>(fn: T): (...args: any[]) => ReturnType -``` - -```javascript -const result = R.binary( - (a, b, c) => a + b + c, -)(1, 2, 3, 4) -// => 3 -``` - -Try this R.binary example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -binary any>(fn: T): (...args: any[]) => ReturnType; -``` - -
- -
- -R.binary source - -```javascript -export function binary(fn){ - if (fn.length <= 2) return fn - - return (a, b) => fn(a, b) -} -``` - -
- -
- -Tests - -```javascript -import { binary } from './binary.js' - -test('happy', () => { - const result = binary(function ( - x, y, z - ){ - expect(arguments).toHaveLength(2) - expect(z).toBeUndefined() - expect(x).toBe(10) - expect(y).toBe(20) - - return x + y - })( - 10, 20, 30 - ) - expect(result).toBe(30) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {binary} from 'rambda' - -describe('R.binary', () => { - it('happy', () => { - const result = binary(function(x: number, y: number, z) { - expect(arguments.length).toBe(2) - expect(z).toBeUndefined() - expect(x).toBe(10) - expect(y).toBe(20) - return x + y - })(10, 20, 30) - result // $ExpectType number - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#binary) - -### bind - -```typescript - -bind(fn: F, thisObj: T): (...args: Parameters) => ReturnType -``` - -Creates a function that is bound to a context. - -> :boom: R.bind does not provide the additional argument-binding capabilities of `Function.prototype.bind`. - -```javascript -const log = R.bind(console.log, console) -const result = R.pipe( - R.assoc('a', 2), - R.tap(log), - R.assoc('a', 3) -)({a: 1}); -// => result - `{a: 3}` -// => console log - `{a: 2}` -``` - -Try this R.bind example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -bind(fn: F, thisObj: T): (...args: Parameters) => ReturnType; -bind(fn: F): (thisObj: T) => (...args: Parameters) => ReturnType; -``` - -
- -
- -R.bind source - -```javascript -import { curryN } from './curryN.js' - -export function bind(fn, thisObj){ - if (arguments.length === 1){ - return _thisObj => bind(fn, _thisObj) - } - - return curryN(fn.length, (...args) => fn.apply(thisObj, args)) -} -``` - -
- -
- -Tests - -```javascript -import { bind } from './bind.js' - -function Foo(x){ - this.x = x -} -function add(x){ - return this.x + x -} -function Bar(x, y){ - this.x = x - this.y = y -} -Bar.prototype = new Foo() -Bar.prototype.getX = function (){ - return 'prototype getX' -} - -test('returns a function', () => { - expect(typeof bind(add)(Foo)).toBe('function') -}) - -test('returns a function bound to the specified context object', () => { - const f = new Foo(12) - function isFoo(){ - return this instanceof Foo - } - const isFooBound = bind(isFoo, f) - expect(isFoo()).toBeFalse() - expect(isFooBound()).toBeTrue() -}) - -test('works with built-in types', () => { - const abc = bind(String.prototype.toLowerCase, 'ABCDEFG') - expect(typeof abc).toBe('function') - expect(abc()).toBe('abcdefg') -}) - -test('works with user-defined types', () => { - const f = new Foo(12) - function getX(){ - return this.x - } - const getXFooBound = bind(getX, f) - expect(getXFooBound()).toBe(12) -}) - -test('works with plain objects', () => { - const pojso = { x : 100 } - function incThis(){ - return this.x + 1 - } - const incPojso = bind(incThis, pojso) - expect(typeof incPojso).toBe('function') - expect(incPojso()).toBe(101) -}) - -test('does not interfere with existing object methods', () => { - const b = new Bar('a', 'b') - function getX(){ - return this.x - } - const getXBarBound = bind(getX, b) - expect(b.getX()).toBe('prototype getX') - expect(getXBarBound()).toBe('a') -}) - -test('preserves arity', () => { - const f0 = function (){ - return 0 - } - const f1 = function (a){ - return a - } - const f2 = function (a, b){ - return a + b - } - const f3 = function ( - a, b, c - ){ - return a + b + c - } - - expect(bind(f0, {})).toHaveLength(0) - expect(bind(f1, {})).toHaveLength(1) - expect(bind(f2, {})).toHaveLength(2) - expect(bind(f3, {})).toHaveLength(3) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {bind} from 'rambda' - -class Foo {} -function isFoo(this: T): boolean { - return this instanceof Foo -} - -describe('R.bind', () => { - it('happy', () => { - const foo = new Foo() - const result = bind(isFoo, foo)() - - result // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#bind) - -### both - -```typescript - -both(pred1: Pred, pred2: Pred): Pred -``` - -It returns a function with `input` argument. - -This function will return `true`, if both `firstCondition` and `secondCondition` return `true` when `input` is passed as their argument. - -```javascript -const firstCondition = x => x > 10 -const secondCondition = x => x < 20 -const fn = R.both(firstCondition, secondCondition) - -const result = [fn(15), fn(30)] -// => [true, false] -``` - -Try this R.both example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -both(pred1: Pred, pred2: Pred): Pred; -both(pred1: Predicate, pred2: Predicate): Predicate; -both(pred1: Predicate): (pred2: Predicate) => Predicate; -both(pred1: Pred): (pred2: Pred) => Pred; -``` - -
- -
- -R.both source - -```javascript -export function both(f, g){ - if (arguments.length === 1) return _g => both(f, _g) - - return (...input) => f(...input) && g(...input) -} -``` - -
- -
- -Tests - -```javascript -import { both } from './both.js' - -const firstFn = val => val > 0 -const secondFn = val => val < 10 - -test('with curry', () => { - expect(both(firstFn)(secondFn)(17)).toBeFalse() -}) - -test('without curry', () => { - expect(both(firstFn, secondFn)(7)).toBeTrue() -}) - -test('with multiple inputs', () => { - const between = function ( - a, b, c - ){ - return a < b && b < c - } - const total20 = function ( - a, b, c - ){ - return a + b + c === 20 - } - const fn = both(between, total20) - expect(fn( - 5, 7, 8 - )).toBeTrue() -}) - -test('skip evaluation of the second expression', () => { - let effect = 'not evaluated' - const F = function (){ - return false - } - const Z = function (){ - effect = 'Z got evaluated' - } - both(F, Z)() - - expect(effect).toBe('not evaluated') -}) -``` - -
- -
- -TypeScript test - -```typescript -import {both} from 'rambda' - -describe('R.both', () => { - it('with passed type', () => { - const fn = both( - x => x > 1, - x => x % 2 === 0 - ) - fn // $ExpectType Predicate - const result = fn(2) // $ExpectType boolean - result // $ExpectType boolean - }) - it('with passed type - curried', () => { - const fn = both(x => x > 1)(x => x % 2 === 0) - fn // $ExpectType Predicate - const result = fn(2) - result // $ExpectType boolean - }) - it('no type passed', () => { - const fn = both( - x => { - x // $ExpectType any - return x > 1 - }, - x => { - x // $ExpectType any - return x % 2 === 0 - } - ) - const result = fn(2) - result // $ExpectType boolean - }) - it('no type passed - curried', () => { - const fn = both((x: number) => { - x // $ExpectType number - return x > 1 - })((x: number) => { - x // $ExpectType number - return x % 2 === 0 - }) - const result = fn(2) - result // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#both) - -### call - -```typescript - -call any>(fn: T, ...args: Parameters): ReturnType -``` - -```javascript -const result = R.call( - (a, b) => a + b, - 1, - 2 -) -// => 3 -``` - -Try this R.call example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -call any>(fn: T, ...args: Parameters): ReturnType; -``` - -
- -
- -R.call source - -```javascript -export const call = (fn, ...inputs) => fn(...inputs) -``` - -
- -
- -Tests - -```javascript -import { bind } from './bind.js' -import { call } from './call.js' - -test('happy', () => { - expect(call( - Math.max, 1, 2, 3, -99, 42, 6, 7 - )).toBe(42) -}) - -test('accepts one or more arguments', () => { - const fn = function (){ - return arguments.length - } - expect(call(fn)).toBe(0) - expect(call(fn, 'x')).toBe(1) - expect(call( - fn, 'x', 'y' - )).toBe(2) - expect(call( - fn, 'x', 'y', 'z' - )).toBe(3) -}) - -test('provides no way to specify context', () => { - var obj = { - method (){ - return this === obj - }, - } - expect(call(obj.method)).toBe(false) - expect(call(bind(obj.method, obj))).toBe(true) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#call) - -### chain - -```typescript - -chain(fn: (n: T) => U[], list: T[]): U[] -``` - -The method is also known as `flatMap`. - -```javascript -const duplicate = n => [ n, n ] -const list = [ 1, 2, 3 ] - -const result = chain(duplicate, list) -// => [ 1, 1, 2, 2, 3, 3 ] -``` - -Try this R.chain example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -chain(fn: (n: T) => U[], list: T[]): U[]; -chain(fn: (n: T) => U[]): (list: T[]) => U[]; -``` - -
- -
- -R.chain source - -```javascript -export function chain(fn, list){ - if (arguments.length === 1){ - return _list => chain(fn, _list) - } - - return [].concat(...list.map(fn)) -} -``` - -
- -
- -Tests - -```javascript -import { chain as chainRamda } from 'ramda' - -import { chain } from './chain.js' - -const duplicate = n => [ n, n ] - -test('happy', () => { - const fn = x => [ x * 2 ] - const list = [ 1, 2, 3 ] - - const result = chain(fn, list) - - expect(result).toEqual([ 2, 4, 6 ]) -}) - -test('maps then flattens one level', () => { - expect(chain(duplicate, [ 1, 2, 3 ])).toEqual([ 1, 1, 2, 2, 3, 3 ]) -}) - -test('maps then flattens one level - curry', () => { - expect(chain(duplicate)([ 1, 2, 3 ])).toEqual([ 1, 1, 2, 2, 3, 3 ]) -}) - -test('flattens only one level', () => { - const nest = n => [ [ n ] ] - expect(chain(nest, [ 1, 2, 3 ])).toEqual([ [ 1 ], [ 2 ], [ 3 ] ]) -}) - -test('can compose', () => { - function dec(x){ - return [ x - 1 ] - } - function times2(x){ - return [ x * 2 ] - } - - const mdouble = chain(times2) - const mdec = chain(dec) - expect(mdec(mdouble([ 10, 20, 30 ]))).toEqual([ 19, 39, 59 ]) -}) - -test('@types/ramda broken test', () => { - const score = { - maths : 90, - physics : 80, - } - - const calculateTotal = score => { - const { maths, physics } = score - - return maths + physics - } - - const assocTotalToScore = (total, score) => ({ - ...score, - total, - }) - - const calculateAndAssocTotalToScore = chainRamda(assocTotalToScore, - calculateTotal) - expect(() => - calculateAndAssocTotalToScore(score)).toThrowErrorMatchingInlineSnapshot('"fn(...) is not a function"') -}) -``` - -
- -
- -TypeScript test - -```typescript -import {chain} from 'rambda' - -const list = [1, 2, 3] -const fn = (x: number) => [`${x}`, `${x}`] - -describe('R.chain', () => { - it('without passing type', () => { - const result = chain(fn, list) - result // $ExpectType string[] - - const curriedResult = chain(fn)(list) - curriedResult // $ExpectType string[] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#chain) - -### clamp - -```typescript - -clamp(min: number, max: number, input: number): number -``` - -Restrict a number `input` to be within `min` and `max` limits. - -If `input` is bigger than `max`, then the result is `max`. - -If `input` is smaller than `min`, then the result is `min`. - -```javascript -const result = [ - R.clamp(0, 10, 5), - R.clamp(0, 10, -1), - R.clamp(0, 10, 11) -] -// => [5, 0, 10] -``` - -Try this R.clamp example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -clamp(min: number, max: number, input: number): number; -clamp(min: number, max: number): (input: number) => number; -``` - -
- -
- -R.clamp source - -```javascript -import { curry } from './curry.js' - -function clampFn( - min, max, input -){ - if (min > max){ - throw new Error('min must not be greater than max in clamp(min, max, value)') - } - if (input >= min && input <= max) return input - - if (input > max) return max - if (input < min) return min -} - -export const clamp = curry(clampFn) -``` - -
- -
- -Tests - -```javascript -import { clamp } from './clamp.js' - -test('when min is greater than max', () => { - expect(() => clamp( - -5, -10, 5 - )).toThrowErrorMatchingInlineSnapshot('"min must not be greater than max in clamp(min, max, value)"') -}) - -test('rambda specs', () => { - expect(clamp( - 1, 10, 0 - )).toBe(1) - expect(clamp( - 3, 12, 1 - )).toBe(3) - expect(clamp( - -15, 3, -100 - )).toBe(-15) - expect(clamp( - 1, 10, 20 - )).toBe(10) - expect(clamp( - 3, 12, 23 - )).toBe(12) - expect(clamp( - -15, 3, 16 - )).toBe(3) - expect(clamp( - 1, 10, 4 - )).toBe(4) - expect(clamp( - 3, 12, 6 - )).toBe(6) - expect(clamp( - -15, 3, 0 - )).toBe(0) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {clamp} from 'rambda' - -describe('R.clamp', () => { - it('happy', () => { - const result = clamp(1, 10, 20) - result // $ExpectType number - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#clamp) - -### clone - -```typescript - -clone(input: T): T -``` - -It creates a deep copy of the `input`, which may contain (nested) Arrays and Objects, Numbers, Strings, Booleans and Dates. - -> :boom: It doesn't work with very specific types, such as MongoDB's ObjectId. - -```javascript -const objects = [{a: 1}, {b: 2}]; -const objectsClone = R.clone(objects); - -const result = [ - R.equals(objects, objectsClone), - R.equals(objects[0], objectsClone[0]), -] // => [ true, true ] -``` - -Try this R.clone example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -clone(input: T): T; -clone(input: T[]): T[]; -``` - -
- -
- -R.clone source - -```javascript -import { isArray } from './_internals/isArray.js' - -export function clone(input){ - const out = isArray(input) ? Array(input.length) : {} - if (input && input.getTime) return new Date(input.getTime()) - - for (const key in input){ - const v = input[ key ] - out[ key ] = - typeof v === 'object' && v !== null ? - v.getTime ? - new Date(v.getTime()) : - clone(v) : - v - } - - return out -} -``` - -
- -
- -Tests - -```javascript -import assert from 'assert' -import { clone as cloneRamda } from 'ramda' - -import { - compareCombinations, - EXTRA_BUILD_IN_OBJECTS, - FALSY_VALUES, -} from './_internals/testUtils.js' -import { clone } from './clone.js' -import { equals } from './equals.js' - -test('with array', () => { - const arr = [ - { - b : 2, - c : 'foo', - d : [ 1, 2, 3 ], - }, - 1, - new Date(), - null, - ] - expect(clone(arr)).toEqual(arr) -}) - -test('with object', () => { - const obj = { - a : 1, - b : 2, - c : 3, - d : [ 1, 2, 3 ], - e : new Date(), - } - expect(clone(obj)).toEqual(obj) -}) - -test('with date', () => { - const date = new Date( - 2014, 10, 14, 23, 59, 59, 999 - ) - - const cloned = clone(date) - assert.notStrictEqual(date, cloned) - expect(cloned).toEqual(new Date( - 2014, 10, 14, 23, 59, 59, 999 - )) - - expect(cloned.getDay()).toBe(5) -}) - -test('with R.equals', () => { - const objects = [ { a : 1 }, { b : 2 } ] - - const objectsClone = clone(objects) - - const result = [ - equals(objects, objectsClone), - equals(objects[ 0 ], objectsClone[ 0 ]), - ] - expect(result).toEqual([ true, true ]) -}) - -describe('brute force', () => { - const possibleInputs = [ ...FALSY_VALUES, ...EXTRA_BUILD_IN_OBJECTS ] - compareCombinations({ - fn : clone, - fnRamda : cloneRamda, - firstInput : possibleInputs, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 0, - "RESULTS_MISMATCH": 15, - "SHOULD_NOT_THROW": 0, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 15, - } - `) - }, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {clone} from 'rambda' - -describe('R.clone', () => { - it('happy', () => { - const obj = {a: 1, b: 2} - const result = clone(obj) - result // $ExpectType { a: number; b: number; } - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#clone) - -### collectBy - -```typescript - -collectBy(keyFn: (value: T) => K, list: T[]): T[][] -``` - -```javascript -const result = R.collectBy( - x => x % 2, - [1, 2, 3, 4] -) -// => [[2, 4], [1, 3]] -``` - -Try this R.collectBy example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -collectBy(keyFn: (value: T) => K, list: T[]): T[][]; -collectBy(keyFn: (value: T) => K): (list: T[]) => T[][]; -``` - -
- -
- -R.collectBy source - -```javascript -import { reduce } from './reduce.js' - -export function collectBy(fn, list){ - if (arguments.length === 1){ - return _list => collectBy(fn, _list) - } - - const group = reduce( - (o, x) => { - const tag = fn(x) - if (o[ tag ] === undefined){ - o[ tag ] = [] - } - o[ tag ].push(x) - - return o - }, - {}, - list - ) - const newList = [] - for (const tag in group){ - newList.push(group[ tag ]) - } - - return newList -} -``` - -
- -
- -Tests - -```javascript -import fc from 'fast-check' -import { - all, - compose, - difference, - equals, - head, - identity, - is, - isEmpty, - length, - uniq, - unnest, -} from 'rambdax' - -import { collectBy } from './collectBy.js' - -test('returns a list of lists', () => { - fc.assert(fc.property(fc.array(fc.nat()), xs => { - const check = all(is(Array)) - const ys = collectBy(identity)(xs) - - return check(ys) - })) -}) - -test('groups items but neither adds new ones nor removes any', () => { - fc.assert(fc.property(fc.array(fc.nat()), xs => { - const check = compose( - isEmpty, difference(xs), unnest - ) - const ys = collectBy(identity)(xs) - - return check(ys) - })) -}) - -test('groups related items together', () => { - fc.assert(fc.property(fc.array(fc.boolean()), xs => { - const ys = collectBy(identity)(xs) - const check = all(compose( - equals(1), length, uniq - )) - - return check(ys) - })) -}) - -test('invokes the tag function for each item in the list', () => { - fc.assert(fc.property(fc.array(fc.nat()), xs => { - const id = jest.fn(x => 42) - collectBy(id)(xs) - const check = compose(isEmpty, difference(xs)) - - return check(id.mock.calls.map(call => call[ 0 ])) - })) -}) - -test('groups items according to the tag value', () => { - fc.assert(fc.property(fc.array(fc.nat()), xs => { - const ys = collectBy(x => 42)(xs) - const check = compose( - isEmpty, difference(xs), head - ) - - return isEmpty(xs) && isEmpty(ys) ? true : check(ys) - })) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#collectBy) - -### comparator - -```typescript - -comparator(pred: (a: T, b: T) => boolean): (x: T, y: T) => Ordering -``` - -It returns a comparator function that can be used in `sort` method. - -```javascript -const result = R.sort( - R.comparator((a, b) => a.x < b.x), - [{x: 2}, {x: 1}] -) -// => [{x: 1}, {x: 2}] -``` - -Try this R.comparator example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -comparator(pred: (a: T, b: T) => boolean): (x: T, y: T) => Ordering; -``` - -
- -
- -R.comparator source - -```javascript -export function comparator(fn){ - return function (a, b){ - return fn(a, b) ? -1 : fn(b, a) ? 1 : 0 - } -} -``` - -
- -
- -Tests - -```javascript -import { comparator } from './comparator.js' - -test('happy', () => { - expect([ 3, 1, 8, 1, 2, 5 ].sort(comparator((a, b) => a < b))).toEqual([ - 1, 1, 2, 3, 5, 8, - ]) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#comparator) - -### complement - -```typescript - -complement(predicate: (...args: T) => unknown): (...args: T) => boolean -``` - -It returns `inverted` version of `origin` function that accept `input` as argument. - -The return value of `inverted` is the negative boolean value of `origin(input)`. - -```javascript -const origin = x => x > 5 -const inverted = complement(origin) - -const result = [ - origin(7), - inverted(7) -] => [ true, false ] -``` - -Try this R.complement example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -complement(predicate: (...args: T) => unknown): (...args: T) => boolean; -``` - -
- -
- -R.complement source - -```javascript -export function complement(fn){ - return (...input) => !fn(...input) -} -``` - -
- -
- -Tests - -```javascript -import { complement } from './complement.js' - -test('happy', () => { - const fn = complement(x => x.length === 0) - - expect(fn([ 1, 2, 3 ])).toBeTrue() -}) - -test('with multiple parameters', () => { - const between = function ( - a, b, c - ){ - return a < b && b < c - } - const f = complement(between) - expect(f( - 4, 5, 11 - )).toBeFalse() - expect(f( - 12, 2, 6 - )).toBeTrue() -}) -``` - -
- -
- -TypeScript test - -```typescript -import {complement, isNil} from 'rambda' - -describe('R.complement', () => { - it('happy', () => { - const fn = complement(isNil) - const result = fn(null) - result // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#complement) - -### compose - -```typescript - -compose( - ...func: [ - fnLast: (a: any) => TResult, - ...func: Array<(a: any) => any>, - f7: (a: R6) => R7, - f6: (a: R5) => R6, - f5: (a: R4) => R5, - f4: (a: R3) => R4, - f3: (a: R2) => R3, - f2: (a: R1) => R2, - f1: (...args: TArgs) => R1 - ] -): (...args: TArgs) => TResult -``` - -It performs right-to-left function composition. - -```javascript -const result = R.compose( - R.map(x => x * 2), - R.filter(x => x > 2) -)([1, 2, 3, 4]) - -// => [6, 8] -``` - -Try this R.compose example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -compose( - ...func: [ - fnLast: (a: any) => TResult, - ...func: Array<(a: any) => any>, - f7: (a: R6) => R7, - f6: (a: R5) => R6, - f5: (a: R4) => R5, - f4: (a: R3) => R4, - f3: (a: R2) => R3, - f2: (a: R1) => R2, - f1: (...args: TArgs) => R1 - ] -): (...args: TArgs) => TResult; -compose( - f7: (a: R6) => R7, - f6: (a: R5) => R6, - f5: (a: R4) => R5, - f4: (a: R3) => R4, - f3: (a: R2) => R3, - f2: (a: R1) => R2, - f1: (...args: TArgs) => R1 -): (...args: TArgs) => R7; -compose( - f7: (a: R6) => R7, - f6: (a: R5) => R6, - f5: (a: R4) => R5, - f4: (a: R3) => R4, - f3: (a: R2) => R3, - f2: (a: R1) => R2, - f1: (...args: TArgs) => R1 -): (...args: TArgs) => R7; -compose( - f6: (a: R5) => R6, - f5: (a: R4) => R5, - f4: (a: R3) => R4, - f3: (a: R2) => R3, - f2: (a: R1) => R2, - f1: (...args: TArgs) => R1 -): (...args: TArgs) => R6; -compose( - f5: (a: R4) => R5, - f4: (a: R3) => R4, - f3: (a: R2) => R3, - f2: (a: R1) => R2, - f1: (...args: TArgs) => R1 -): (...args: TArgs) => R5; -compose( - f4: (a: R3) => R4, - f3: (a: R2) => R3, - f2: (a: R1) => R2, - f1: (...args: TArgs) => R1 -): (...args: TArgs) => R4; -compose( - f3: (a: R2) => R3, - f2: (a: R1) => R2, - f1: (...args: TArgs) => R1 -): (...args: TArgs) => R3; -compose( - f2: (a: R1) => R2, - f1: (...args: TArgs) => R1 -): (...args: TArgs) => R2; -compose( - f1: (...args: TArgs) => R1 -): (...args: TArgs) => R1; -``` - -
- -
- -R.compose source - -```javascript -import { pipe } from './pipe.js' - -export function compose(){ - if (arguments.length === 0){ - throw new Error('compose requires at least one argument') - } - - return pipe.apply(this, Array.prototype.slice.call(arguments, 0).reverse()) -} -``` - -
- -
- -Tests - -```javascript -import { compose as composeRamda } from 'ramda' - -import { add } from './add.js' -import { compose } from './compose.js' -import { filter } from './filter.js' -import { last } from './last.js' -import { map } from './map.js' - -test('happy', () => { - const result = compose( - last, map(add(10)), map(add(1)) - )([ 1, 2, 3 ]) - - expect(result).toBe(14) -}) - -test('can accepts initially two arguments', () => { - const result = compose(map(x => x * 2), - (list, limit) => filter(x => x > limit, list))([ 1, 2, 3, 4, false ], 2) - - expect(result).toEqual([ 6, 8 ]) -}) - -test('when no arguments is passed', () => { - expect(() => compose()).toThrowErrorMatchingInlineSnapshot('"compose requires at least one argument"') -}) - -test('ramda spec', () => { - const f = function ( - a, b, c - ){ - return [ a, b, c ] - } - const g = compose(f) - - expect(g( - 1, 2, 3 - )).toEqual([ 1, 2, 3 ]) -}) - -test('does return correct length of composed function', () => { - expect(compose( - map, map, map - )).toHaveLength(2) - expect(composeRamda( - map, map, map - )).toHaveLength(2) -}) -``` - -
- -
- -TypeScript test - -```typescript -import { - add, - subtract, - compose, - map, - filter, - identity, - inc, - negate, - dissoc, -} from 'rambda' - -interface Input { - a: string, - b: string, -} -interface Output { - c: string, -} - -describe('R.compose with explicit types', () => { - it('with explicit types - complex', () => { - const obj = { - a: 'foo', - b: 'bar', - } - interface AfterInput { - a: number, - } - interface BeforeOutput { - b: string, - } - - const result = compose( - x => ({c: x.b + 'bar'}), - x => ({b: x.a + 'foo'}), - x => ({a: x.a.length + x.b.length}) - )(obj) - - result // $ExpectType Output - }) - it('with explicit types - correct', () => { - const obj = { - a: 'foo', - b: 'bar', - } - const result = compose(identity, input => { - input // $ExpectType Input - return input as unknown as Output - })(obj) - result // $ExpectType Output - }) - it('with explicit types - wrong', () => { - const obj: Input = { - a: 'foo', - b: 'bar', - } - - // @ts-expect-error - compose(identity, dissoc('b'))(obj) - }) -}) - -describe('R.compose', () => { - it('happy', () => { - const result = compose(subtract(11), add(1), add(1))(1) - result // $ExpectType number - }) - it('happy - more complex', () => { - const result = compose( - (x: number) => x + 1, - (x: string) => x.length + 1 - )('foo') - result // $ExpectType number - }) - - it('with R.filter', () => { - const result = compose( - filter(x => x > 2), - map(add(1)) - )([1, 2, 3]) - result // $ExpectType number[] - }) - - it('with native filter', () => { - const result = compose( - (list: number[]) => list.filter(x => x > 2), - (list: number[]) => { - list // $ExpectType number[] - return list - }, - map(add(1)) - )([1, 2, 3]) - - result // $ExpectType number[] - }) - - it('with void', () => { - const result = compose( - () => {}, - () => {} - )() - result // $ExpectType void - }) -}) - -describe('R.compose - @types/ramda tests', () => { - test('complex', () => { - const fn = compose( - inc, - inc, - inc, - inc, - inc, - inc, - inc, - inc, - negate, - Math.pow - ) - const result = fn(3, 4) - result // $ExpectType number - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#compose) - -### composeWith - -```typescript - -composeWith( - transformer: (fn: (...args: any[]) => any, intermediatResult: any) => any, - fns: AtLeastOneFunctionsFlowFromRightToLeft, -): (...args: TArgs) => TResult -``` - -```javascript -const result = R.composeWith( - (fn, intermediateResult) => fn(intermediateResult), - [ - R.map(x => x + 1), - R.map(x => x * 2), - ] -)([1, 2, 3]) -// => [3, 5, 7] -``` - -Try this R.composeWith example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -composeWith( - transformer: (fn: (...args: any[]) => any, intermediatResult: any) => any, - fns: AtLeastOneFunctionsFlowFromRightToLeft, -): (...args: TArgs) => TResult; -composeWith( - transformer: (fn: (...args: any[]) => any, intermediatResult: any) => any, -): ( - fns: AtLeastOneFunctionsFlowFromRightToLeft, -) => (...args: TArgs) => TResult; -``` - -
- -
- -R.composeWith source - -```javascript -import { _arity } from './_internals/_arity.js' -import { head } from './head.js' -import { identity } from './identity.js' -import { reduce } from './reduce.js' -import { reverse } from './reverse.js' -import { tail } from './tail.js' - -export function pipeWith(xf, list){ - if (list.length <= 0){ - return identity - } - - const headList = head(list) - const tailList = tail(list) - - return _arity(headList.length, function (){ - return reduce( - function (result, f){ - return xf.call( - this, f, result - ) - }, - headList.apply(this, arguments), - tailList - ) - }) -} - -export function composeWith(xf, list){ - if (arguments.length === 1) return _list => composeWith(xf, _list) - - return pipeWith.apply(this, [ xf, reverse(list) ]) -} -``` - -
- -
- -Tests - -```javascript -import { always, identity, inc, isNil, map, modulo, multiply } from 'rambdax' -import { composeWith as composeWithRamda, concat, flip, ifElse } from 'ramda' - -import { composeWith } from './composeWith.js' - -test('performs right-to-left function composition with function applying', () => { - const f = composeWith((f, res) => f(res))([ map, multiply, parseInt ]) - - expect(f).toHaveLength(2) - expect(f('10')([ 1, 2, 3 ])).toEqual([ 10, 20, 30 ]) - expect(f('10', 2)([ 1, 2, 3 ])).toEqual([ 2, 4, 6 ]) -}) - -test('performs right-to-left function while not nil result', () => { - const isOdd = flip(modulo)(2) - const composeWhenNotNil = composeWithRamda((f, res) => - isNil(res) ? null : f(res)) - - const f = composeWhenNotNil([ - inc, - ifElse( - isOdd, identity, always(null) - ), - parseInt, - ]) - expect(f).toHaveLength(2) - expect(f('1')).toBe(2) - expect(f('2')).toBeNull() -}) - -test('performs right-to-left function using promise chaining', () => { - const then = function (f, p){ - return p.then(f) - } - const composeP = composeWithRamda(then) - const toListPromise = function (a){ - return new Promise(res => { - res([ a ]) - }) - } - const doubleListPromise = function (a){ - return new Promise(res => { - res(concat(a, a)) - }) - } - const f = composeP([ doubleListPromise, toListPromise ]) - - return f(1).then(res => { - expect(res).toEqual([ 1, 1 ]) - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#composeWith) - -### concat - -```typescript - -concat(x: T[], y: T[]): T[] -``` - -It returns a new string or array, which is the result of merging `x` and `y`. - -```javascript -R.concat([1, 2])([3, 4]) // => [1, 2, 3, 4] -R.concat('foo', 'bar') // => 'foobar' -``` - -Try this R.concat example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -concat(x: T[], y: T[]): T[]; -concat(x: T[]): (y: T[]) => T[]; -concat(x: string, y: string): string; -concat(x: string): (y: string) => string; -``` - -
- -
- -R.concat source - -```javascript -export function concat(x, y){ - if (arguments.length === 1) return _y => concat(x, _y) - - return typeof x === 'string' ? `${ x }${ y }` : [ ...x, ...y ] -} -``` - -
- -
- -Tests - -```javascript -import { concat } from './concat.js' - -test('happy', () => { - const arr1 = [ 'a', 'b', 'c' ] - const arr2 = [ 'd', 'e', 'f' ] - - const a = concat(arr1, arr2) - const b = concat(arr1)(arr2) - const expectedResult = [ 'a', 'b', 'c', 'd', 'e', 'f' ] - - expect(a).toEqual(expectedResult) - expect(b).toEqual(expectedResult) -}) - -test('with strings', () => { - expect(concat('ABC', 'DEF')).toBe('ABCDEF') -}) -``` - -
- -
- -TypeScript test - -```typescript -import {concat} from 'rambda' - -const list1 = [1, 2, 3] -const list2 = [4, 5, 6] - -describe('R.concat', () => { - it('happy', () => { - const result = concat(list1, list2) - - result // $ExpectType number[] - }) - it('curried', () => { - const result = concat(list1)(list2) - - result // $ExpectType number[] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#concat) - -### cond - -```typescript - -cond(conditions: Array>): (...args: T) => R -``` - -It takes list with `conditions` and returns a new function `fn` that expects `input` as argument. - -This function will start evaluating the `conditions` in order to find the first winner(order of conditions matter). - -The winner is this condition, which left side returns `true` when `input` is its argument. Then the evaluation of the right side of the winner will be the final result. - -If no winner is found, then `fn` returns `undefined`. - -```javascript -const fn = R.cond([ - [ x => x > 25, R.always('more than 25') ], - [ x => x > 15, R.always('more than 15') ], - [ R.T, x => `${x} is nothing special` ], -]) - -const result = [ - fn(30), - fn(20), - fn(10), -] -// => ['more than 25', 'more than 15', '10 is nothing special'] -``` - -Try this R.cond example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -cond(conditions: Array>): (...args: T) => R; -``` - -
- -
- -R.cond source - -```javascript -export function cond(conditions){ - return (...input) => { - let done = false - let toReturn - conditions.forEach(([ predicate, getResult ]) => { - if (!done && predicate(...input)){ - done = true - toReturn = getResult(...input) - } - }) - - return toReturn - } -} -``` - -
- -
- -Tests - -```javascript -import { always } from './always.js' -import { cond } from './cond.js' -import { equals } from './equals.js' -import { T } from './T.js' - -test('returns a function', () => { - expect(typeof cond([])).toBe('function') -}) - -test('returns a conditional function', () => { - const fn = cond([ - [ equals(0), always('water freezes at 0°C') ], - [ equals(100), always('water boils at 100°C') ], - [ - T, - function (temp){ - return 'nothing special happens at ' + temp + '°C' - }, - ], - ]) - expect(fn(0)).toBe('water freezes at 0°C') - expect(fn(50)).toBe('nothing special happens at 50°C') - expect(fn(100)).toBe('water boils at 100°C') -}) - -test('no winner', () => { - const fn = cond([ - [ equals('foo'), always(1) ], - [ equals('bar'), always(2) ], - ]) - expect(fn('quux')).toBeUndefined() -}) - -test('predicates are tested in order', () => { - const fn = cond([ - [ T, always('foo') ], - [ T, always('bar') ], - [ T, always('baz') ], - ]) - expect(fn()).toBe('foo') -}) - -test('pass all inputs',() => { - cond([ [()=> true, (...x) => { - expect(x).toEqual([1,2,3]) - }] ])(1,2,3) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {cond, always, equals} from 'rambda' - -describe('R.cond', () => { - it('happy', () => { - const fn = cond([ - [equals(0), always('water freezes at 0°C')], - [equals(100), always('water boils at 100°C')], - [ - () => true, - function(temp) { - temp // $ExpectType number - return 'nothing special happens at ' + temp + '°C' - }, - ], - ]) - - const result = fn(0) - result // $ExpectType string - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#cond) - -### converge - -```typescript - -converge(after: ((...a: any[]) => any), fns: ((...x: any[]) => any)[]): (...y: any[]) => any -``` - -Accepts a converging function and a list of branching functions and returns a new function. When invoked, this new function is applied to some arguments, each branching function is applied to those same arguments. The results of each branching function are passed as arguments to the converging function to produce the return value. - -> :boom: Explanation is taken from `Ramda` documentation - -```javascript -const result = R.converge(R.multiply)([ R.add(1), R.add(3) ])(2) -// => 15 -``` - -Try this R.converge example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -converge(after: ((...a: any[]) => any), fns: ((...x: any[]) => any)[]): (...y: any[]) => any; -``` - -
- -
- -R.converge source - -```javascript -import { curryN } from './curryN.js' -import { map } from './map.js' -import { max } from './max.js' -import { reduce } from './reduce.js' - -export function converge(fn, transformers){ - if (arguments.length === 1) - return _transformers => converge(fn, _transformers) - - const highestArity = reduce( - (a, b) => max(a, b.length), 0, transformers - ) - - return curryN(highestArity, function (){ - return fn.apply(this, - map(g => g.apply(this, arguments), transformers)) - }) -} -``` - -
- -
- -Tests - -```javascript -import { add } from './add.js' -import { converge } from './converge.js' -import { multiply } from './multiply.js' - -const f1 = converge(multiply, [ a => a + 1, a => a + 10 ]) -const f2 = converge(multiply, [ a => a + 1, (a, b) => a + b + 10 ]) -const f3 = converge(multiply, [ a => a + 1, ( - a, b, c -) => a + b + c + 10 ]) - -test('happy', () => { - expect(f2(6, 7)).toBe(161) -}) - -test('passes the results of applying the arguments individually', () => { - const result = converge(multiply)([ add(1), add(3) ])(2) - expect(result).toBe(15) -}) - -test('returns a function with the length of the longest argument', () => { - expect(f1).toHaveLength(1) - expect(f2).toHaveLength(2) - expect(f3).toHaveLength(3) -}) - -test('passes context to its functions', () => { - const a = function (x){ - return this.f1(x) - } - const b = function (x){ - return this.f2(x) - } - const c = function (x, y){ - return this.f3(x, y) - } - const d = converge(c, [ a, b ]) - const context = { - f1 : add(1), - f2 : add(2), - f3 : add, - } - expect(a.call(context, 1)).toBe(2) - expect(b.call(context, 1)).toBe(3) - expect(d.call(context, 1)).toBe(5) -}) - -test('works with empty functions list', () => { - const fn = converge(function (){ - return arguments.length - }, []) - expect(fn).toHaveLength(0) - expect(fn()).toBe(0) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {converge} from 'rambda' - -const mult = (a: number, b: number) => { - return a * b -} -const fn = converge(mult, [ - (a: number) => { - return a - }, - (a: number, b: number) => { - return b - }, -]) - -describe('R.converge', () => { - it('happy', () => { - const result = fn(2, 3) - const curriedResult = fn(2)(3) - - result // $ExpectType any - curriedResult // $ExpectType any - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#converge) - -### count - -```typescript - -count(predicate: (x: T) => boolean, list: T[]): number -``` - -It counts how many times `predicate` function returns `true`, when supplied with iteration of `list`. - -```javascript -const list = [{a: 1}, 1, {a:2}] -const result = R.count(x => x.a !== undefined, list) -// => 2 -``` - -Try this R.count example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -count(predicate: (x: T) => boolean, list: T[]): number; -count(predicate: (x: T) => boolean): (list: T[]) => number; -``` - -
- -
- -R.count source - -```javascript -import { isArray } from './_internals/isArray.js' - -export function count(predicate, list){ - if (arguments.length === 1){ - return _list => count(predicate, _list) - } - if (!isArray(list)) return 0 - - return list.filter(x => predicate(x)).length -} -``` - -
- -
- -Tests - -```javascript -import { count as countRamda } from 'ramda' - -import { count } from './count.js' - -const predicate = x => x.a !== undefined - -test('with empty list', () => { - expect(count(predicate, [])).toBe(0) -}) - -test('happy', () => { - const list = [ 1, 2, { a : 1 }, 3, { a : 1 } ] - - expect(count(predicate)(list)).toBe(2) -}) - -test('rambdax/issues/86', () => { - const arr = [ true, false, true, false ] - expect(count(Boolean, arr)).toBe(countRamda(Boolean, arr)) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {count} from 'rambda' - -const list = [1, 2, 3] -const predicate = (x: number) => x > 1 - -describe('R.count', () => { - it('happy', () => { - const result = count(predicate, list) - - result // $ExpectType number - }) - it('curried', () => { - const result = count(predicate)(list) - - result // $ExpectType number - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#count) - -### countBy - -```typescript - -countBy(transformFn: (x: T) => any, list: T[]): Record -``` - -It counts elements in a list after each instance of the input list is passed through `transformFn` function. - -```javascript -const list = [ 'a', 'A', 'b', 'B', 'c', 'C' ] - -const result = countBy(R.toLower, list) -const expected = { a: 2, b: 2, c: 2 } -// => `result` is equal to `expected` -``` - -Try this R.countBy example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -countBy(transformFn: (x: T) => any, list: T[]): Record; -countBy(transformFn: (x: T) => any): (list: T[]) => Record; -``` - -
- -
- -R.countBy source - -```javascript -export function countBy(fn, list){ - if (arguments.length === 1){ - return _list => countBy(fn, _list) - } - const willReturn = {} - - list.forEach(item => { - const key = fn(item) - if (!willReturn[ key ]){ - willReturn[ key ] = 1 - } else { - willReturn[ key ]++ - } - }) - - return willReturn -} -``` - -
- -
- -Tests - -```javascript -import { countBy } from './countBy.js' - -const list = [ 'a', 'A', 'b', 'B', 'c', 'C' ] - -test('happy', () => { - const result = countBy(x => x.toLowerCase(), list) - expect(result).toEqual({ - a : 2, - b : 2, - c : 2, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {countBy} from 'rambda' - -const transformFn = (x: string) => x.toLowerCase() -const list = ['a', 'A', 'b', 'B', 'c', 'C'] - -describe('R.countBy', () => { - it('happy', () => { - const result = countBy(transformFn, list) - - result // $ExpectType Record - }) - it('curried', () => { - const result = countBy(transformFn)(list) - - result // $ExpectType Record - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#countBy) - -### curry - -```typescript - -curry(fn: AnyFunction): (...a: any[]) => any -``` - -It expects a function as input and returns its curried version. - -```javascript -const fn = (a, b, c) => a + b + c -const curried = R.curry(fn) -const sum = curried(1,2) - -const result = sum(3) // => 6 -``` - -Try this R.curry example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -curry(fn: AnyFunction): (...a: any[]) => any; -``` - -
- -
- -R.curry source - -```javascript -export function curry(fn, args = []){ - return (..._args) => - (rest => rest.length >= fn.length ? fn(...rest) : curry(fn, rest))([ - ...args, - ..._args, - ]) -} -``` - -
- -
- -Tests - -```javascript -import { curry } from './curry.js' - -test('happy', () => { - const addFourNumbers = ( - a, b, c, d - ) => a + b + c + d - const curriedAddFourNumbers = curry(addFourNumbers) - const f = curriedAddFourNumbers(1, 2) - const g = f(3) - - expect(g(4)).toBe(10) -}) - -test('when called with more arguments', () => { - const add = curry((n, n2) => n + n2) - - expect(add( - 1, 2, 3 - )).toBe(3) -}) - -test('when called with zero arguments', () => { - const sub = curry((a, b) => a - b) - const s0 = sub() - - expect(s0(5, 2)).toBe(3) -}) - -test('when called via multiple curry stages', () => { - const join = curry(( - a, b, c, d - ) => [ a, b, c, d ].join('-')) - - const stage1 = join('A') - const stage2 = stage1('B', 'C') - - expect(stage2('D')).toBe('A-B-C-D') -}) -``` - -
- -
- -TypeScript test - -```typescript -import {curry} from 'rambda' - -function source(a: number, b: number, c: number, d: number) { - void d - - return a * b * c -} - -describe('R.curry', () => { - it('happy', () => { - const curried = curry(source) - - const result1 = curried(1)(2)(3) - const result2 = curried(1, 2)(3) - const result3 = curried(1)(2, 3) - const result4 = curried(1, 2, 3) - - result1 // $ExpectType any - result2 // $ExpectType any - result3 // $ExpectType any - result4 // $ExpectType any - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#curry) - -### curryN - -```typescript - -curryN(length: number, fn: AnyFunction): (...a: any[]) => any -``` - -It returns a curried equivalent of the provided function, with the specified arity. - -
- -All TypeScript definitions - -```typescript -curryN(length: number, fn: AnyFunction): (...a: any[]) => any; -``` - -
- -
- -R.curryN source - -```javascript -import { _arity } from './_internals/_arity.js' - -function _curryN( - n, cache, fn -){ - return function (){ - let ci = 0 - let ai = 0 - const cl = cache.length - const al = arguments.length - const args = new Array(cl + al) - while (ci < cl){ - args[ ci ] = cache[ ci ] - ci++ - } - while (ai < al){ - args[ cl + ai ] = arguments[ ai ] - ai++ - } - const remaining = n - args.length - - return args.length >= n ? - fn.apply(this, args) : - _arity(remaining, _curryN( - n, args, fn - )) - } -} - -export function curryN(n, fn){ - if (arguments.length === 1) return _fn => curryN(n, _fn) - - if (n > 10){ - throw new Error('First argument to _arity must be a non-negative integer no greater than ten') - } - - return _arity(n, _curryN( - n, [], fn - )) -} -``` - -
- -
- -Tests - -```javascript -import { curryN } from './curryN.js' - -function multiply( - a, b, c, d, e, f, g, h, i, j, k, l -){ - if (l){ - return a * b * c * d * e * f * g * h * i * j * k * l - } - if (k){ - return a * b * c * d * e * f * g * h * i * j * k - } - if (j){ - return a * b * c * d * e * f * g * h * i * j - } - if (i){ - return a * b * c * d * e * f * g * h * i - } - if (h){ - return a * b * c * d * e * f * g * h - } - if (g){ - return a * b * c * d * e * f * g - } - if (f){ - return a * b * c * d * e * f - } - if (e){ - return a * b * c * d * e - } - - return a * b * c -} - -test('accepts an arity', () => { - const curried = curryN(3, multiply) - expect(curried(1)(2)(3)).toBe(6) - expect(curried(1, 2)(3)).toBe(6) - expect(curried(1)(2, 3)).toBe(6) - expect(curried( - 1, 2, 3 - )).toBe(6) -}) - -test('can be partially applied', () => { - const curry3 = curryN(3) - const curried = curry3(multiply) - expect(curried).toHaveLength(3) - expect(curried(1)(2)(3)).toBe(6) - expect(curried(1, 2)(3)).toBe(6) - expect(curried(1)(2, 3)).toBe(6) - expect(curried( - 1, 2, 3 - )).toBe(6) -}) - -test('preserves context', () => { - const ctx = { x : 10 } - const f = function (a, b){ - return a + b * this.x - } - const g = curryN(2, f) - - expect(g.call( - ctx, 2, 4 - )).toBe(42) - expect(g.call(ctx, 2).call(ctx, 4)).toBe(42) -}) - -test('number of arguments is 4', () => { - const fn = curryN(4, multiply) - expect(fn( - 1, 2, 3, 4 - )).toBe(6) -}) - -test('number of arguments is 5', () => { - const fn = curryN(5, multiply) - expect(fn( - 1, 2, 3, 4, 5 - )).toBe(120) -}) - -test('number of arguments is 6', () => { - const fn = curryN(6, multiply) - expect(fn( - 1, 2, 3, 4, 5, 6 - )).toBe(720) -}) - -test('number of arguments is 7', () => { - const fn = curryN(7, multiply) - expect(fn( - 1, 2, 3, 4, 5, 6, 7 - )).toBe(5040) -}) - -test('number of arguments is 8', () => { - const fn = curryN(8, multiply) - expect(fn( - 1, 2, 3, 4, 5, 6, 7, 8 - )).toBe(40320) -}) - -test('number of arguments is 9', () => { - const fn = curryN(9, multiply) - expect(fn( - 1, 2, 3, 4, 5, 6, 7, 8, 9 - )).toBe(362880) -}) - -test('number of arguments is 10', () => { - const fn = curryN(10, multiply) - expect(fn( - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - )).toBe(3628800) -}) - -test('number of arguments is 11', () => { - expect(() => { - const fn = curryN(11, multiply) - fn( - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - ) - }).toThrowWithMessage(Error, - 'First argument to _arity must be a non-negative integer no greater than ten') -}) - -test('forwards extra arguments', () => { - const createArray = function (){ - return Array.prototype.slice.call(arguments) - } - const fn = curryN(3, createArray) - - expect(fn( - 1, 2, 3 - )).toEqual([ 1, 2, 3 ]) - expect(fn( - 1, 2, 3, 4 - )).toEqual([ 1, 2, 3, 4 ]) - expect(fn(1, 2)(3, 4)).toEqual([ 1, 2, 3, 4 ]) - expect(fn(1)( - 2, 3, 4 - )).toEqual([ 1, 2, 3, 4 ]) - expect(fn(1)(2)(3, 4)).toEqual([ 1, 2, 3, 4 ]) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {curryN} from 'rambda' - -function source(a: number, b: number, c: number, d: number) { - void d - - return a * b * c -} - -describe('R.curryN', () => { - it('happy', () => { - const curried = curryN(3, source) - - const result1 = curried(1)(2)(3) - const result2 = curried(1, 2)(3) - const result3 = curried(1)(2, 3) - const result4 = curried(1, 2, 3) - - result1 // $ExpectType any - result2 // $ExpectType any - result3 // $ExpectType any - result4 // $ExpectType any - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#curryN) - -### dec - -```typescript - -dec(x: number): number -``` - -It decrements a number. - -```javascript -const result = R.dec(2) // => 1 -``` - -Try this R.dec example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -dec(x: number): number; -``` - -
- -
- -R.dec source - -```javascript -export const dec = x => x - 1 -``` - -
- -
- -Tests - -```javascript -import { dec } from './dec.js' - -test('happy', () => { - expect(dec(2)).toBe(1) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dec) - -### defaultTo - -```typescript - -defaultTo(defaultValue: T, input: T | null | undefined): T -``` - -It returns `defaultValue`, if all of `inputArguments` are `undefined`, `null` or `NaN`. - -Else, it returns the first truthy `inputArguments` instance(from left to right). - -> :boom: Rambda's **defaultTo** accept indefinite number of arguments when non curried, i.e. `R.defaultTo(2, foo, bar, baz)`. - -```javascript -R.defaultTo('foo', 'bar') // => 'bar' -R.defaultTo('foo', undefined) // => 'foo' - -// Important - emtpy string is not falsy value(same as Ramda) -R.defaultTo('foo', '') // => 'foo' -``` - -Try this R.defaultTo example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -defaultTo(defaultValue: T, input: T | null | undefined): T; -defaultTo(defaultValue: T): (input: T | null | undefined) => T; -``` - -
- -
- -R.defaultTo source - -```javascript -function isFalsy(input){ - return ( - input === undefined || input === null || Number.isNaN(input) === true - ) -} - -export function defaultTo(defaultArgument, input){ - if (arguments.length === 1){ - return _input => defaultTo(defaultArgument, _input) - } - - return isFalsy(input) ? defaultArgument : input -} -``` - -
- -
- -Tests - -```javascript -import { defaultTo } from './defaultTo.js' - -test('with undefined', () => { - expect(defaultTo('foo')(undefined)).toBe('foo') -}) - -test('with null', () => { - expect(defaultTo('foo')(null)).toBe('foo') -}) - -test('with NaN', () => { - expect(defaultTo('foo')(NaN)).toBe('foo') -}) - -test('with empty string', () => { - expect(defaultTo('foo', '')).toBe('') -}) - -test('with false', () => { - expect(defaultTo('foo', false)).toBeFalse() -}) - -test('when inputArgument passes initial check', () => { - expect(defaultTo('foo', 'bar')).toBe('bar') -}) -``` - -
- -
- -TypeScript test - -```typescript -import {defaultTo} from 'rambda' - -describe('R.defaultTo with Ramda spec', () => { - it('happy', () => { - const result = defaultTo('foo', '') - result // $ExpectType "" | "foo" - }) - it('with explicit type', () => { - const result = defaultTo('foo', null) - result // $ExpectType string - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#defaultTo) - -### descend - -```typescript - -descend(fn: (obj: T) => Ord, a: T, b: T): Ordering -``` - -```javascript -const result = R.sort(R.descend(x => x), [1, 2]) -// => [2, 1] -``` - -Try this R.descend example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -descend(fn: (obj: T) => Ord, a: T, b: T): Ordering; -descend(fn: (obj: T) => Ord): (a: T, b: T) => Ordering; -``` - -
- -
- -R.descend source - -```javascript -import { createCompareFunction } from './ascend.js' - -export function descend( - getFunction, a, b -){ - if (arguments.length === 1){ - return (_a, _b) => descend( - getFunction, _a, _b - ) - } - const aValue = getFunction(a) - const bValue = getFunction(b) - - return createCompareFunction( - aValue, bValue, 1, -1 - ) -} -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#descend) - -### difference - -```typescript - -difference(a: T[], b: T[]): T[] -``` - -It returns the uniq set of all elements in the first list `a` not contained in the second list `b`. - -`R.equals` is used to determine equality. - -```javascript -const a = [ 1, 2, 3, 4 ] -const b = [ 3, 4, 5, 6 ] - -const result = R.difference(a, b) -// => [ 1, 2 ] -``` - -Try this R.difference example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -difference(a: T[], b: T[]): T[]; -difference(a: T[]): (b: T[]) => T[]; -``` - -
- -
- -R.difference source - -```javascript -import { includes } from './includes.js' -import { uniq } from './uniq.js' - -export function difference(a, b){ - if (arguments.length === 1) return _b => difference(a, _b) - - return uniq(a).filter(aInstance => !includes(aInstance, b)) -} -``` - -
- -
- -Tests - -```javascript -import { difference as differenceRamda } from 'ramda' - -import { difference } from './difference.js' - -test('difference', () => { - const a = [ 1, 2, 3, 4 ] - const b = [ 3, 4, 5, 6 ] - expect(difference(a)(b)).toEqual([ 1, 2 ]) - - expect(difference([], [])).toEqual([]) -}) - -test('difference with objects', () => { - const a = [ { id : 1 }, { id : 2 }, { id : 3 }, { id : 4 } ] - const b = [ { id : 3 }, { id : 4 }, { id : 5 }, { id : 6 } ] - expect(difference(a, b)).toEqual([ { id : 1 }, { id : 2 } ]) -}) - -test('no duplicates in first list', () => { - const M2 = [ 1, 2, 3, 4, 1, 2, 3, 4 ] - const N2 = [ 3, 3, 4, 4, 5, 5, 6, 6 ] - expect(difference(M2, N2)).toEqual([ 1, 2 ]) -}) - -test('should use R.equals', () => { - expect(difference([ 1 ], [ 1 ])).toHaveLength(0) - expect(differenceRamda([ NaN ], [ NaN ])).toHaveLength(0) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {difference} from 'rambda' - -const list1 = [1, 2, 3] -const list2 = [1, 2, 4] - -describe('R.difference', () => { - it('happy', () => { - const result = difference(list1, list2) - - result // $ExpectType number[] - }) - it('curried', () => { - const result = difference(list1)(list2) - - result // $ExpectType number[] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#difference) - -### differenceWith - -```typescript - -differenceWith( - pred: (a: T1, b: T2) => boolean, - list1: T1[], - list2: T2[], -): T1[] -``` - -```javascript -const result = R.differenceWith( - (a, b) => a.x === b.x, - [{x: 1}, {x: 2}], - [{x: 1}, {x: 3}] -) -// => [{x: 2}] -``` - -Try this R.differenceWith example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -differenceWith( - pred: (a: T1, b: T2) => boolean, - list1: T1[], - list2: T2[], -): T1[]; -differenceWith( - pred: (a: T1, b: T2) => boolean, -): (list1: T1[], list2: T2[]) => T1[]; -differenceWith( - pred: (a: T1, b: T2) => boolean, - list1: T1[], -): (list2: T2[]) => T1[]; -``` - -
- -
- -R.differenceWith source - -```javascript -import { curry } from './curry.js' -import { _indexOf } from './equals.js' - -export function differenceWithFn( - fn, a, b -){ - const willReturn = [] - const [ first, second ] = a.length >= b.length ? [ a, b ] : [ b, a ] - - first.forEach(item => { - const hasItem = second.some(secondItem => fn(item, secondItem)) - if (!hasItem && _indexOf(item, willReturn) === -1){ - willReturn.push(item) - } - }) - - return willReturn -} - -export const differenceWith = curry(differenceWithFn) -``` - -
- -
- -Tests - -```javascript -import { differenceWith } from './differenceWith.js'; - -const fn = (a, b) => a.x === b.x; - -test('same length of list', () => { - const result = differenceWith(fn, [{ x: 1 }, { x: 2 }], [{ x: 1 }, { x: 3 }]); - expect(result).toEqual([{ x: 2 }]); -}); - -test('different length of list', () => { - const foo = [{ x: 1 }, { x: 2 }, { x: 3 }]; - const bar = [{ x: 3 }, { x: 4 }]; - const result = differenceWith(fn, foo, bar); - expect(result).toEqual([{ x: 1 }, { x: 2 }]); -}); -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#differenceWith) - -### dissoc - -```typescript - -dissoc(prop: K): (obj: string extends keyof U ? U : undefined extends U[K] ? U : never) => U -``` - -It returns a new object that does not contain property `prop`. - -```javascript -R.dissoc('b', {a: 1, b: 2, c: 3}) -// => {a: 1, c: 3} -``` - -Try this R.dissoc example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -dissoc(prop: K): (obj: string extends keyof U ? U : undefined extends U[K] ? U : never) => U; -dissoc(prop: string extends keyof U ? K : undefined extends U[K] ? K : never, obj: U): U; -``` - -
- -
- -R.dissoc source - -```javascript -export function dissoc(prop, obj){ - if (arguments.length === 1) return _obj => dissoc(prop, _obj) - - if (obj === null || obj === undefined) return {} - - const willReturn = {} - for (const p in obj){ - willReturn[ p ] = obj[ p ] - } - delete willReturn[ prop ] - - return willReturn -} -``` - -
- -
- -Tests - -```javascript -import { dissoc } from './dissoc.js' - -test('input is null or undefined', () => { - expect(dissoc('b', null)).toEqual({}) - expect(dissoc('b', undefined)).toEqual({}) -}) - -test('property exists curried', () => { - expect(dissoc('b')({ - a : 1, - b : 2, - })).toEqual({ a : 1 }) -}) - -test('property doesn\'t exists', () => { - expect(dissoc('c', { - a : 1, - b : 2, - })).toEqual({ - a : 1, - b : 2, - }) -}) - -test('works with non-string property', () => { - expect(dissoc(42, { - a : 1, - 42 : 2, - })).toEqual({ a : 1 }) - - expect(dissoc(null, { - a : 1, - null : 2, - })).toEqual({ a : 1 }) - - expect(dissoc(undefined, { - a : 1, - undefined : 2, - })).toEqual({ a : 1 }) -}) - -test('includes prototype properties', () => { - function Rectangle(width, height){ - this.width = width - this.height = height - } - const area = Rectangle.prototype.area = function (){ - return this.width * this.height - } - const rect = new Rectangle(7, 6) - - expect(dissoc('area', rect)).toEqual({ - width : 7, - height : 6, - }) - - expect(dissoc('width', rect)).toEqual({ - height : 6, - area, - }) - - expect(dissoc('depth', rect)).toEqual({ - width : 7, - height : 6, - area, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import { dissoc } from 'rambda'; - -type Obj = { - str: string; - num: number; - opt?: boolean; - orUndefined: boolean | undefined; - orNull: boolean | null; -}; - -const obj: Obj = { str: 'foo', num: 1, orUndefined: true, orNull: true }; - -describe('R.dissoc', () => { - it('ramda tests', () => { - // @ts-expect-error - dissoc('str', obj); - // @ts-expect-error - dissoc('num', obj); - // @ts-expect-error - dissoc('orNull', obj); - - const result1 = dissoc('opt', obj); - result1; // $ExpectType Obj - // @ts-expect-error - dissoc('num')(obj); - const result2 = dissoc('orUndefined', obj); - result2; // $ExpectType Obj - const result3 = dissoc('opt')(obj); - result3; // $ExpectType Obj - }); -}); -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dissoc) - -### dissocPath - -```typescript - -dissocPath(path: Path, obj: any): T -``` - -```javascript -const result = R.dissocPath(['a', 'b'], {a: {b: 1, c: 2}}) -// => {a: {c: 2}} -``` - -Try this R.dissocPath example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -dissocPath(path: Path, obj: any): T; -dissocPath(path: Path): (obj: any) => T; -``` - -
- -
- -R.dissocPath source - -```javascript -import { createPath } from '../src/_internals/createPath.js' -import { isArray } from './_internals/isArray.js' -import { isIndexInteger } from './_internals/isInteger.js' -import { omit } from './omit.js' -import { path } from './path.js' -import { removeIndex } from './removeIndex.js' -import { update } from './update.js' - -export function dissocPath(pathInput, input){ - if (arguments.length === 1) return _obj => dissocPath(pathInput, _obj) - - const pathArrValue = createPath(pathInput) - // this {...input} spread could be done to satisfy ramda specs, but this is done on so many places - // TODO: add warning that Rambda simply returns input if path is empty - if (pathArrValue.length === 0) return input - - const pathResult = path(pathArrValue, input) - if (pathResult === undefined) return input - - const index = pathArrValue[ 0 ] - const condition = - typeof input !== 'object' || - input === null || - !input.hasOwnProperty(index) - if (pathArrValue.length > 1){ - const nextInput = condition ? - isIndexInteger(pathArrValue[ 1 ]) ? - [] : - {} : - input[ index ] - const nextPathInput = Array.prototype.slice.call(pathArrValue, 1) - const intermediateResult = dissocPath( - nextPathInput, nextInput, input - ) - if (isArray(input)) return update( - index, intermediateResult, input - ) - - return { - ...input, - [ index ] : intermediateResult, - } - } - if (isArray(input)) return removeIndex(index, input) - - return omit([ index ], input) -} -``` - -
- -
- -Tests - -```javascript -const assert = require('assert') -import { eq } from './_internals/testUtils.js' -import { dissocPath } from './dissocPath.js' - -const testInput = { - a : { - b : 1, - c : 2, - d : { e : 3 }, - }, - f : [ - { g : 4 }, - { - h : 5, - i : 6, - j : { - k : 7, - l : 8, - }, - }, - ], - m : 9, -} - -test('update array', () => { - const expected = { - a : { - b : 1, - c : 2, - d : { e : 3 }, - }, - f : [ - { g : 4 }, - { - h : 5, - j : { - k : 7, - l : 8, - }, - }, - ], - m : 9, - } - const result = dissocPath('f.1.i', testInput) - expect(result).toEqual(expected) -}) - -test('update object', () => { - const result = dissocPath('a.b', testInput) - const expected = { - a : { - c : 2, - d : { e : 3 }, - }, - f : [ - { g : 4 }, - { - h : 5, - i : 6, - j : { - k : 7, - l : 8, - }, - }, - ], - m : 9, - } - expect(result).toEqual(expected) -}) - -test('does not try to omit inner properties that do not exist', () => { - const obj1 = { - a : 1, - b : { - c : 2, - d : 3, - }, - e : 4, - f : 5, - } - const obj2 = dissocPath([ 'x', 0, 'z' ], obj1) - eq(obj2, { - a : 1, - b : { - c : 2, - d : 3, - }, - e : 4, - f : 5, - }) - // Note: reference equality below! - assert.strictEqual(obj2.a, obj1.a) - assert.strictEqual(obj2.b, obj1.b) - assert.strictEqual(obj2.f, obj1.f) -}) - -test('leaves an empty object when all properties omitted', () => { - const obj1 = { - a : 1, - b : { c : 2 }, - d : 3, - } - const obj2 = dissocPath([ 'b', 'c' ], obj1) - eq(obj2, { - a : 1, - b : {}, - d : 3, - }) -}) - -test('leaves an empty array when all indexes are omitted', () => { - const obj1 = { - a : 1, - b : [ 2 ], - d : 3, - } - const obj2 = dissocPath([ 'b', 0 ], obj1) - eq(obj2, { - a : 1, - b : [], - d : 3, - }) -}) - -test('accepts empty path', () => { - eq(dissocPath([], { - a : 1, - b : 2, - }), - { - a : 1, - b : 2, - }) -}) - -test('allow integer to be used as key for object', () => { - eq(dissocPath([ 42 ], { - 42 : 3, - a : 1, - b : 2, - }), - { - a : 1, - b : 2, - }) -}) - -test('support remove null/undefined value path', () => { - eq(dissocPath([ 'c', 'd' ], { - a : 1, - b : 2, - c : null, - }), - { - a : 1, - b : 2, - c : null, - }) - eq(dissocPath([ 'c', 'd' ], { - a : 1, - b : 2, - c : undefined, - }), - { - a : 1, - b : 2, - c : undefined, - }) - - const obj1 = { - a : 1, - b : 2, - } - const obj2 = dissocPath([ 'c', 'd' ], obj1) - - eq(obj2, obj1) - - // NOTE: commented out on purpose - // assert.notStrictEqual(obj2, obj1) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dissocPath) - -### divide - -```typescript - -divide(x: number, y: number): number -``` - -```javascript -R.divide(71, 100) // => 0.71 -``` - -Try this R.divide example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -divide(x: number, y: number): number; -divide(x: number): (y: number) => number; -``` - -
- -
- -R.divide source - -```javascript -export function divide(a, b){ - if (arguments.length === 1) return _b => divide(a, _b) - - return a / b -} -``` - -
- -
- -Tests - -```javascript -import { divide } from './divide.js' - -test('happy', () => { - expect(divide(71, 100)).toBe(0.71) - expect(divide(71)(100)).toBe(0.71) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#divide) - -### drop - -```typescript - -drop(howMany: number, input: T[]): T[] -``` - -It returns `howMany` items dropped from beginning of list or string `input`. - -```javascript -R.drop(2, ['foo', 'bar', 'baz']) // => ['baz'] -R.drop(2, 'foobar') // => 'obar' -``` - -Try this R.drop example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -drop(howMany: number, input: T[]): T[]; -drop(howMany: number, input: string): string; -drop(howMany: number): { - (input: T[]): T[]; - (input: string): string; -}; -``` - -
- -
- -R.drop source - -```javascript -export function drop(howManyToDrop, listOrString){ - if (arguments.length === 1) return _list => drop(howManyToDrop, _list) - - return listOrString.slice(howManyToDrop > 0 ? howManyToDrop : 0) -} -``` - -
- -
- -Tests - -```javascript -import assert from 'assert' - -import { drop } from './drop.js' - -test('with array', () => { - expect(drop(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'baz' ]) - expect(drop(3, [ 'foo', 'bar', 'baz' ])).toEqual([]) - expect(drop(4, [ 'foo', 'bar', 'baz' ])).toEqual([]) -}) - -test('with string', () => { - expect(drop(3, 'rambda')).toBe('bda') -}) - -test('with non-positive count', () => { - expect(drop(0, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) - expect(drop(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) - expect(drop(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) -}) - -test('should return copy', () => { - const xs = [ 1, 2, 3 ] - - assert.notStrictEqual(drop(0, xs), xs) - assert.notStrictEqual(drop(-1, xs), xs) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {drop} from 'rambda' - -const list = [1, 2, 3, 4] -const str = 'foobar' -const howMany = 2 - -describe('R.drop - array', () => { - it('happy', () => { - const result = drop(howMany, list) - result // $ExpectType number[] - }) - it('curried', () => { - const result = drop(howMany)(list) - result // $ExpectType number[] - }) -}) - -describe('R.drop - string', () => { - it('happy', () => { - const result = drop(howMany, str) - result // $ExpectType string - }) - it('curried', () => { - const result = drop(howMany)(str) - result // $ExpectType string - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#drop) - -### dropLast - -```typescript - -dropLast(howMany: number, input: T[]): T[] -``` - -It returns `howMany` items dropped from the end of list or string `input`. - -```javascript -R.dropLast(2, ['foo', 'bar', 'baz']) // => ['foo'] -R.dropLast(2, 'foobar') // => 'foob' -``` - -Try this R.dropLast example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -dropLast(howMany: number, input: T[]): T[]; -dropLast(howMany: number, input: string): string; -dropLast(howMany: number): { - (input: T[]): T[]; - (input: string): string; -}; -``` - -
- -
- -R.dropLast source - -```javascript -export function dropLast(howManyToDrop, listOrString){ - if (arguments.length === 1){ - return _listOrString => dropLast(howManyToDrop, _listOrString) - } - - return howManyToDrop > 0 ? - listOrString.slice(0, -howManyToDrop) : - listOrString.slice() -} -``` - -
- -
- -Tests - -```javascript -import assert from 'assert' - -import { dropLast } from './dropLast.js' - -test('with array', () => { - expect(dropLast(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'foo' ]) - expect(dropLast(3, [ 'foo', 'bar', 'baz' ])).toEqual([]) - expect(dropLast(4, [ 'foo', 'bar', 'baz' ])).toEqual([]) -}) - -test('with string', () => { - expect(dropLast(3, 'rambda')).toBe('ram') -}) - -test('with non-positive count', () => { - expect(dropLast(0, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) - expect(dropLast(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) - expect(dropLast(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) -}) - -test('should return copy', () => { - const xs = [ 1, 2, 3 ] - - assert.notStrictEqual(dropLast(0, xs), xs) - assert.notStrictEqual(dropLast(-1, xs), xs) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {dropLast} from 'rambda' - -const list = [1, 2, 3, 4] -const str = 'foobar' -const howMany = 2 - -describe('R.dropLast - array', () => { - it('happy', () => { - const result = dropLast(howMany, list) - result // $ExpectType number[] - }) - it('curried', () => { - const result = dropLast(howMany)(list) - result // $ExpectType number[] - }) -}) - -describe('R.dropLast - string', () => { - it('happy', () => { - const result = dropLast(howMany, str) - result // $ExpectType string - }) - it('curried', () => { - const result = dropLast(howMany)(str) - result // $ExpectType string - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropLast) - -### dropLastWhile - -```typescript - -dropLastWhile(predicate: (x: string) => boolean, iterable: string): string -``` - -```javascript -const list = [1, 2, 3, 4, 5]; -const predicate = x => x >= 3 - -const result = dropLastWhile(predicate, list); -// => [1, 2] -``` - -Try this R.dropLastWhile example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -dropLastWhile(predicate: (x: string) => boolean, iterable: string): string; -dropLastWhile(predicate: (x: string) => boolean): (iterable: string) => string; -dropLastWhile(predicate: (x: T) => boolean, iterable: T[]): T[]; -dropLastWhile(predicate: (x: T) => boolean): (iterable: T[]) => T[]; -``` - -
- -
- -R.dropLastWhile source - -```javascript -import { isArray as isArrayMethod } from './_internals/isArray.js' - -export function dropLastWhile(predicate, iterable){ - if (arguments.length === 1){ - return _iterable => dropLastWhile(predicate, _iterable) - } - if (iterable.length === 0) return iterable - const isArray = isArrayMethod(iterable) - - if (typeof predicate !== 'function'){ - throw new Error(`'predicate' is from wrong type ${ typeof predicate }`) - } - if (!isArray && typeof iterable !== 'string'){ - throw new Error(`'iterable' is from wrong type ${ typeof iterable }`) - } - - const toReturn = [] - let counter = iterable.length - - while (counter){ - const item = iterable[ --counter ] - if (!predicate(item)){ - toReturn.push(item) - break - } - } - - while (counter){ - toReturn.push(iterable[ --counter ]) - } - - return isArray ? toReturn.reverse() : toReturn.reverse().join('') -} -``` - -
- -
- -Tests - -```javascript -import { dropLastWhile as dropLastWhileRamda } from 'ramda' - -import { compareCombinations } from './_internals/testUtils.js' -import { dropLastWhile } from './dropLastWhile.js' - -const list = [ 1, 2, 3, 4, 5 ] -const str = 'foobar' - -test('with list', () => { - const result = dropLastWhile(x => x >= 3, list) - expect(result).toEqual([ 1, 2 ]) -}) - -test('with string', () => { - const result = dropLastWhile(x => x !== 'b')(str) - expect(result).toBe('foob') -}) - -test('with empty list', () => { - expect(dropLastWhile(() => true, [])).toEqual([]) - expect(dropLastWhile(() => false, [])).toEqual([]) -}) - -const possiblePredicates = [ - x => x > 2, - x => x < 2, - x => x < -2, - x => x > 10, - '', - [], - [ 1 ], -] - -const possibleIterables = [ - list, - [ {}, '1', 2 ], - str, - `${ str }${ str }`, - /foo/g, - Promise.resolve('foo'), - 2, -] - -describe('brute force', () => { - compareCombinations({ - fn : dropLastWhile, - fnRamda : dropLastWhileRamda, - firstInput : possiblePredicates, - secondInput : possibleIterables, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 12, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 21, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 49, - } - `) - }, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {dropLastWhile} from 'rambda' - -const list = [1, 2, 3] -const str = 'FOO' - -describe('R.dropLastWhile', () => { - it('with array', () => { - const result = dropLastWhile(x => x > 1, list) - - result // $ExpectType number[] - }) - it('with array - curried', () => { - const result = dropLastWhile(x => x > 1, list) - - result // $ExpectType number[] - }) - it('with string', () => { - const result = dropLastWhile(x => x !== 'F', str) - - result // $ExpectType string - }) - it('with string - curried', () => { - const result = dropLastWhile(x => x !== 'F')(str) - - result // $ExpectType string - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropLastWhile) - -### dropRepeats - -```typescript - -dropRepeats(list: T[]): T[] -``` - -It removes any successive duplicates according to `R.equals`. - -```javascript -const result = R.dropRepeats([ - 1, - 1, - {a: 1}, - {a:1}, - 1 -]) -// => [1, {a: 1}, 1] -``` - -Try this R.dropRepeats example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -dropRepeats(list: T[]): T[]; -``` - -
- -
- -R.dropRepeats source - -```javascript -import { isArray } from './_internals/isArray.js' -import { equals } from './equals.js' - -export function dropRepeats(list){ - if (!isArray(list)){ - throw new Error(`${ list } is not a list`) - } - - const toReturn = [] - - list.reduce((prev, current) => { - if (!equals(prev, current)){ - toReturn.push(current) - } - - return current - }, undefined) - - return toReturn -} -``` - -
- -
- -Tests - -```javascript -import { dropRepeats as dropRepeatsRamda } from 'ramda' - -import { compareCombinations } from './_internals/testUtils.js' -import { add } from './add.js' -import { dropRepeats } from './dropRepeats.js' - -const list = [ 1, 2, 2, 2, 3, 4, 4, 5, 5, 3, 2, 2, { a : 1 }, { a : 1 } ] -const listClean = [ 1, 2, 3, 4, 5, 3, 2, { a : 1 } ] - -test('happy', () => { - const result = dropRepeats(list) - expect(result).toEqual(listClean) -}) - -const possibleLists = [ - [ add(1), async () => {}, [ 1 ], [ 1 ], [ 2 ], [ 2 ] ], - [ add(1), add(1), add(2) ], - [], - 1, - /foo/g, - Promise.resolve(1), -] - -describe('brute force', () => { - compareCombinations({ - firstInput : possibleLists, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 0, - "RESULTS_MISMATCH": 1, - "SHOULD_NOT_THROW": 3, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 6, - } - `) - }, - fn : dropRepeats, - fnRamda : dropRepeatsRamda, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {dropRepeats} from 'rambda' - -describe('R.dropRepeats', () => { - it('happy', () => { - const result = dropRepeats([1, 2, 2, 3]) - - result // $ExpectType number[] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeats) - -### dropRepeatsBy - -```typescript - -dropRepeatsBy(fn: (a: T) => U, list: T[]): T[] -``` - -```javascript -const result = R.dropRepeatsBy( - Math.abs, - [1, -1, 2, 3, -3] -) -// => [1, 2, 3] -``` - -Try this R.dropRepeatsBy example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -dropRepeatsBy(fn: (a: T) => U, list: T[]): T[]; -dropRepeatsBy( - fn: (a: T) => U -): (list: T[]) => T[]; -dropRepeatsBy(fn: any): (list: T[]) => T[]; -``` - -
- -
- -R.dropRepeatsBy source - -```javascript -import { equals } from './equals.js' - -export function dropRepeatsBy(fn, list){ - if (arguments.length === 1) return _list => dropRepeatsBy(fn, _list) - - let lastEvaluated = null - - return list.slice().filter(item => { - if (lastEvaluated === null){ - lastEvaluated = fn(item) - - return true - } - const evaluatedResult = fn(item) - if (equals(lastEvaluated, evaluatedResult)) return false - - lastEvaluated = evaluatedResult - - return true - }) -} -``` - -
- -
- -Tests - -```javascript -import { dropRepeatsBy } from './dropRepeatsBy.js' - -test('happy', () => { - const fn = ({ i }) => ({ i : Math.abs(i) }) - const objs = [ { i : 1 }, { i : 2 }, { i : 3 }, { i : -4 }, { i : 5 }, { i : 3 } ] - const objs2 = [ - { i : 1 }, - { i : -1 }, - { i : 1 }, - { i : 2 }, - { i : 3 }, - { i : 3 }, - { i : -4 }, - { i : 4 }, - { i : 5 }, - { i : 3 }, - ] - expect(dropRepeatsBy(fn, objs2)).toEqual(objs) - expect(dropRepeatsBy(fn, objs)).toEqual(objs) -}) - -test('keeps elements from the left', () => { - expect(dropRepeatsBy(({ n, ...rest }) => ({ ...rest }), - [ - { - i : 1, - n : 1, - }, - { - i : 1, - n : 2, - }, - { - i : 1, - n : 3, - }, - { - i : 4, - n : 1, - }, - { - i : 4, - n : 2, - }, - ])).toEqual([ - { - i : 1, - n : 1, - }, - { - i : 4, - n : 1, - }, - ]) -}) - -test('returns an empty array for an empty array', () => { - expect(dropRepeatsBy(() => {}, [])).toEqual([]) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeatsBy) - -### dropRepeatsWith - -```typescript - -dropRepeatsWith(predicate: (x: T, y: T) => boolean, list: T[]): T[] -``` - -```javascript -const list = [{a:1,b:2}, {a:1,b:3}, {a:2, b:4}] -const result = R.dropRepeatsWith(R.prop('a'), list) - -// => [{a:1,b:2}, {a:2, b:4}] -``` - -Try this R.dropRepeatsWith example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -dropRepeatsWith(predicate: (x: T, y: T) => boolean, list: T[]): T[]; -dropRepeatsWith(predicate: (x: T, y: T) => boolean): (list: T[]) => T[]; -``` - -
- -
- -R.dropRepeatsWith source - -```javascript -import { isArray } from './_internals/isArray.js' - -export function dropRepeatsWith(predicate, list){ - if (arguments.length === 1){ - return _iterable => dropRepeatsWith(predicate, _iterable) - } - - if (!isArray(list)){ - throw new Error(`${ list } is not a list`) - } - - const toReturn = [] - - list.reduce((prev, current) => { - if (prev === undefined){ - toReturn.push(current) - - return current - } - if (!predicate(prev, current)){ - toReturn.push(current) - } - - return current - }, undefined) - - return toReturn -} -``` - -
- -
- -Tests - -```javascript -import { dropRepeatsWith as dropRepeatsWithRamda, eqProps } from 'ramda' - -import { compareCombinations } from './_internals/testUtils.js' -import { dropRepeatsWith } from './dropRepeatsWith.js' -import { path } from './path.js' -import { prop } from './prop.js' - -const eqI = eqProps('i') - -test('happy', () => { - const list = [ { i : 1 }, { i : 2 }, { i : 2 }, { i : 3 } ] - const expected = [ { i : 1 }, { i : 2 }, { i : 3 } ] - const result = dropRepeatsWith(eqI, list) - expect(result).toEqual(expected) -}) - -test('readme example', () => { - const list = [ - { - a : 1, - b : 2, - }, - { - a : 1, - b : 3, - }, - { - a : 2, - b : 4, - }, - ] - const result = dropRepeatsWith(prop('a'), list) - expect(result).toEqual([ - { - a : 1, - b : 2, - }, - ]) -}) - -test('keeps elements from the left predicate input', () => { - const list = [ - { - i : 1, - n : 1, - }, - { - i : 1, - n : 2, - }, - { - i : 1, - n : 3, - }, - { - i : 4, - n : 1, - }, - { - i : 4, - n : 2, - }, - ] - const expected = [ - { - i : 1, - n : 1, - }, - { - i : 4, - n : 1, - }, - ] - const result = dropRepeatsWith(eqI)(list) - expect(result).toEqual(expected) -}) - -const possiblePredicates = [ - null, - undefined, - x => x + 1, - x => true, - x => false, - x => '', - path([ 'a', 'b' ]), -] -const possibleLists = [ - null, - undefined, - [], - [ 1 ], - [ { a : { b : 1 } }, { a : { b : 1 } } ], - [ /foo/g, /foo/g ], -] - -describe('brute force', () => { - compareCombinations({ - firstInput : possiblePredicates, - secondInput : possibleLists, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 4, - "ERRORS_TYPE_MISMATCH": 14, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 0, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 42, - } - `) - }, - fn : dropRepeatsWith, - fnRamda : dropRepeatsWithRamda, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {dropRepeatsWith} from 'rambda' - -interface Foo { - a: number, -} - -describe('R.dropRepeatsWith', () => { - it('happy', () => { - const result = dropRepeatsWith( - (x: Foo, y: Foo) => { - return x.a > y.a - }, - [{a: 2}, {a: 1}] - ) - - result // $ExpectType { a: number; }[] - result[0].a // $ExpectType number - }) - it('curried', () => { - const result = dropRepeatsWith((x: Foo, y: Foo) => { - return x.a > y.a - })([{a: 2}, {a: 1}]) - - result // $ExpectType Foo[] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeatsWith) - -### dropWhile - -```typescript - -dropWhile(fn: Predicate, iterable: string): string -``` - -```javascript -const list = [1, 2, 3, 4] -const predicate = x => x < 3 -const result = R.dropWhile(predicate, list) -// => [3, 4] -``` - -Try this R.dropWhile example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -dropWhile(fn: Predicate, iterable: string): string; -dropWhile(fn: Predicate): (iterable: string) => string; -dropWhile(fn: Predicate, iterable: T[]): T[]; -dropWhile(fn: Predicate): (iterable: T[]) => T[]; -``` - -
- -
- -R.dropWhile source - -```javascript -import { isArray as isArrayMethod } from './_internals/isArray.js' - -export function dropWhile(predicate, iterable){ - if (arguments.length === 1){ - return _iterable => dropWhile(predicate, _iterable) - } - const isArray = isArrayMethod(iterable) - if (!isArray && typeof iterable !== 'string'){ - throw new Error('`iterable` is neither list nor a string') - } - - const toReturn = [] - let counter = 0 - - while (counter < iterable.length){ - const item = iterable[ counter++ ] - if (!predicate(item)){ - toReturn.push(item) - break - } - } - - while (counter < iterable.length){ - toReturn.push(iterable[ counter++ ]) - } - - return isArray ? toReturn : toReturn.join('') -} -``` - -
- -
- -Tests - -```javascript -import { dropWhile as dropWhileRamda } from 'ramda' - -import { compareCombinations } from './_internals/testUtils.js' -import { dropWhile } from './dropWhile.js' - -const list = [ 1, 2, 3, 4 ] - -test('happy', () => { - const predicate = x => x < 3 - const result = dropWhile(predicate, list) - expect(result).toEqual([ 3, 4 ]) -}) - -test('always true', () => { - const predicate = () => true - const result = dropWhileRamda(predicate, list) - expect(result).toEqual([]) -}) - -test('always false', () => { - const predicate = () => 0 - const result = dropWhile(predicate)(list) - expect(result).toEqual(list) -}) - -test('works with string as iterable', () => { - const iterable = 'foobar' - const predicate = x => x !== 'b' - const result = dropWhile(predicate, iterable) - expect(result).toBe('bar') -}) - -const possiblePredicates = [ - null, - undefined, - () => 0, - () => true, - /foo/g, - {}, - [], -] - -const possibleIterables = [ - null, - undefined, - [], - {}, - 1, - '', - 'foobar', - [ '' ], - [ 1, 2, 3, 4, 5 ], -] - -describe('brute force', () => { - compareCombinations({ - firstInput : possiblePredicates, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 15, - "ERRORS_TYPE_MISMATCH": 14, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 14, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 63, - } - `) - }, - secondInput : possibleIterables, - fn : dropWhile, - fnRamda : dropWhileRamda, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {dropWhile} from 'rambda' - -const list = [1, 2, 3, 4] - -describe('R.dropWhile', () => { - it('happy', () => { - const result = dropWhile(x => x > 2, list) - - result // $ExpectType number[] - }) - it('curried require explicit type', () => { - const result = dropWhile(x => x > 2)(list) - - result // $ExpectType number[] - }) -}) - -describe('with string as iterable', () => { - const str = 'foobar' - it('happy', () => { - const result = dropWhile(x => x !== 'b', str) - - result // $ExpectType string - }) - it('curried require explicit type', () => { - const result = dropWhile(x => x !== 'b')(str) - - result // $ExpectType string - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropWhile) - -### either - -```typescript - -either(firstPredicate: Pred, secondPredicate: Pred): Pred -``` - -It returns a new `predicate` function from `firstPredicate` and `secondPredicate` inputs. - -This `predicate` function will return `true`, if any of the two input predicates return `true`. - -```javascript -const firstPredicate = x => x > 10 -const secondPredicate = x => x % 2 === 0 -const predicate = R.either(firstPredicate, secondPredicate) - -const result = [ - predicate(15), - predicate(8), - predicate(7), -] -// => [true, true, false] -``` - -Try this R.either example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -either(firstPredicate: Pred, secondPredicate: Pred): Pred; -either(firstPredicate: Predicate, secondPredicate: Predicate): Predicate; -either(firstPredicate: Predicate): (secondPredicate: Predicate) => Predicate; -either(firstPredicate: Pred): (secondPredicate: Pred) => Pred; -``` - -
- -
- -R.either source - -```javascript -export function either(firstPredicate, secondPredicate){ - if (arguments.length === 1){ - return _secondPredicate => either(firstPredicate, _secondPredicate) - } - - return (...input) => - Boolean(firstPredicate(...input) || secondPredicate(...input)) -} -``` - -
- -
- -Tests - -```javascript -import { either } from './either.js' - -test('with multiple inputs', () => { - const between = function ( - a, b, c - ){ - return a < b && b < c - } - const total20 = function ( - a, b, c - ){ - return a + b + c === 20 - } - const fn = either(between, total20) - expect(fn( - 7, 8, 5 - )).toBeTrue() -}) - -test('skip evaluation of the second expression', () => { - let effect = 'not evaluated' - const F = function (){ - return true - } - const Z = function (){ - effect = 'Z got evaluated' - } - either(F, Z)() - - expect(effect).toBe('not evaluated') -}) - -test('case 1', () => { - const firstFn = val => val > 0 - const secondFn = val => val * 5 > 10 - - expect(either(firstFn, secondFn)(1)).toBeTrue() -}) - -test('case 2', () => { - const firstFn = val => val > 0 - const secondFn = val => val === -10 - const fn = either(firstFn)(secondFn) - - expect(fn(-10)).toBeTrue() -}) -``` - -
- -
- -TypeScript test - -```typescript -import {either} from 'rambda' - -describe('R.either', () => { - it('with passed type', () => { - const fn = either( - x => x > 1, - x => x % 2 === 0 - ) - fn // $ExpectType Predicate - const result = fn(2) // $ExpectType boolean - result // $ExpectType boolean - }) - it('with passed type - curried', () => { - const fn = either(x => x > 1)(x => x % 2 === 0) - fn // $ExpectType Predicate - const result = fn(2) - result // $ExpectType boolean - }) - it('no type passed', () => { - const fn = either( - x => { - x // $ExpectType any - return x > 1 - }, - x => { - x // $ExpectType any - return x % 2 === 0 - } - ) - const result = fn(2) - result // $ExpectType boolean - }) - it('no type passed - curried', () => { - const fn = either((x: number) => { - x // $ExpectType number - return x > 1 - })((x: number) => { - x // $ExpectType number - return x % 2 === 0 - }) - const result = fn(2) - result // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#either) - -### empty - -```typescript - -empty(x: T): T -``` - -```javascript -const result = [R.empty([1,2,3]), R.empty('foo'), R.empty({x: 1, y: 2})] -// => [[], '', {}] -``` - -Try this R.empty example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -empty(x: T): T; -``` - -
- -
- -R.empty source - -```javascript -import { type } from './type.js' - -export function empty(list){ - if (typeof list === 'string') return '' - - if (Array.isArray(list)){ - const { name } = list.constructor - if (name === 'Uint8Array') return Uint8Array.from('') - - if (name === 'Float32Array') return new Float32Array([]) - - return [] - } - if (type(list) === 'Object') return {} -} -``` - -
- -
- -Tests - -```javascript -import { empty } from 'ramda' - -test('returns empty array given array', () => { - expect(empty([ 1, 2, 3 ])).toEqual([]) -}) - -test('returns empty array of equivalent type given typed array', () => { - expect(empty(Uint8Array.from('123'))).toEqual(Uint8Array.from('')) - expect(empty(Uint8Array.from('123')).constructor.name).toBe('Uint8Array') - expect(empty(new Float32Array([ 1, 2, 3 ]))).toEqual(new Float32Array([])) - expect(empty(new Float32Array([ 1, 2, 3 ])).constructor.name).toBe('Float32Array') -}) - -test('returns empty string given string', () => { - expect(empty('abc')).toBe('') - expect(empty(new String('abc'))).toBe('') -}) - -test('other types', () => { - expect(empty({ a : 1 })).toEqual({}) - expect(empty(/foo/g)).toBeUndefined() -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#empty) - -### endsWith - -```typescript - -endsWith(question: T, str: string): boolean -``` - -When iterable is a string, then it behaves as `String.prototype.endsWith`. -When iterable is a list, then it uses R.equals to determine if the target list ends in the same way as the given target. - -```javascript -const str = 'foo-bar' -const list = [{a:1}, {a:2}, {a:3}] - -const result = [ - R.endsWith('bar', str), - R.endsWith([{a:1}, {a:2}], list) -] -// => [true, true] -``` - -Try this R.endsWith example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -endsWith(question: T, str: string): boolean; -endsWith(question: T): (str: string) => boolean; -endsWith(question: T[], list: T[]): boolean; -endsWith(question: T[]): (list: T[]) => boolean; -``` - -
- -
- -R.endsWith source - -```javascript -import { isArray } from './_internals/isArray.js' -import { equals } from './equals.js' - -export function endsWith(target, iterable){ - if (arguments.length === 1) return _iterable => endsWith(target, _iterable) - - if (typeof iterable === 'string'){ - return iterable.endsWith(target) - } - if (!isArray(target)) return false - - const diff = iterable.length - target.length - let correct = true - const filtered = target.filter((x, index) => { - if (!correct) return false - const result = equals(x, iterable[ index + diff ]) - if (!result) correct = false - - return result - }) - - return filtered.length === target.length -} -``` - -
- -
- -Tests - -```javascript -import { endsWith as endsWithRamda } from 'ramda' - -import { compareCombinations } from './_internals/testUtils.js' -import { endsWith } from './endsWith.js' - -test('with string', () => { - expect(endsWith('bar', 'foo-bar')).toBeTrue() - expect(endsWith('baz')('foo-bar')).toBeFalse() -}) - -test('use R.equals with array', () => { - const list = [ { a : 1 }, { a : 2 }, { a : 3 } ] - expect(endsWith({ a : 3 }, list)).toBeFalse(), - expect(endsWith([ { a : 3 } ], list)).toBeTrue() - expect(endsWith([ { a : 2 }, { a : 3 } ], list)).toBeTrue() - expect(endsWith(list, list)).toBeTrue() - expect(endsWith([ { a : 1 } ], list)).toBeFalse() -}) - -export const possibleTargets = [ - NaN, - [ NaN ], - /foo/, - [ /foo/ ], - Promise.resolve(1), - [ Promise.resolve(1) ], - Error('foo'), - [ Error('foo') ], -] - -export const possibleIterables = [ - [ Promise.resolve(1), Promise.resolve(2) ], - [ /foo/, /bar/ ], - [ NaN ], - [ Error('foo'), Error('bar') ], -] - -describe('brute force', () => { - compareCombinations({ - fn : endsWith, - fnRamda : endsWithRamda, - firstInput : possibleTargets, - secondInput : possibleIterables, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 0, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 0, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 32, - } - `) - }, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {endsWith} from 'rambda' - -describe('R.endsWith - array', () => { - const target = [{a: 2}] - const input = [{a: 1}, {a: 2}] - it('happy', () => { - const result = endsWith(target, input) - result // $ExpectType boolean - }) - it('curried', () => { - const result = endsWith(target)(input) - result // $ExpectType boolean - }) -}) - -describe('R.endsWith - string', () => { - const target = 'bar' - const input = 'foo bar' - it('happy', () => { - const result = endsWith(target, input) - result // $ExpectType boolean - }) - it('curried', () => { - const result = endsWith(target)(input) - result // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#endsWith) - -### eqBy - -```typescript - -eqBy(fn: (a: T) => unknown, a: T, b: T): boolean -``` - -```javascript -const result = R.eqBy(Math.abs, 5, -5) -// => true -``` - -Try this R.eqBy example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -eqBy(fn: (a: T) => unknown, a: T, b: T): boolean; -eqBy(fn: (a: T) => unknown, a: T): (b: T) => boolean; -eqBy(fn: (a: T) => unknown): { - (a: T, b: T): boolean; - (a: T): (b: T) => boolean; -}; -``` - -
- -
- -R.eqBy source - -```javascript -import { curry } from './curry.js' -import { equals } from './equals.js' - -export function eqByFn( - fn, a, b -){ - return equals(fn(a), fn(b)) -} - -export const eqBy = curry(eqByFn) -``` - -
- -
- -Tests - -```javascript -import { eqByFn } from './eqBy.js' - -test('deteremines whether two values map to the same value in the codomain', () => { - expect(eqByFn( - Math.abs, 5, 5 - )).toBe(true) - expect(eqByFn( - Math.abs, 5, -5 - )).toBe(true) - expect(eqByFn( - Math.abs, -5, 5 - )).toBe(true) - expect(eqByFn( - Math.abs, -5, -5 - )).toBe(true) - expect(eqByFn( - Math.abs, 42, 99 - )).toBe(false) -}) - -test('has R.equals semantics', () => { - expect(eqByFn( - Math.abs, NaN, NaN - )).toBe(true) - expect(eqByFn( - Math.abs, [ 42 ], [ 42 ] - )).toBe(true) - expect(eqByFn( - x => x, { a : 1 }, { a : 1 } - )).toBe(true) - expect(eqByFn( - x => x, { a : 1 }, { a : 2 } - )).toBe(false) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#eqBy) - -### eqProps - -```typescript - -eqProps(prop: string, obj1: T, obj2: U): boolean -``` - -It returns `true` if property `prop` in `obj1` is equal to property `prop` in `obj2` according to `R.equals`. - -```javascript -const obj1 = {a: 1, b:2} -const obj2 = {a: 1, b:3} -const result = R.eqProps('a', obj1, obj2) -// => true -``` - -Try this R.eqProps example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -eqProps(prop: string, obj1: T, obj2: U): boolean; -eqProps

(prop: P): (obj1: Record, obj2: Record) => boolean; -eqProps(prop: string, obj1: T): (obj2: U) => boolean; -``` - -

- -
- -R.eqProps source - -```javascript -import { curry } from './curry.js' -import { equals } from './equals.js' -import { prop } from './prop.js' - -function eqPropsFn( - property, objA, objB -){ - return equals(prop(property, objA), prop(property, objB)) -} - -export const eqProps = curry(eqPropsFn) -``` - -
- -
- -Tests - -```javascript -import { eqProps as eqPropsRamda } from 'ramda' - -import { compareCombinations } from './_internals/testUtils.js' -import { eqProps } from './eqProps.js' - -const obj1 = { - a : 1, - b : 2, -} -const obj2 = { - a : 1, - b : 3, -} - -test('props are equal', () => { - const result = eqProps( - 'a', obj1, obj2 - ) - expect(result).toBeTrue() -}) - -test('props are not equal', () => { - const result = eqProps( - 'b', obj1, obj2 - ) - expect(result).toBeFalse() -}) - -test('prop does not exist', () => { - const result = eqProps( - 'c', obj1, obj2 - ) - expect(result).toBeTrue() -}) - -test('can handle null or undefined object', () => { - expect(eqProps( - 'value', { value : 0 }, null - )).toBeFalse() - expect(eqProps( - 'value', { value : 0 }, undefined - )).toBeFalse() - expect(eqProps( - 'value', null, { value : 0 } - )).toBeFalse() - expect(eqProps( - 'value', undefined, { value : 0 } - )).toBeFalse() - expect(eqProps( - 'value', undefined, { value : undefined } - )).toBeTrue() - expect(eqProps( - 'value', null, { value : undefined } - )).toBeTrue() - expect(eqProps( - 'value', { value : undefined }, undefined - )).toBeTrue() - expect(eqProps( - 'value', { value : undefined }, null - )).toBeTrue() - expect(eqProps( - 'value', {}, null - )).toBeTrue() - expect(eqProps( - 'value', {}, undefined - )).toBeTrue() - expect(eqProps( - 'value', null, {} - )).toBeTrue() - expect(eqProps( - 'value', undefined, {} - )).toBeTrue() -}) - -const possibleProps = [ 'a', 'a.b', null, false, 0, 1, {}, [] ] - -const possibleObjects = [ - { a : 1 }, - { - a : 1, - b : 2, - }, - {}, - [], - null, - { - a : { b : 1 }, - c : 2, - }, - { - a : { b : 1 }, - c : 3, - }, - { a : { b : 2 } }, -] - -describe('brute force', () => { - let totalTestsCounter = 0 - - compareCombinations({ - firstInput : possibleProps, - setCounter : () => totalTestsCounter++, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 0, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 0, - "SHOULD_THROW": 120, - "TOTAL_TESTS": 512, - } - `) - }, - secondInput : possibleObjects, - thirdInput : possibleObjects, - fn : eqProps, - fnRamda : eqPropsRamda, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {eqProps} from 'rambda' - -const obj1 = {a: {b: 1}, c: 2} -const obj2 = {a: {b: 1}, c: 3} - -describe('R.eqProps', () => { - it('happy', () => { - const result = eqProps('a', obj1, obj2) - - result // $ExpectType boolean - }) - it('curried', () => { - const result = eqProps('a', obj1)(obj2) - - result // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#eqProps) - -### equals - -```typescript - -equals(x: T, y: T): boolean -``` - -It deeply compares `x` and `y` and returns `true` if they are equal. - -> :boom: It doesn't handle cyclical data structures and functions - -```javascript -R.equals( - [1, {a:2}, [{b: 3}]], - [1, {a:2}, [{b: 3}]] -) // => true -``` - -Try this R.equals example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -equals(x: T, y: T): boolean; -equals(x: T): (y: T) => boolean; -``` - -
- -
- -R.equals source - -```javascript -import { isArray } from './_internals/isArray.js' -import { type } from './type.js' - -export function _lastIndexOf(valueToFind, list){ - if (!isArray(list)) - throw new Error(`Cannot read property 'indexOf' of ${ list }`) - - const typeOfValue = type(valueToFind) - if (![ 'Array', 'NaN', 'Object', 'RegExp' ].includes(typeOfValue)) - return list.lastIndexOf(valueToFind) - - const { length } = list - let index = length - let foundIndex = -1 - - while (--index > -1 && foundIndex === -1) - if (equals(list[ index ], valueToFind)) - foundIndex = index - - return foundIndex -} - -export function _indexOf(valueToFind, list){ - if (!isArray(list)) - throw new Error(`Cannot read property 'indexOf' of ${ list }`) - - const typeOfValue = type(valueToFind) - if (![ 'Array', 'NaN', 'Object', 'RegExp' ].includes(typeOfValue)) - return list.indexOf(valueToFind) - - let index = -1 - let foundIndex = -1 - const { length } = list - - while (++index < length && foundIndex === -1) - if (equals(list[ index ], valueToFind)) - foundIndex = index - - return foundIndex -} - -function _arrayFromIterator(iter){ - const list = [] - let next - while (!(next = iter.next()).done) - list.push(next.value) - - return list -} - -function _compareSets(a, b){ - if (a.size !== b.size) - return false - - const aList = _arrayFromIterator(a.values()) - const bList = _arrayFromIterator(b.values()) - - const filtered = aList.filter(aInstance => _indexOf(aInstance, bList) === -1) - - return filtered.length === 0 -} - -function compareErrors(a, b){ - if (a.message !== b.message) return false - if (a.toString !== b.toString) return false - - return a.toString() === b.toString() -} - -function parseDate(maybeDate){ - if (!maybeDate.toDateString) return [ false ] - - return [ true, maybeDate.getTime() ] -} - -function parseRegex(maybeRegex){ - if (maybeRegex.constructor !== RegExp) return [ false ] - - return [ true, maybeRegex.toString() ] -} - -export function equals(a, b){ - if (arguments.length === 1) return _b => equals(a, _b) - - if (Object.is(a, b)) return true - - const aType = type(a) - - if (aType !== type(b)) return false - if (aType === 'Function') - return a.name === undefined ? false : a.name === b.name - - if ([ 'NaN', 'Null', 'Undefined' ].includes(aType)) return true - - if ([ 'BigInt', 'Number' ].includes(aType)){ - if (Object.is(-0, a) !== Object.is(-0, b)) return false - - return a.toString() === b.toString() - } - - if ([ 'Boolean', 'String' ].includes(aType)) - return a.toString() === b.toString() - - if (aType === 'Array'){ - const aClone = Array.from(a) - const bClone = Array.from(b) - - if (aClone.toString() !== bClone.toString()) - return false - - let loopArrayFlag = true - aClone.forEach((aCloneInstance, aCloneIndex) => { - if (loopArrayFlag) - if ( - aCloneInstance !== bClone[ aCloneIndex ] && - !equals(aCloneInstance, bClone[ aCloneIndex ]) - ) - loopArrayFlag = false - - }) - - return loopArrayFlag - } - - const aRegex = parseRegex(a) - const bRegex = parseRegex(b) - - if (aRegex[ 0 ]) - return bRegex[ 0 ] ? aRegex[ 1 ] === bRegex[ 1 ] : false - else if (bRegex[ 0 ]) return false - - const aDate = parseDate(a) - const bDate = parseDate(b) - - if (aDate[ 0 ]) - return bDate[ 0 ] ? aDate[ 1 ] === bDate[ 1 ] : false - else if (bDate[ 0 ]) return false - - if (a instanceof Error){ - if (!(b instanceof Error)) return false - - return compareErrors(a, b) - } - - if (aType === 'Set') - return _compareSets(a, b) - - if (aType === 'Object'){ - const aKeys = Object.keys(a) - - if (aKeys.length !== Object.keys(b).length) - return false - - let loopObjectFlag = true - aKeys.forEach(aKeyInstance => { - if (loopObjectFlag){ - const aValue = a[ aKeyInstance ] - const bValue = b[ aKeyInstance ] - - if (aValue !== bValue && !equals(aValue, bValue)) - loopObjectFlag = false - - } - }) - - return loopObjectFlag - } - - return false -} -``` - -
- -
- -Tests - -```javascript -import { equals as equalsRamda } from 'ramda' - -import { compareCombinations } from './_internals/testUtils.js' -import { variousTypes } from './benchmarks/_utils.js' -import { equals } from './equals.js' - -test('compare functions', () => { - function foo(){} - function bar(){} - const baz = () => {} - - const expectTrue = equals(foo, foo) - const expectFalseFirst = equals(foo, bar) - const expectFalseSecond = equals(foo, baz) - - expect(expectTrue).toBeTrue() - expect(expectFalseFirst).toBeFalse() - expect(expectFalseSecond).toBeFalse() -}) - -test('with array of objects', () => { - const list1 = [ { a : 1 }, [ { b : 2 } ] ] - const list2 = [ { a : 1 }, [ { b : 2 } ] ] - const list3 = [ { a : 1 }, [ { b : 3 } ] ] - - expect(equals(list1, list2)).toBeTrue() - expect(equals(list1, list3)).toBeFalse() -}) - -test('with regex', () => { - expect(equals(/s/, /s/)).toBeTrue() - expect(equals(/s/, /d/)).toBeFalse() - expect(equals(/a/gi, /a/gi)).toBeTrue() - expect(equals(/a/gim, /a/gim)).toBeTrue() - expect(equals(/a/gi, /a/i)).toBeFalse() -}) - -test('not a number', () => { - expect(equals([ NaN ], [ NaN ])).toBeTrue() -}) - -test('new number', () => { - expect(equals(new Number(0), new Number(0))).toBeTrue() - expect(equals(new Number(0), new Number(1))).toBeFalse() - expect(equals(new Number(1), new Number(0))).toBeFalse() -}) - -test('new string', () => { - expect(equals(new String(''), new String(''))).toBeTrue() - expect(equals(new String(''), new String('x'))).toBeFalse() - expect(equals(new String('x'), new String(''))).toBeFalse() - expect(equals(new String('foo'), new String('foo'))).toBeTrue() - expect(equals(new String('foo'), new String('bar'))).toBeFalse() - expect(equals(new String('bar'), new String('foo'))).toBeFalse() -}) - -test('new Boolean', () => { - expect(equals(new Boolean(true), new Boolean(true))).toBeTrue() - expect(equals(new Boolean(false), new Boolean(false))).toBeTrue() - expect(equals(new Boolean(true), new Boolean(false))).toBeFalse() - expect(equals(new Boolean(false), new Boolean(true))).toBeFalse() -}) - -test('new Error', () => { - expect(equals(new Error('XXX'), {})).toBeFalse() - expect(equals(new Error('XXX'), new TypeError('XXX'))).toBeFalse() - expect(equals(new Error('XXX'), new Error('YYY'))).toBeFalse() - expect(equals(new Error('XXX'), new Error('XXX'))).toBeTrue() - expect(equals(new Error('XXX'), new TypeError('YYY'))).toBeFalse() - expect(equals(new Error('XXX'), new Error('XXX'))).toBeTrue() -}) - -test('with dates', () => { - expect(equals(new Date(0), new Date(0))).toBeTrue() - expect(equals(new Date(1), new Date(1))).toBeTrue() - expect(equals(new Date(0), new Date(1))).toBeFalse() - expect(equals(new Date(1), new Date(0))).toBeFalse() - expect(equals(new Date(0), {})).toBeFalse() - expect(equals({}, new Date(0))).toBeFalse() -}) - -test('ramda spec', () => { - expect(equals({}, {})).toBeTrue() - - expect(equals({ - a : 1, - b : 2, - }, - { - a : 1, - b : 2, - })).toBeTrue() - - expect(equals({ - a : 2, - b : 3, - }, - { - a : 2, - b : 3, - })).toBeTrue() - - expect(equals({ - a : 2, - b : 3, - }, - { - a : 3, - b : 3, - })).toBeFalse() - - expect(equals({ - a : 2, - b : 3, - c : 1, - }, - { - a : 2, - b : 3, - })).toBeFalse() -}) - -test('works with boolean tuple', () => { - expect(equals([ true, false ], [ true, false ])).toBeTrue() - expect(equals([ true, false ], [ true, true ])).toBeFalse() -}) - -test('works with equal objects within array', () => { - const objFirst = { - a : { - b : 1, - c : 2, - d : [ 1 ], - }, - } - const objSecond = { - a : { - b : 1, - c : 2, - d : [ 1 ], - }, - } - - const x = [ 1, 2, objFirst, null, '', [] ] - const y = [ 1, 2, objSecond, null, '', [] ] - expect(equals(x, y)).toBeTrue() -}) - -test('works with different objects within array', () => { - const objFirst = { a : { b : 1 } } - const objSecond = { a : { b : 2 } } - - const x = [ 1, 2, objFirst, null, '', [] ] - const y = [ 1, 2, objSecond, null, '', [] ] - expect(equals(x, y)).toBeFalse() -}) - -test('works with undefined as second argument', () => { - expect(equals(1, undefined)).toBeFalse() - - expect(equals(undefined, undefined)).toBeTrue() -}) - -test('compare sets', () => { - const toCompareDifferent = new Set([ { a : 1 }, { a : 2 } ]) - const toCompareSame = new Set([ { a : 1 }, { a : 2 }, { a : 1 } ]) - const testSet = new Set([ { a : 1 }, { a : 2 }, { a : 1 } ]) - expect(equals(toCompareSame, testSet)).toBeTruthy() - expect(equals(toCompareDifferent, testSet)).toBeFalsy() - expect(equalsRamda(toCompareSame, testSet)).toBeTruthy() - expect(equalsRamda(toCompareDifferent, testSet)).toBeFalsy() -}) - -test('compare simple sets', () => { - const testSet = new Set([ '2', '3', '3', '2', '1' ]) - expect(equals(new Set([ '3', '2', '1' ]), testSet)).toBeTruthy() - expect(equals(new Set([ '3', '2', '0' ]), testSet)).toBeFalsy() -}) - -test('various examples', () => { - expect(equals([ 1, 2, 3 ])([ 1, 2, 3 ])).toBeTrue() - - expect(equals([ 1, 2, 3 ], [ 1, 2 ])).toBeFalse() - - expect(equals(1, 1)).toBeTrue() - - expect(equals(1, '1')).toBeFalse() - - expect(equals({}, {})).toBeTrue() - - expect(equals({ - a : 1, - b : 2, - }, - { - a : 1, - b : 2, - })).toBeTrue() - - expect(equals({ - a : 1, - b : 2, - }, - { - a : 1, - b : 1, - })).toBeFalse() - - expect(equals({ - a : 1, - b : false, - }, - { - a : 1, - b : 1, - })).toBeFalse() - - expect(equals({ - a : 1, - b : 2, - }, - { - a : 1, - b : 2, - c : 3, - })).toBeFalse() - - expect(equals({ - x : { - a : 1, - b : 2, - }, - }, - { - x : { - a : 1, - b : 2, - c : 3, - }, - })).toBeFalse() - - expect(equals({ - a : 1, - b : 2, - }, - { - a : 1, - b : 3, - })).toBeFalse() - - expect(equals({ a : { b : { c : 1 } } }, { a : { b : { c : 1 } } })).toBeTrue() - - expect(equals({ a : { b : { c : 1 } } }, { a : { b : { c : 2 } } })).toBeFalse() - - expect(equals({ a : {} }, { a : {} })).toBeTrue() - - expect(equals('', '')).toBeTrue() - - expect(equals('foo', 'foo')).toBeTrue() - - expect(equals('foo', 'bar')).toBeFalse() - - expect(equals(0, false)).toBeFalse() - - expect(equals(/\s/g, null)).toBeFalse() - - expect(equals(null, null)).toBeTrue() - - expect(equals(false)(null)).toBeFalse() -}) - -test('with custom functions', () => { - function foo(){ - return 1 - } - foo.prototype.toString = () => '' - const result = equals(foo, foo) - - expect(result).toBeTrue() -}) - -test('with classes', () => { - class Foo{} - const foo = new Foo() - const result = equals(foo, foo) - - expect(result).toBeTrue() -}) - -test('with negative zero', () => { - expect(equals(-0, -0)).toBeTrue() - expect(equals(-0, 0)).toBeFalse() - expect(equals(0, 0)).toBeTrue() - expect(equals(-0, 1)).toBeFalse() -}) - -test('with big int', () => { - const a = BigInt(9007199254740991) - const b = BigInt(9007199254740991) - const c = BigInt(7007199254740991) - expect(equals(a, b)).toBeTrue() - expect(equals(a, c)).toBeFalse() -}) - -describe('brute force', () => { - compareCombinations({ - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` -{ - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 0, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 0, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 289, -} -`) - }, - firstInput : variousTypes, - fn : equals, - fnRamda : equalsRamda, - secondInput : variousTypes, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {equals} from 'rambda' - -describe('R.equals', () => { - it('happy', () => { - const result = equals(4, 1) - result // $ExpectType boolean - }) - it('with object', () => { - const foo = {a: 1} - const bar = {a: 2} - const result = equals(foo, bar) - result // $ExpectType boolean - }) - it('curried', () => { - const result = equals(4)(1) - - result // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#equals) - -### evolve - -```typescript - -evolve(rules: ((x: T) => U)[], list: T[]): U[] -``` - -It takes object or array of functions as set of rules. These `rules` are applied to the `iterable` input to produce the result. - -> :boom: Error handling of this method differs between Ramda and Rambda. Ramda for some wrong inputs returns result and for other - it returns one of the inputs. Rambda simply throws when inputs are not correct. Full details for this mismatch are listed in `source/_snapshots/evolve.spec.js.snap` file. - -```javascript -const rules = { - foo : add(1), - bar : add(-1), -} -const input = { - a : 1, - foo : 2, - bar : 3, -} -const result = evolve(rules, input) -const expected = { - a : 1, - foo : 3, - bar : 2, -}) -// => `result` is equal to `expected` -``` - -Try this R.evolve example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -evolve(rules: ((x: T) => U)[], list: T[]): U[]; -evolve(rules: ((x: T) => U)[]) : (list: T[]) => U[]; -evolve>(rules: E, obj: V): Evolve; -evolve(rules: E): >(obj: V) => Evolve; -``` - -
- -
- -R.evolve source - -```javascript -import { isArray } from './_internals/isArray.js' -import { mapArray, mapObject } from './map.js' -import { type } from './type.js' - -export function evolveArray(rules, list){ - return mapArray( - (x, i) => { - if (type(rules[ i ]) === 'Function'){ - return rules[ i ](x) - } - - return x - }, - list, - true - ) -} - -export function evolveObject(rules, iterable){ - return mapObject((x, prop) => { - if (type(x) === 'Object'){ - const typeRule = type(rules[ prop ]) - if (typeRule === 'Function'){ - return rules[ prop ](x) - } - if (typeRule === 'Object'){ - return evolve(rules[ prop ], x) - } - - return x - } - if (type(rules[ prop ]) === 'Function'){ - return rules[ prop ](x) - } - - return x - }, iterable) -} - -export function evolve(rules, iterable){ - if (arguments.length === 1){ - return _iterable => evolve(rules, _iterable) - } - const rulesType = type(rules) - const iterableType = type(iterable) - - if (iterableType !== rulesType){ - throw new Error('iterableType !== rulesType') - } - - if (![ 'Object', 'Array' ].includes(rulesType)){ - throw new Error(`'iterable' and 'rules' are from wrong type ${ rulesType }`) - } - - if (iterableType === 'Object'){ - return evolveObject(rules, iterable) - } - - return evolveArray(rules, iterable) -} -``` - -
- -
- -Tests - -```javascript -import { evolve as evolveRamda } from 'ramda' - -import { add } from '../rambda.js' -import { compareCombinations, compareToRamda } from './_internals/testUtils.js' -import { evolve } from './evolve.js' - -test('happy', () => { - const rules = { - foo : add(1), - nested : { bar : x => Object.keys(x).length }, - } - const input = { - a : 1, - foo : 2, - nested : { bar : { z : 3 } }, - } - const result = evolve(rules, input) - expect(result).toEqual({ - a : 1, - foo : 3, - nested : { bar : 1 }, - }) -}) - -test('nested rule is wrong', () => { - const rules = { - foo : add(1), - nested : { bar : 10 }, - } - const input = { - a : 1, - foo : 2, - nested : { bar : { z : 3 } }, - } - const result = evolve(rules)(input) - expect(result).toEqual({ - a : 1, - foo : 3, - nested : { bar : { z : 3 } }, - }) -}) - -test('is recursive', () => { - const rules = { - nested : { - second : add(-1), - third : add(1), - }, - } - const object = { - first : 1, - nested : { - second : 2, - third : 3, - }, - } - const expected = { - first : 1, - nested : { - second : 1, - third : 4, - }, - } - const result = evolve(rules, object) - expect(result).toEqual(expected) -}) - -test('ignores primitive values', () => { - const rules = { - n : 2, - m : 'foo', - } - const object = { - n : 0, - m : 1, - } - const expected = { - n : 0, - m : 1, - } - const result = evolve(rules, object) - expect(result).toEqual(expected) -}) - -test('with array', () => { - const rules = [ add(1), add(-1) ] - const list = [ 100, 1400 ] - const expected = [ 101, 1399 ] - const result = evolve(rules, list) - expect(result).toEqual(expected) -}) - -const rulesObject = { a : add(1) } -const rulesList = [ add(1) ] -const possibleIterables = [ null, undefined, '', 42, [], [ 1 ], { a : 1 } ] -const possibleRules = [ ...possibleIterables, rulesList, rulesObject ] - -describe('brute force', () => { - compareCombinations({ - firstInput : possibleRules, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 4, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 51, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 63, - } - `) - }, - secondInput : possibleIterables, - fn : evolve, - fnRamda : evolveRamda, - }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {evolve, add} from 'rambda' - -describe('R.evolve', () => { - it('happy', () => { - const input = { - foo: 2, - nested: { - a: 1, - bar: 3, - }, - } - const rules = { - foo: add(1), - nested: { - a: add(-1), - bar: add(1), - }, - } - const result = evolve(rules, input) - const curriedResult = evolve(rules)(input) - - result.nested.a // $ExpectType number - curriedResult.nested.a // $ExpectType number - result.nested.bar // $ExpectType number - result.foo // $ExpectType number - }) - it('with array', () => { - const rules = [String, String] - const input = [100, 1400] - const result = evolve(rules, input) - const curriedResult = evolve(rules)(input) - result // $ExpectType string[] - curriedResult // $ExpectType string[] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#evolve) - -### F - -```typescript - -F(): boolean -``` - -```javascript -F() // => false -``` - -Try this R.F example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -F(): boolean; -``` - -
- -
- -R.F source - -```javascript -export function F(){ - return false -} -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#F) - -### filter - -```typescript - -filter(predicate: Predicate): (input: T[]) => T[] -``` - -It filters list or object `input` using a `predicate` function. - -```javascript -const list = [3, 4, 3, 2] -const listPredicate = x => x > 2 - -const object = {abc: 'fo', xyz: 'bar', baz: 'foo'} -const objectPredicate = (x, prop) => x.length + prop.length > 5 - -const result = [ - R.filter(listPredicate, list), - R.filter(objectPredicate, object) -] -// => [ [3, 4], { xyz: 'bar', baz: 'foo'} ] -``` - -Try this R.filter example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -filter(predicate: Predicate): (input: T[]) => T[]; -filter(predicate: Predicate, input: T[]): T[]; -filter(predicate: ObjectPredicate): (x: Dictionary) => Dictionary; -filter(predicate: ObjectPredicate, x: Dictionary): Dictionary; -``` - -
- -
- -R.filter source - -```javascript -import { isArray } from './_internals/isArray.js' - -export function filterObject(predicate, obj){ - const willReturn = {} - - for (const prop in obj){ - if (predicate( - obj[ prop ], prop, obj - )){ - willReturn[ prop ] = obj[ prop ] - } - } - - return willReturn -} - -export function filterArray( - predicate, list, indexed = false -){ - let index = 0 - const len = list.length - const willReturn = [] - - while (index < len){ - const predicateResult = indexed ? - predicate(list[ index ], index) : - predicate(list[ index ]) - if (predicateResult){ - willReturn.push(list[ index ]) - } - - index++ - } - - return willReturn -} - -export function filter(predicate, iterable){ - if (arguments.length === 1) - return _iterable => filter(predicate, _iterable) - if (!iterable){ - throw new Error('Incorrect iterable input') - } - - if (isArray(iterable)) return filterArray( - predicate, iterable, false - ) - - return filterObject(predicate, iterable) -} -``` - -
- -
- -Tests - -```javascript -import { filter as filterRamda } from 'ramda' - -import { filter } from './filter.js' -import { T } from './T.js' - -const sampleObject = { - a : 1, - b : 2, - c : 3, - d : 4, -} - -test('happy', () => { - const isEven = n => n % 2 === 0 - - expect(filter(isEven, [ 1, 2, 3, 4 ])).toEqual([ 2, 4 ]) - expect(filter(isEven, { - a : 1, - b : 2, - d : 3, - })).toEqual({ b : 2 }) -}) - -test('predicate when input is object', () => { - const obj = { - a : 1, - b : 2, - } - const predicate = ( - val, prop, inputObject - ) => { - expect(inputObject).toEqual(obj) - expect(typeof prop).toBe('string') - - return val < 2 - } - expect(filter(predicate, obj)).toEqual({ a : 1 }) -}) - -test('with object', () => { - const isEven = n => n % 2 === 0 - const result = filter(isEven, sampleObject) - const expectedResult = { - b : 2, - d : 4, - } - - expect(result).toEqual(expectedResult) -}) - -test('bad inputs difference between Ramda and Rambda', () => { - expect(() => filter(T, null)).toThrowWithMessage(Error, - 'Incorrect iterable input') - expect(() => filter(T)(undefined)).toThrowWithMessage(Error, - 'Incorrect iterable input') - expect(() => filterRamda(T, null)).toThrowWithMessage(TypeError, - 'Cannot read properties of null (reading \'fantasy-land/filter\')') - expect(() => filterRamda(T, undefined)).toThrowWithMessage(TypeError, - 'Cannot read properties of undefined (reading \'fantasy-land/filter\')') -}) -``` - -
- -
- -TypeScript test - -```typescript -import {filter} from 'rambda' - -const list = [1, 2, 3] -const obj = {a: 1, b: 2} - -describe('R.filter with array', () => { - it('happy', () => { - const result = filter(x => { - x // $ExpectType number - return x > 1 - }, list) - result // $ExpectType number[] - }) - it('curried', () => { - const result = filter(x => { - x // $ExpectType number - return x > 1 - })(list) - result // $ExpectType number[] - }) -}) - -describe('R.filter with objects', () => { - it('happy', () => { - const result = filter((val, prop, origin) => { - val // $ExpectType number - prop // $ExpectType string - origin // $ExpectType Dictionary - - return val > 1 - }, obj) - result // $ExpectType Dictionary - }) - it('curried version requires second dummy type', () => { - const result = filter((val, prop, origin) => { - val // $ExpectType number - prop // $ExpectType string - origin // $ExpectType Dictionary - - return val > 1 - })(obj) - result // $ExpectType Dictionary - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#filter) - -### find - -```typescript - -find(predicate: (x: T) => boolean, list: T[]): T | undefined -``` - -It returns the first element of `list` that satisfy the `predicate`. - -If there is no such element, it returns `undefined`. - -```javascript -const predicate = x => R.type(x.foo) === 'Number' -const list = [{foo: 'bar'}, {foo: 1}] - -const result = R.find(predicate, list) -// => {foo: 1} -``` - -Try this R.find example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -find(predicate: (x: T) => boolean, list: T[]): T | undefined; -find(predicate: (x: T) => boolean): (list: T[]) => T | undefined; -``` - -
- -
- -R.find source - -```javascript -export function find(predicate, list){ - if (arguments.length === 1) return _list => find(predicate, _list) - - let index = 0 - const len = list.length - - while (index < len){ - const x = list[ index ] - if (predicate(x)){ - return x - } - - index++ - } -} -``` - -
- -
- -Tests - -```javascript -import { find } from './find.js' -import { propEq } from './propEq.js' - -const list = [ { a : 1 }, { a : 2 }, { a : 3 } ] - -test('happy', () => { - const fn = propEq(2, 'a') - expect(find(fn, list)).toEqual({ a : 2 }) -}) - -test('with curry', () => { - const fn = propEq(4, 'a') - expect(find(fn)(list)).toBeUndefined() -}) - -test('with empty list', () => { - expect(find(() => true, [])).toBeUndefined() -}) -``` - -
- -
- -TypeScript test - -```typescript -import {find} from 'rambda' - -const list = [1, 2, 3] - -describe('R.find', () => { - it('happy', () => { - const predicate = (x: number) => x > 2 - const result = find(predicate, list) - result // $ExpectType number | undefined - }) - it('curried', () => { - const predicate = (x: number) => x > 2 - const result = find(predicate)(list) - result // $ExpectType number | undefined - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#find) - -### findIndex - -```typescript - -findIndex(predicate: (x: T) => boolean, list: T[]): number -``` - -It returns the index of the first element of `list` satisfying the `predicate` function. - -If there is no such element, then `-1` is returned. - -```javascript -const predicate = x => R.type(x.foo) === 'Number' -const list = [{foo: 'bar'}, {foo: 1}] - -const result = R.findIndex(predicate, list) -// => 1 -``` - -Try this R.findIndex example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -findIndex(predicate: (x: T) => boolean, list: T[]): number; -findIndex(predicate: (x: T) => boolean): (list: T[]) => number; -``` - -
- -
- -R.findIndex source - -```javascript -export function findIndex(predicate, list){ - if (arguments.length === 1) return _list => findIndex(predicate, _list) - - const len = list.length - let index = -1 - - while (++index < len){ - if (predicate(list[ index ])){ - return index - } - } - - return -1 -} -``` - -
- -
- -Tests - -```javascript -import { findIndex } from './findIndex.js' -import { propEq } from './propEq.js' - -const list = [ { a : 1 }, { a : 2 }, { a : 3 } ] - -test('happy', () => { - expect(findIndex(propEq(2, 'a'), list)).toBe(1) - expect(findIndex(propEq(1, 'a'))(list)).toBe(0) - expect(findIndex(propEq(4, 'a'))(list)).toBe(-1) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {findIndex} from 'rambda' - -const list = [1, 2, 3] - -describe('R.findIndex', () => { - it('happy', () => { - const predicate = (x: number) => x > 2 - const result = findIndex(predicate, list) - result // $ExpectType number - }) - it('curried', () => { - const predicate = (x: number) => x > 2 - const result = findIndex(predicate)(list) - result // $ExpectType number - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#findIndex) - -### findLast - -```typescript - -findLast(fn: (x: T) => boolean, list: T[]): T | undefined -``` - -It returns the last element of `list` satisfying the `predicate` function. - -If there is no such element, then `undefined` is returned. - -```javascript -const predicate = x => R.type(x.foo) === 'Number' -const list = [{foo: 0}, {foo: 1}] - -const result = R.findLast(predicate, list) -// => {foo: 1} -``` - -Try this R.findLast example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -findLast(fn: (x: T) => boolean, list: T[]): T | undefined; -findLast(fn: (x: T) => boolean): (list: T[]) => T | undefined; -``` - -
- -
- -R.findLast source - -```javascript -export function findLast(predicate, list){ - if (arguments.length === 1) return _list => findLast(predicate, _list) - - let index = list.length - - while (--index >= 0){ - if (predicate(list[ index ])){ - return list[ index ] - } - } - - return undefined -} -``` - -
- -
- -Tests - -```javascript -import { findLast } from './findLast.js' - -test('happy', () => { - const result = findLast(x => x > 1, [ 1, 1, 1, 2, 3, 4, 1 ]) - expect(result).toBe(4) - - expect(findLast(x => x === 0, [ 0, 1, 1, 2, 3, 4, 1 ])).toBe(0) -}) - -test('with curry', () => { - expect(findLast(x => x > 1)([ 1, 1, 1, 2, 3, 4, 1 ])).toBe(4) -}) - -const obj1 = { x : 100 } -const obj2 = { x : 200 } -const a = [ 11, 10, 9, 'cow', obj1, 8, 7, 100, 200, 300, obj2, 4, 3, 2, 1, 0 ] -const even = function (x){ - return x % 2 === 0 -} -const gt100 = function (x){ - return x > 100 -} -const isStr = function (x){ - return typeof x === 'string' -} -const xGt100 = function (o){ - return o && o.x > 100 -} - -test('ramda 1', () => { - expect(findLast(even, a)).toBe(0) - expect(findLast(gt100, a)).toBe(300) - expect(findLast(isStr, a)).toBe('cow') - expect(findLast(xGt100, a)).toEqual(obj2) -}) - -test('ramda 2', () => { - expect(findLast(even, [ 'zing' ])).toBeUndefined() -}) - -test('ramda 3', () => { - expect(findLast(even, [ 2, 3, 5 ])).toBe(2) -}) - -test('ramda 4', () => { - expect(findLast(even, [])).toBeUndefined() -}) -``` - -
- -
- -TypeScript test - -```typescript -import {findLast} from 'rambda' - -const list = [1, 2, 3] - -describe('R.findLast', () => { - it('happy', () => { - const predicate = (x: number) => x > 2 - const result = findLast(predicate, list) - result // $ExpectType number | undefined - }) - it('curried', () => { - const predicate = (x: number) => x > 2 - const result = findLast(predicate)(list) - result // $ExpectType number | undefined - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#findLast) - -### findLastIndex - -```typescript - -findLastIndex(predicate: (x: T) => boolean, list: T[]): number -``` - -It returns the index of the last element of `list` satisfying the `predicate` function. - -If there is no such element, then `-1` is returned. - -```javascript -const predicate = x => R.type(x.foo) === 'Number' -const list = [{foo: 0}, {foo: 1}] - -const result = R.findLastIndex(predicate, list) -// => 1 -``` - -Try this R.findLastIndex example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -findLastIndex(predicate: (x: T) => boolean, list: T[]): number; -findLastIndex(predicate: (x: T) => boolean): (list: T[]) => number; -``` - -
- -
- -R.findLastIndex source - -```javascript -export function findLastIndex(fn, list){ - if (arguments.length === 1) return _list => findLastIndex(fn, _list) - - let index = list.length - - while (--index >= 0){ - if (fn(list[ index ])){ - return index - } - } - - return -1 -} -``` - -
- -
- -Tests - -```javascript -import { findLastIndex } from './findLastIndex.js' - -test('happy', () => { - const result = findLastIndex(x => x > 1, [ 1, 1, 1, 2, 3, 4, 1 ]) - - expect(result).toBe(5) - - expect(findLastIndex(x => x === 0, [ 0, 1, 1, 2, 3, 4, 1 ])).toBe(0) -}) - -test('with curry', () => { - expect(findLastIndex(x => x > 1)([ 1, 1, 1, 2, 3, 4, 1 ])).toBe(5) -}) - -const obj1 = { x : 100 } -const obj2 = { x : 200 } -const a = [ 11, 10, 9, 'cow', obj1, 8, 7, 100, 200, 300, obj2, 4, 3, 2, 1, 0 ] -const even = function (x){ - return x % 2 === 0 -} -const gt100 = function (x){ - return x > 100 -} -const isStr = function (x){ - return typeof x === 'string' -} -const xGt100 = function (o){ - return o && o.x > 100 -} - -test('ramda 1', () => { - expect(findLastIndex(even, a)).toBe(15) - expect(findLastIndex(gt100, a)).toBe(9) - expect(findLastIndex(isStr, a)).toBe(3) - expect(findLastIndex(xGt100, a)).toBe(10) -}) - -test('ramda 2', () => { - expect(findLastIndex(even, [ 'zing' ])).toBe(-1) -}) - -test('ramda 3', () => { - expect(findLastIndex(even, [ 2, 3, 5 ])).toBe(0) -}) - -test('ramda 4', () => { - expect(findLastIndex(even, [])).toBe(-1) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {findLastIndex} from 'rambda' - -const list = [1, 2, 3] - -describe('R.findLastIndex', () => { - it('happy', () => { - const predicate = (x: number) => x > 2 - const result = findLastIndex(predicate, list) - result // $ExpectType number - }) - it('curried', () => { - const predicate = (x: number) => x > 2 - const result = findLastIndex(predicate)(list) - result // $ExpectType number - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#findLastIndex) - -### flatten - -```typescript - -flatten(list: any[]): T[] -``` - -It deeply flattens an array. - -```javascript -const result = R.flatten([ - 1, - 2, - [3, 30, [300]], - [4] -]) -// => [ 1, 2, 3, 30, 300, 4 ] -``` - -Try this R.flatten example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -flatten(list: any[]): T[]; -``` - -
- -
- -R.flatten source - -```javascript -import { isArray } from './_internals/isArray.js' - -export function flatten(list, input){ - const willReturn = input === undefined ? [] : input - - for (let i = 0; i < list.length; i++){ - if (isArray(list[ i ])){ - flatten(list[ i ], willReturn) - } else { - willReturn.push(list[ i ]) - } - } - - return willReturn -} -``` - -
- -
- -Tests - -```javascript -import { flatten } from './flatten.js' - -test('happy', () => { - expect(flatten([ 1, 2, 3, [ [ [ [ [ 4 ] ] ] ] ] ])).toEqual([ 1, 2, 3, 4 ]) - - expect(flatten([ 1, [ 2, [ [ 3 ] ] ], [ 4 ] ])).toEqual([ 1, 2, 3, 4 ]) - - expect(flatten([ 1, [ 2, [ [ [ 3 ] ] ] ], [ 4 ] ])).toEqual([ 1, 2, 3, 4 ]) - - expect(flatten([ 1, 2, [ 3, 4 ], 5, [ 6, [ 7, 8, [ 9, [ 10, 11 ], 12 ] ] ] ])).toEqual([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]) -}) - -test('readme example', () => { - const result = flatten([ 1, 2, [ 3, 30, [ 300 ] ], [ 4 ] ]) - expect(result).toEqual([ 1, 2, 3, 30, 300, 4 ]) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {flatten} from 'rambda' - -describe('flatten', () => { - it('happy', () => { - const result = flatten([1, 2, [3, [4]]]) - result // $ExpectType number[] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#flatten) - -### flip - -```typescript - -flip(fn: (arg0: T, arg1: U) => TResult): (arg1: U, arg0?: T) => TResult -``` - -It returns function which calls `fn` with exchanged first and second argument. - -> :boom: Rambda's **flip** will throw if the arity of the input function is greater or equal to 5. - -```javascript -const subtractFlip = R.flip(R.subtract) - -const result = [ - subtractFlip(1,7), - R.subtract(1, 6) -] -// => [6, -6] -``` - -Try this R.flip example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -flip(fn: (arg0: T, arg1: U) => TResult): (arg1: U, arg0?: T) => TResult; -``` - -
- -
- -R.flip source - -```javascript -function flipFn(fn){ - return (...input) => { - if (input.length === 1){ - return holder => fn(holder, input[ 0 ]) - } else if (input.length === 2){ - return fn(input[ 1 ], input[ 0 ]) - } else if (input.length === 3){ - return fn( - input[ 1 ], input[ 0 ], input[ 2 ] - ) - } else if (input.length === 4){ - return fn( - input[ 1 ], input[ 0 ], input[ 2 ], input[ 3 ] - ) - } - - throw new Error('R.flip doesn\'t work with arity > 4') - } -} - -export function flip(fn){ - return flipFn(fn) -} -``` - -
- -
- -Tests - -```javascript -import { flip } from './flip.js' -import { subtract } from './subtract.js' -import { update } from './update.js' - -test('function with arity of 2', () => { - const subtractFlipped = flip(subtract) - - expect(subtractFlipped(1)(7)).toBe(6) - expect(subtractFlipped(1, 7)).toBe(6) - expect(subtractFlipped( - 1, 7, 9 - )).toBe(6) -}) - -test('function with arity of 3', () => { - const updateFlipped = flip(update) - - const result = updateFlipped( - 88, 0, [ 1, 2, 3 ] - ) - const curriedResult = updateFlipped(88, 0)([ 1, 2, 3 ]) - const tripleCurriedResult = updateFlipped(88)(0)([ 1, 2, 3 ]) - expect(result).toEqual([ 88, 2, 3 ]) - expect(curriedResult).toEqual([ 88, 2, 3 ]) - expect(tripleCurriedResult).toEqual([ 88, 2, 3 ]) -}) - -test('function with arity of 4', () => { - const testFunction = ( - a, b, c, d - ) => `${ a - b }==${ c - d }` - const testFunctionFlipped = flip(testFunction) - - const result = testFunction( - 1, 2, 3, 4 - ) - const flippedResult = testFunctionFlipped( - 2, 1, 3, 4 - ) - expect(result).toEqual(flippedResult) - expect(result).toBe('-1==-1') -}) - -test('function with arity of 5', () => { - const testFunction = ( - a, b, c, d, e - ) => `${ a - b }==${ c - d - e }` - const testFunctionFlipped = flip(testFunction) - - expect(() => - testFunctionFlipped( - 1, 2, 3, 4, 5 - )).toThrowErrorMatchingInlineSnapshot('"R.flip doesn\'t work with arity > 4"') -}) -``` - -
- -
- -TypeScript test - -```typescript -import * as R from 'ramda' -import {flip, subtract} from 'rambda' - -describe('R.flip', () => { - it('function with arity of 2', () => { - const subtractFlipped = flip(subtract) - const result = subtractFlipped(1, 7) - const curriedResult = subtractFlipped(1)(7) - curriedResult // $ExpectType number - - // This is wrong - // ============================================ - result // $ExpectType (y: number) => number - }) -}) - -describe('Ramda.flip', () => { - it('function with arity of 2', () => { - const subtractFlipped = R.flip(R.subtract) - const result = subtractFlipped(1, 7) - const curriedResult = subtractFlipped(1)(7) - result // $ExpectType number - curriedResult // $ExpectType number - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#flip) - -### forEach - -```typescript - -forEach(fn: Iterator, list: T[]): T[] -``` - -It applies `iterable` function over all members of `list` and returns `list`. - -> :boom: It works with objects, unlike `Ramda`. - -```javascript -const sideEffect = {} -const result = R.forEach( - x => sideEffect[`foo${x}`] = x -)([1, 2]) - -sideEffect // => {foo1: 1, foo2: 2} -result // => [1, 2] -``` - -Try this R.forEach example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -forEach(fn: Iterator, list: T[]): T[]; -forEach(fn: Iterator): (list: T[]) => T[]; -forEach(fn: ObjectIterator, list: Dictionary): Dictionary; -forEach(fn: ObjectIterator): (list: Dictionary) => Dictionary; -``` - -
- -
- -R.forEach source - -```javascript -import { isArray } from './_internals/isArray.js' -import { forEachObjIndexedFn } from './forEachObjIndexed.js' - -export function forEach(fn, iterable){ - if (arguments.length === 1) return _list => forEach(fn, _list) - if (iterable === undefined) return - - if (isArray(iterable)){ - let index = 0 - const len = iterable.length - - while (index < len){ - fn(iterable[ index ]) - index++ - } - } else return forEachObjIndexedFn(fn, iterable) - - return iterable -} -``` - -
- -
- -Tests - -```javascript -import { forEach } from './forEach.js' -import { type } from './type.js' - -test('happy', () => { - const sideEffect = {} - forEach(x => sideEffect[ `foo${ x }` ] = x + 10)([ 1, 2 ]) - - expect(sideEffect).toEqual({ - foo1 : 11, - foo2 : 12, - }) -}) - -test('iterate over object', () => { - const obj = { - a : 1, - b : [ 1, 2 ], - c : { d : 7 }, - f : 'foo', - } - const result = {} - const returned = forEach(( - val, prop, inputObj - ) => { - expect(type(inputObj)).toBe('Object') - result[ prop ] = `${ prop }-${ type(val) }` - })(obj) - - const expected = { - a : 'a-Number', - b : 'b-Array', - c : 'c-Object', - f : 'f-String', - } - - expect(result).toEqual(expected) - expect(returned).toEqual(obj) -}) - -test('with empty list', () => { - const list = [] - const result = forEach(x => x * x)(list) - - expect(result).toEqual(list) -}) - -test('with wrong input', () => { - const list = undefined - const result = forEach(x => x * x)(list) - - expect(result).toBeUndefined() -}) - -test('returns the input', () => { - const list = [ 1, 2, 3 ] - const result = forEach(x => x * x)(list) - - expect(result).toEqual(list) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {forEach} from 'rambda' - -const list = [1, 2, 3] -const obj = {a: 1, b: 2} - -describe('R.forEach with arrays', () => { - it('happy', () => { - const result = forEach(a => { - a // $ExpectType number - }, list) - result // $ExpectType number[] - }) - it('curried require an explicit typing', () => { - const result = forEach(a => { - a // $ExpectType number - })(list) - result // $ExpectType number[] - }) -}) - -describe('R.forEach with objects', () => { - it('happy', () => { - const result = forEach((a, b, c) => { - a // $ExpectType number - b // $ExpectType string - c // $ExpectType Dictionary - return `${a}` - }, obj) - result // $ExpectType Dictionary - }) - it('curried require an input typing and a dummy third typing', () => { - // Required in order all typings to work - const result = forEach((a, b, c) => { - a // $ExpectType number - b // $ExpectType string - c // $ExpectType Dictionary - })(obj) - result // $ExpectType Dictionary - }) - it('iterator without property', () => { - const result = forEach(a => { - a // $ExpectType number - }, obj) - result // $ExpectType Dictionary - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#forEach) - -### forEachObjIndexed - -```typescript - -forEachObjIndexed(fn: (value: T[keyof T], key: keyof T, obj: T) => void, obj: T): T -``` - -
- -All TypeScript definitions - -```typescript -forEachObjIndexed(fn: (value: T[keyof T], key: keyof T, obj: T) => void, obj: T): T; -forEachObjIndexed(fn: (value: T[keyof T], key: keyof T, obj: T) => void): (obj: T) => T; -``` - -
- -
- -R.forEachObjIndexed source - -```javascript -import { keys } from './_internals/keys.js' - -export function forEachObjIndexedFn(fn, obj){ - let index = 0 - const listKeys = keys(obj) - const len = listKeys.length - - while (index < len){ - const key = listKeys[ index ] - fn( - obj[ key ], key, obj - ) - index++ - } - - return obj -} - -export function forEachObjIndexed(fn, list){ - if (arguments.length === 1) return _list => forEachObjIndexed(fn, _list) - if (list === undefined) return - - return forEachObjIndexedFn(fn, list) -} -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#forEachObjIndexed) - -### fromPairs - -```typescript - -fromPairs(listOfPairs: ([number, V])[]): { [index: number]: V } -``` - -It transforms a `listOfPairs` to an object. - -```javascript -const listOfPairs = [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', [ 3, 4 ] ] ] -const expected = { - a : 1, - b : 2, - c : [ 3, 4 ], -} - -const result = R.fromPairs(listOfPairs) -// => `result` is equal to `expected` -``` - -Try this R.fromPairs example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -fromPairs(listOfPairs: ([number, V])[]): { [index: number]: V }; -fromPairs(listOfPairs: ([string, V])[]): { [index: string]: V }; -``` - -
- -
- -R.fromPairs source - -```javascript -export function fromPairs(listOfPairs){ - const toReturn = {} - listOfPairs.forEach(([ prop, value ]) => toReturn[ prop ] = value) - - return toReturn -} -``` - -
- -
- -Tests - -```javascript -import { fromPairs } from './fromPairs.js' - -const list = [ - [ 'a', 1 ], - [ 'b', 2 ], - [ 'c', [ 3, 4 ] ], -] -const expected = { - a : 1, - b : 2, - c : [ 3, 4 ], -} - -test('happy', () => { - expect(fromPairs(list)).toEqual(expected) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {fromPairs} from 'rambda' - -describe('R.fromPairs - require explicit type for input list', () => { - it('with string index', () => { - const list: [string, number][] = [ - ['a', 1], - ['b', 2], - ['c', 3], - ] - const result = fromPairs(list) - - result // $ExpectType { [index: string]: number; } - }) - it('with number index', () => { - const list: [number, string][] = [ - [10, 'foo'], - [20, 'bar'], - [30, 'baz'], - ] - const result = fromPairs(list) - - result // $ExpectType { [index: number]: string; } - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#fromPairs) - -### groupBy - -```typescript - -groupBy(fn: (a: T) => K): (list: T[]) => Partial> -``` - -It splits `list` according to a provided `groupFn` function and returns an object. - -```javascript -const list = [ 'a', 'b', 'aa', 'bb' ] -const groupFn = x => x.length - -const result = R.groupBy(groupFn, list) -// => { '1': ['a', 'b'], '2': ['aa', 'bb'] } -``` - -Try this R.groupBy example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -groupBy(fn: (a: T) => K): (list: T[]) => Partial>; -groupBy(fn: (a: T) => K, list: T[]): Partial>; -``` - -
- -
- -R.groupBy source - -```javascript -export function groupBy(groupFn, list){ - if (arguments.length === 1) return _list => groupBy(groupFn, _list) - - const result = {} - for (let i = 0; i < list.length; i++){ - const item = list[ i ] - const key = groupFn(item) - - if (!result[ key ]){ - result[ key ] = [] - } - - result[ key ].push(item) - } - - return result -} -``` - -
- -
- -Tests - -```javascript -import { groupBy } from './groupBy.js' -import { prop } from './prop.js' - -test('groupBy', () => { - const list = [ - { - age : 12, - name : 'john', - }, - { - age : 12, - name : 'jack', - }, - { - age : 24, - name : 'mary', - }, - { - age : 24, - name : 'steve', - }, - ] - const expectedResult = { - 12 : [ - { - age : 12, - name : 'john', - }, - { - age : 12, - name : 'jack', - }, - ], - 24 : [ - { - age : 24, - name : 'mary', - }, - { - age : 24, - name : 'steve', - }, - ], - } - - expect(groupBy(prop('age'))(list)).toEqual(expectedResult) - expect(groupBy(prop('age'), list)).toEqual(expectedResult) -}) -``` - -
- -
- -TypeScript test - -```typescript -import { groupBy, prop } from 'rambda'; - -interface Thing { - name: string; - position: string; -} - -const things = [ - { name: 'one', position: 'left' }, - { name: 'two', position: 'left' }, - { name: 'three', position: 'right' }, - { name: 'four', position: 'right' }, -]; - -describe('R.groupBy', () => { - it('happy', () => { - const groupByFn = (x: string) => String(x.length); - const list = ['foo', 'bar']; - - const result = groupBy(groupByFn, list); - result; // $ExpectType Partial> - - const curriedResult = groupBy(groupByFn)(list); - curriedResult; // $ExpectType Partial> - }); - it('with one explicit types', () => { - const groupByPosition = groupBy(prop('position')); - - const result = groupByPosition(things); - result; // $ExpectType Partial> - result[9]; // $ExpectType Thing[] | undefined - result.foo; // $ExpectType Thing[] | undefined - }); -}); -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#groupBy) - -### groupWith - -```typescript - -groupWith(compareFn: (x: T, y: T) => boolean): (input: T[]) => (T[])[] -``` - -It returns separated version of list or string `input`, where separation is done with equality `compareFn` function. - -```javascript -const isConsecutive = (x, y) => x === y -const list = [1, 2, 2, 1, 1, 2] - -const result = R.groupWith(isConsecutive, list) -// => [[1], [2,2], [1,1], [2]] -``` - -Try this R.groupWith example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -groupWith(compareFn: (x: T, y: T) => boolean): (input: T[]) => (T[])[]; -groupWith(compareFn: (x: T, y: T) => boolean, input: T[]): (T[])[]; -groupWith(compareFn: (x: T, y: T) => boolean, input: string): string[]; -``` - -
- -
- -R.groupWith source - -```javascript -import { cloneList } from './_internals/cloneList.js' -import { isArray } from './_internals/isArray.js' - -export function groupWith(compareFn, list){ - if (!isArray(list)) throw new TypeError('list.reduce is not a function') - - const clone = cloneList(list) - - if (list.length === 1) return [ clone ] - - const toReturn = [] - let holder = [] - - clone.reduce(( - prev, current, i - ) => { - if (i === 0) return current - - const okCompare = compareFn(prev, current) - const holderIsEmpty = holder.length === 0 - const lastCall = i === list.length - 1 - - if (okCompare){ - if (holderIsEmpty) holder.push(prev) - holder.push(current) - if (lastCall) toReturn.push(holder) - - return current - } - - if (holderIsEmpty){ - toReturn.push([ prev ]) - if (lastCall) toReturn.push([ current ]) - - return current - } - - toReturn.push(holder) - if (lastCall) toReturn.push([ current ]) - holder = [] - - return current - }, undefined) - - return toReturn -} -``` - -
- -
- -Tests - -```javascript -import { equals } from './equals.js' -import { groupWith } from './groupWith.js' - -test('issue is fixed', () => { - const result = groupWith(equals, [ 1, 2, 2, 3 ]) - const expected = [ [ 1 ], [ 2, 2 ], [ 3 ] ] - expect(result).toEqual(expected) -}) - -test('long list', () => { - const result = groupWith(equals, - [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 21, 21, 1, 2 ]) - - const expected = [ - [ 0 ], - [ 1, 1 ], - [ 2 ], - [ 3 ], - [ 5 ], - [ 8 ], - [ 13 ], - [ 21, 21, 21 ], - [ 1 ], - [ 2 ], - ] - expect(result).toEqual(expected) -}) - -test('readme example', () => { - const list = [ 4, 3, 6, 2, 2, 1 ] - - const result = groupWith((a, b) => a - b === 1, list) - const expected = [ [ 4, 3 ], [ 6 ], [ 2 ], [ 2, 1 ] ] - expect(result).toEqual(expected) -}) - -test('throw with string as input', () => { - expect(() => - groupWith(equals, 'Mississippi')).toThrowErrorMatchingInlineSnapshot('"list.reduce is not a function"') -}) - -const isConsecutive = function (a, b){ - return a + 1 === b -} - -test('fix coverage', () => { - expect(groupWith(isConsecutive, [ 1, 2, 3, 0 ])).toEqual([ [ 1, 2, 3 ], [ 0 ] ]) -}) - -test('from ramda 0', () => { - expect(groupWith(equals, [])).toEqual([]) - expect(groupWith(isConsecutive, [])).toEqual([]) -}) - -test('from ramda 1', () => { - expect(groupWith(isConsecutive, [ 4, 3, 2, 1 ])).toEqual([ - [ 4 ], - [ 3 ], - [ 2 ], - [ 1 ], - ]) -}) - -test('from ramda 2', () => { - expect(groupWith(isConsecutive, [ 1, 2, 3, 4 ])).toEqual([ [ 1, 2, 3, 4 ] ]) -}) - -test('from ramda 3', () => { - expect(groupWith(isConsecutive, [ 1, 2, 2, 3 ])).toEqual([ - [ 1, 2 ], - [ 2, 3 ], - ]) - expect(groupWith(isConsecutive, [ 1, 2, 9, 3, 4 ])).toEqual([ - [ 1, 2 ], - [ 9 ], - [ 3, 4 ], - ]) -}) - -test('list with single item', () => { - const result = groupWith(equals, [ 0 ]) - - const expected = [ [ 0 ] ] - expect(result).toEqual(expected) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {groupWith} from 'rambda' - -describe('R.groupWith', () => { - it('happy', () => { - const groupWithFn = (x: string, y: string) => x.length === y.length - const list = ['foo', 'bar', 'bazzz'] - - const result = groupWith(groupWithFn, list) - const curriedResult = groupWith(groupWithFn)(list) - result // $ExpectType string[][] - curriedResult // $ExpectType string[][] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#groupWith) - -### gt - -```typescript - -gt(x: T, y: U): boolean -``` - -```javascript -const result = [R.gt(2, 1), R.gt(2, 3)] -// => [true, false] -``` - -Try this R.gt example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -gt(x: T, y: U): boolean; -gt(x: T): (y: U) => boolean; -``` - -
- -
- -R.gt source - -```javascript -export function gt(a, b){ - if (arguments.length === 1) - return _b => gt(a, _b) - - return a > b -} -``` - -
- -
- -TypeScript test - -```typescript -import {gt} from 'rambda' - -describe('R.gt', () => { - it('happy', () => { - const result = gt(1, 2) - const curriedResult = gt(2)(3) - result // $ExpectType boolean - curriedResult // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#gt) - -### gte - -```typescript - -gte(x: T, y: U): boolean -``` - -```javascript -const result = [R.gte(2, 1), R.gte(2, 2), R.gte(2, 3)] -// => [true, true, false] -``` - -Try this R.gte example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -gte(x: T, y: U): boolean; -gte(x: T): (y: U) => boolean; -``` - -
- -
- -R.gte source - -```javascript -export function gte(a, b){ - if (arguments.length === 1) - return _b => gte(a, _b) - - return a >= b -} -``` - -
- -
- -TypeScript test - -```typescript -import {gte} from 'rambda' - -describe('R.gte', () => { - it('happy', () => { - const result = gte(1, 2) - const curriedResult = gte(2)(3) - result // $ExpectType boolean - curriedResult // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#gte) - -### has - -```typescript - -has(prop: string, obj: T): boolean -``` - -It returns `true` if `obj` has property `prop`. - -```javascript -const obj = {a: 1} - -const result = [ - R.has('a', Record), - R.has('b', Record) -] -// => [true, false] -``` - -Try this R.has example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -has(prop: string, obj: T): boolean; -has(prop: string): (obj: T) => boolean; -``` - -
- -
- -R.has source - -```javascript -export function has(prop, obj){ - if (arguments.length === 1) return _obj => has(prop, _obj) - - if (!obj) return false - - return obj.hasOwnProperty(prop) -} -``` - -
- -
- -Tests - -```javascript -import { has } from './has.js' - -test('happy', () => { - expect(has('a')({ a : 1 })).toBeTrue() - expect(has('b', { a : 1 })).toBeFalse() -}) - -test('with non-object', () => { - expect(has('a', undefined)).toBeFalse() - expect(has('a', null)).toBeFalse() - expect(has('a', true)).toBeFalse() - expect(has('a', '')).toBeFalse() - expect(has('a', /a/)).toBeFalse() -}) -``` - -
- -
- -TypeScript test - -```typescript -import {has} from 'rambda' - -describe('R.has', () => { - it('happy', () => { - const result = has('foo', {a: 1}) - const curriedResult = has('bar')({a: 1}) - result // $ExpectType boolean - curriedResult // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#has) - -### hasIn - -```typescript - -hasIn(searchProperty: string): (obj: T) => boolean -``` - -```javascript -const result = R.hasIn('a', {a: 1}) -// => true -``` - -Try this R.hasIn example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -hasIn(searchProperty: string): (obj: T) => boolean; -hasIn(searchProperty: string, obj: T): boolean; -``` - -
- -
- -R.hasIn source - -```javascript -import { propFn } from './prop.js'; - -export function hasIn(searchProperty, obj) { - if (arguments.length === 1) { - return (_obj) => hasIn(searchProperty, _obj); - } - - return propFn(searchProperty, obj) !== undefined; -} -``` - -
- -
- -Tests - -```javascript -import { hasIn as hasInRamda } from 'ramda' - -import { hasIn } from './hasIn.js' - -const fred = { - age : 23, - name : 'Fred', -} -const anon = { age : 99 } - -test('returns a function that checks the appropriate property', () => { - const nm = hasIn('name') - expect(typeof nm).toBe('function') - expect(nm(fred)).toBe(true) - expect(nm(anon)).toBe(false) -}) - -test('checks properties from the prototype chain', () => { - function Person(){} - Person.prototype.age = function (){} - - const bob = new Person() - expect(hasIn('age', bob)).toBe(true) -}) - -test('works properly when called with two arguments', () => { - expect(hasIn('name', fred)).toBe(true) - expect(hasIn('name', anon)).toBe(false) -}) - -test('returns false when non-existent object', () => { - expect(hasIn('name', null)).toBe(false) - expect(hasIn('name', undefined)).toBe(false) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#hasIn) - -### hasPath - -```typescript - -hasPath( - path: string | string[], - input: object -): boolean -``` - -It will return true, if `input` object has truthy `path`(calculated with `R.path`). - -```javascript -const path = 'a.b' -const pathAsArray = ['a', 'b'] -const obj = {a: {b: []}} - -const result = [ - R.hasPath(path, Record), - R.hasPath(pathAsArray, Record), - R.hasPath('a.c', Record), -] -// => [true, true, false] -``` - -Try this R.hasPath example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -hasPath( - path: string | string[], - input: object -): boolean; -hasPath( - path: string | string[] -): (input: object) => boolean; -``` - -
- -
- -R.hasPath source - -```javascript -import { path } from './path.js' - -export function hasPath(pathInput, obj){ - if (arguments.length === 1){ - return objHolder => hasPath(pathInput, objHolder) - } - - return path(pathInput, obj) !== undefined -} -``` - -
- -
- -Tests - -```javascript -import { hasPath } from './hasPath.js' - -test('when true', () => { - const path = 'a.b' - const obj = { a : { b : [] } } - - const result = hasPath(path)(obj) - const expectedResult = true - - expect(result).toEqual(expectedResult) -}) - -test('when false', () => { - const path = 'a.b' - const obj = {} - - const result = hasPath(path, obj) - const expectedResult = false - - expect(result).toEqual(expectedResult) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {hasPath} from 'rambda' - -describe('R.hasPath', () => { - it('string path', () => { - const obj = {a: {b: 1}} - const result = hasPath('a.b', obj) - const curriedResult = hasPath('a.c')(obj) - result // $ExpectType boolean - curriedResult // $ExpectType boolean - }) - it('array path', () => { - const obj = {a: {b: 1}} - const result = hasPath(['a', 'b'], obj) - const curriedResult = hasPath(['a', 'c'])(obj) - result // $ExpectType boolean - curriedResult // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#hasPath) - -### head - -```typescript - -head(str: string): string -``` - -It returns the first element of list or string `input`. It returns `undefined` if array has length of 0. - -```javascript -const result = [ - R.head([1, 2, 3]), - R.head('foo') -] -// => [1, 'f'] -``` - -Try this R.head example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -head(str: string): string; -head(str: ''): undefined; -head(list: readonly[]): undefined; -head(list: never[]): undefined; -head(array: T): FirstArrayElement -head(array: T): FirstArrayElement -``` - -
- -
- -R.head source - -```javascript -export function head(listOrString){ - if (typeof listOrString === 'string') return listOrString[ 0 ] || '' - - return listOrString[ 0 ] -} -``` - -
- -
- -Tests - -```javascript -import { head } from './head.js' - -test('head', () => { - expect(head([ 'fi', 'fo', 'fum' ])).toBe('fi') - expect(head([])).toBeUndefined() - expect(head('foo')).toBe('f') - expect(head('')).toBe('') -}) -``` - -
- -
- -TypeScript test - -```typescript -import { - emptyList, - emptyString, - mixedList, - mixedListConst, - numberList, - numberListConst, - string, -} from '_internals/typescriptTestUtils' -import {head, last} from 'rambda' - -describe('R.head', () => { - it('string', () => { - head(string) // $ExpectType string - last(string) // $ExpectType string - }) - it('empty string', () => { - head(emptyString) // $ExpectType undefined - last(emptyString) // $ExpectType undefined - }) - it('array', () => { - head(numberList) // $ExpectType number - head(numberListConst) // $ExpectType 1 - - last(numberList) // $ExpectType number - last(numberListConst) // $ExpectType 3 - }) - it('empty array', () => { - const list = [] as const - head(emptyList) // $ExpectType undefined - head(list) // $ExpectType undefined - last(emptyList) // $ExpectType undefined - last(list) // $ExpectType undefined - }) - - it('mixed', () => { - head(mixedList) // $ExpectType string | number - head(mixedListConst) // $ExpectType 1 - last(mixedList) // $ExpectType string | number - last(mixedListConst) // $ExpectType "bar" - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#head) - -### identical - -```typescript - -identical(x: T, y: T): boolean -``` - -It returns `true` if its arguments `a` and `b` are identical. - -Otherwise, it returns `false`. - -> :boom: Values are identical if they reference the same memory. `NaN` is identical to `NaN`; `0` and `-0` are not identical. - -```javascript -const objA = {a: 1}; -const objB = {a: 1}; -R.identical(objA, objA); // => true -R.identical(objA, objB); // => false -R.identical(1, 1); // => true -R.identical(1, '1'); // => false -R.identical([], []); // => false -R.identical(0, -0); // => false -R.identical(NaN, NaN); // => true -``` - -Try this R.identical example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -identical(x: T, y: T): boolean; -identical(x: T): (y: T) => boolean; -``` - -
- -
- -R.identical source - -```javascript -import { objectIs } from './_internals/objectIs.js' - -export function identical(a, b){ - if (arguments.length === 1) return _b => identical(a, _b) - - return objectIs(a, b) -} -``` - -
- -
- -Tests - -```javascript -import { F, T } from '../rambda.js' -import { identical } from './identical.js' - -test('r.F and R.T', () => { - expect(F()).toBeFalse() - expect(T()).toBeTrue() -}) - -test('identical', () => { - const a = { a : 1 } - const b = { a : 1 } - const c = { - a : 1, - b : 2, - } - - expect(identical(100)(100)).toBeTrue() - expect(identical(100, '100')).toBeFalse() - expect(identical('string', 'string')).toBeTrue() - expect(identical([], [])).toBeFalse() - expect(identical(a, a)).toBeTrue() - expect(identical(a, b)).toBeFalse() - expect(identical(a, c)).toBeFalse() - expect(identical(undefined, undefined)).toBeTrue() - expect(identical(null, undefined)).toBeFalse() -}) -``` - -
- -
- -TypeScript test - -```typescript -import {identical} from 'rambda' - -describe('R.identical', () => { - it('happy', () => { - const result = identical(4, 1) - const curriedResult = identical(4)(1) - result // $ExpectType boolean - curriedResult // $ExpectType boolean - }) - it('with object', () => { - const result = identical({a: 1}, {b: 2}) - result // $ExpectType boolean - identical({a: 1}, {b: 2}) - - // @ts-expect-error - identical({a: 1})({b: 2}) - }) - it('with object - explicit type', () => { - interface Foo { - a: number, - } - identical({a: 1}, {a: 2}) - // @ts-expect-error - identical({a: 1}, {b: 2}) - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#identical) - -### identity - -```typescript - -identity(input: T): T -``` - -It just passes back the supplied `input` argument. - -> :boom: Logic - -```javascript -R.identity(7) // => 7 -``` - -Try this R.identity example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -identity(input: T): T; -``` - -
- -
- -R.identity source - -```javascript -export function identity(x){ - return x -} -``` - -
- -
- -Tests - -```javascript -import { identity } from './identity.js' - -test('happy', () => { - expect(identity(7)).toBe(7) - expect(identity(true)).toBeTrue() - expect(identity({ a : 1 })).toEqual({ a : 1 }) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {identity} from 'rambda' - -describe('R.identity', () => { - it('happy', () => { - const result = identity(4) - result // $ExpectType 4 - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#identity) - -### ifElse - -```typescript - -ifElse( - pred: (a: T) => a is TFiltered, - onTrue: (a: TFiltered) => TOnTrueResult, - onFalse: (a: Exclude) => TOnFalseResult, -): (a: T) => TOnTrueResult | TOnFalseResult -``` - -It expects `condition`, `onTrue` and `onFalse` functions as inputs and it returns a new function with example name of `fn`. - -When `fn`` is called with `input` argument, it will return either `onTrue(input)` or `onFalse(input)` depending on `condition(input)` evaluation. - -```javascript -const fn = R.ifElse( - x => x>10, - x => x*2, - x => x*10 -) - -const result = [ fn(8), fn(18) ] -// => [80, 36] -``` - -Try this R.ifElse example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -ifElse( - pred: (a: T) => a is TFiltered, - onTrue: (a: TFiltered) => TOnTrueResult, - onFalse: (a: Exclude) => TOnFalseResult, -): (a: T) => TOnTrueResult | TOnFalseResult; -ifElse(fn: (...args: TArgs) => boolean, onTrue: (...args: TArgs) => TOnTrueResult, onFalse: (...args: TArgs) => TOnFalseResult): (...args: TArgs) => TOnTrueResult | TOnFalseResult; -``` - -
- -
- -R.ifElse source - -```javascript -import { curry } from './curry.js' - -function ifElseFn( - condition, onTrue, onFalse -){ - return (...input) => { - const conditionResult = - typeof condition === 'boolean' ? condition : condition(...input) - if (Boolean(conditionResult) ){ - return onTrue(...input) - } - - return onFalse(...input) - } -} - -export const ifElse = curry(ifElseFn) -``` - -
- -
- -Tests - -```javascript -import { always } from './always.js' -import { has } from './has.js' -import { identity } from './identity.js' -import { ifElse } from './ifElse.js' -import { prop } from './prop.js' -import * as R from 'ramda' - -const condition = has('foo') -const v = function (a){ - return typeof a === 'number' -} -const t = function (a){ - return a + 1 -} -const ifFn = x => prop('foo', x).length -const elseFn = () => false - -test('happy', () => { - const fn = ifElse(condition, ifFn)(elseFn) - - expect(fn({ foo : 'bar' })).toBe(3) - expect(fn({ fo : 'bar' })).toBeFalse() -}) - -test('ramda spec', () => { - const ifIsNumber = ifElse(v) - expect(ifIsNumber(t, identity)(15)).toBe(16) - expect(ifIsNumber(t, identity)('hello')).toBe('hello') -}) - -test('pass all arguments', () => { - const identity = function (a){ - return a - } - const v = function (){ - return true - } - const onTrue = function (a, b){ - expect(a).toBe(123) - expect(b).toBe('abc') - } - ifElse( - v, onTrue, identity - )(123, 'abc') -}) - -test('accept constant as condition', () => { - const fn = ifElse(true)(always(true))(always(false)) - - expect(fn()).toBeTrue() -}) - -test('accept constant as condition - case 2', () => { - const fn = ifElse( - false, always(true), always(false) - ) - - expect(fn()).toBeFalse() -}) - -test('curry 1', () => { - const fn = ifElse(condition, ifFn)(elseFn) - - expect(fn({ foo : 'bar' })).toBe(3) - expect(fn({ fo : 'bar' })).toBeFalse() -}) - -test('curry 2', () => { - const fn = ifElse(condition)(ifFn)(elseFn) - - expect(fn({ foo : 'bar' })).toBe(3) - expect(fn({ fo : 'bar' })).toBeFalse() -}) - -test('simple arity of 1', () => { - const condition = x => x > 5 - const onTrue = x => x + 1 - const onFalse = x => x + 10 - const result = ifElse( - condition, onTrue, onFalse - )(1) - expect(result).toBe(11) -}) - -test('simple arity of 2', () => { - const condition = (x, y) => x + y > 5 - const onTrue = (x, y) => x + y + 1 - const onFalse = (x, y) => x + y + 10 - const result = ifElse( - condition, onTrue, onFalse - )(1, 10) - expect(result).toBe(12) -}) - -test('bug 750', () => { - const value = 34; - - let result = ifElse( - R.identity, - R.always('true'), - R.always('false') - )(value) - expect(result).toBe('true') -}) -``` - -
- -
- -TypeScript test - -```typescript -import {ifElse} from 'rambda' - -describe('R.ifElse', () => { - it('happy', () => { - const condition = (x: number) => x > 5 - const onTrue = (x: number) => `foo${x}` - const onFalse = (x: number) => `bar${x}` - const fn = ifElse(condition, onTrue, onFalse) - fn // $ExpectType (x: number) => string - const result = fn(3) - result // $ExpectType string - }) - it('arity of 2', () => { - const condition = (x: number, y: string) => x + y.length > 5 - const onTrue = (x: number, y: string) => `foo${x}-${y}` - const onFalse = (x: number, y: string) => `bar${x}-${y}` - const fn = ifElse(condition, onTrue, onFalse) - fn // $ExpectType (x: number, y: string) => string - const result = fn(3, 'hello') - result // $ExpectType string - }) - test('DefinitelyTyped#59291', () => { - const getLengthIfStringElseDouble = ifElse( - (a: string | number): a is string => true, - a => a.length, - a => a * 2 - ) - - getLengthIfStringElseDouble('foo') // $ExpectType number - getLengthIfStringElseDouble(3) // $ExpectType number - const result = ifElse( - (a: { - foo?: string, - bar: number | string, - }): a is {foo: string, bar: string} => true, - (a): [string, string] => [a.foo, a.bar], - (a): [string | undefined, string | number] => [a.foo, a.bar] - ) - result // $ExpectType (a: { foo?: string | undefined; bar: string | number; }) => [string, string] | [string | undefined, string | number] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#ifElse) - -### inc - -```typescript - -inc(x: number): number -``` - -It increments a number. - -```javascript -R.inc(1) // => 2 -``` - -Try this R.inc example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -inc(x: number): number; -``` - -
- -
- -R.inc source - -```javascript -export const inc = x => x + 1 -``` - -
- -
- -Tests - -```javascript -import { inc } from './inc.js' - -test('happy', () => { - expect(inc(1)).toBe(2) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#inc) - -### includes - -```typescript - -includes(valueToFind: T, input: string): boolean -``` - -If `input` is string, then this method work as native `String.includes`. - -If `input` is array, then `R.equals` is used to define if `valueToFind` belongs to the list. - -```javascript -const result = [ - R.includes('oo', 'foo'), - R.includes({a: 1}, [{a: 1}]) -] -// => [true, true ] -``` - -Try this R.includes example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -includes(valueToFind: T, input: string): boolean; -includes(valueToFind: T): (input: string) => boolean; -includes(valueToFind: T, input: T[]): boolean; -includes(valueToFind: T): (input: T[]) => boolean; -``` - -
- -
- -R.includes source - -```javascript -import { isArray } from './_internals/isArray.js' -import { _indexOf } from './equals.js' - -export function includes(valueToFind, iterable){ - if (arguments.length === 1) - return _iterable => includes(valueToFind, _iterable) - if (typeof iterable === 'string'){ - return iterable.includes(valueToFind) - } - if (!iterable){ - throw new TypeError(`Cannot read property \'indexOf\' of ${ iterable }`) - } - if (!isArray(iterable)) return false - - return _indexOf(valueToFind, iterable) > -1 -} -``` - -
- -
- -Tests - -```javascript -import { includes as includesRamda } from 'ramda' - -import { includes } from './includes.js' - -test('with string as iterable', () => { - const str = 'foo bar' - - expect(includes('bar')(str)).toBeTrue() - expect(includesRamda('bar')(str)).toBeTrue() - expect(includes('never', str)).toBeFalse() - expect(includesRamda('never', str)).toBeFalse() -}) - -test('with array as iterable', () => { - const arr = [ 1, 2, 3 ] - - expect(includes(2)(arr)).toBeTrue() - expect(includesRamda(2)(arr)).toBeTrue() - - expect(includes(4, arr)).toBeFalse() - expect(includesRamda(4, arr)).toBeFalse() -}) - -test('with list of objects as iterable', () => { - const arr = [ { a : 1 }, { b : 2 }, { c : 3 } ] - - expect(includes({ c : 3 }, arr)).toBeTrue() - expect(includesRamda({ c : 3 }, arr)).toBeTrue() -}) - -test('with NaN', () => { - const result = includes(NaN, [ NaN ]) - const ramdaResult = includesRamda(NaN, [ NaN ]) - expect(result).toBeTrue() - expect(ramdaResult).toBeTrue() -}) - -test('with wrong input that does not throw', () => { - const result = includes(1, /foo/g) - const ramdaResult = includesRamda(1, /foo/g) - expect(result).toBeFalse() - expect(ramdaResult).toBeFalse() -}) - -test('throws on wrong input - match ramda behaviour', () => { - expect(() => includes(2, null)).toThrowWithMessage(TypeError, - 'Cannot read property \'indexOf\' of null') - expect(() => includesRamda(2, null)).toThrowWithMessage(TypeError, - 'Cannot read properties of null (reading \'indexOf\')') - expect(() => includes(2, undefined)).toThrowWithMessage(TypeError, - 'Cannot read property \'indexOf\' of undefined') - expect(() => includesRamda(2, undefined)).toThrowWithMessage(TypeError, - 'Cannot read properties of undefined (reading \'indexOf\')') -}) -``` - -
- -
- -TypeScript test - -```typescript -import {includes} from 'rambda' - -const list = [{a: {b: '1'}}, {a: {c: '2'}}, {a: {b: '3'}}] - -describe('R.includes', () => { - it('happy', () => { - const result = includes({a: {b: '1'}}, list) - result // $ExpectType boolean - const result2 = includes('oo', ['f', 'oo']) - result2 // $ExpectType boolean - }) - it('with string', () => { - const str = 'foo' as 'foo' | 'bar' - const result = includes('oo', str) - const curriedResult = includes('oo')(str) - - result // $ExpectType boolean - curriedResult // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#includes) - -### indexBy - -```typescript - -indexBy(condition: (key: T) => K, list: T[]): { [key in K]: T } -``` - -It generates object with properties provided by `condition` and values provided by `list` array. - -If `condition` is a function, then all list members are passed through it. - -If `condition` is a string, then all list members are passed through `R.path(condition)`. - -```javascript -const list = [ {id: 10}, {id: 20} ] - -const withFunction = R.indexBy( - x => x.id, - list -) -const withString = R.indexBy( - 'id', - list -) -const result = [ - withFunction, - R.equals(withFunction, withString) -] -// => [ { 10: {id: 10}, 20: {id: 20} }, true ] -``` - -Try this R.indexBy example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -indexBy(condition: (key: T) => K, list: T[]): { [key in K]: T }; -indexBy(condition: (key: T) => K, list: T[]): { [key in NonNullable]?: T }; -indexBy(condition: (key: T) => K): (list: T[]) => { [key in K]: T }; -indexBy(condition: (key: T) => K | undefined): (list: T[]) => { [key in NonNullable]?: T }; -indexBy(condition: string, list: T[]): { [key: string]: T }; -indexBy(condition: string): (list: T[]) => { [key: string]: T }; -``` + return curryN(fn.length, (...args) => fn.apply(thisObj, args)) +} +```
-R.indexBy source +Tests ```javascript -import { path } from './path.js' - -function indexByPath(pathInput, list){ - const toReturn = {} - for (let i = 0; i < list.length; i++){ - const item = list[ i ] - toReturn[ path(pathInput, item) ] = item - } +import { bind } from './bind.js' - return toReturn +function Foo(x){ + this.x = x } - -export function indexBy(condition, list){ - if (arguments.length === 1){ - return _list => indexBy(condition, _list) - } - - if (typeof condition === 'string'){ - return indexByPath(condition, list) - } - - const toReturn = {} - for (let i = 0; i < list.length; i++){ - const item = list[ i ] - toReturn[ condition(item) ] = item - } - - return toReturn +function add(x){ + return this.x + x } -``` - -
- -
- -Tests - -```javascript -import { indexBy } from './indexBy.js' -import { prop } from './prop.js' - -test('happy', () => { - const list = [ - { id : 1 }, - { - id : 1, - a : 2, - }, - { id : 2 }, - { id : 10 }, - { id : 'a' }, - ] - - expect(indexBy(prop('id'))(list)).toEqual({ - 1 : { - id : 1, - a : 2, - }, - 2 : { id : 2 }, - 10 : { id : 10 }, - a : { id : 'a' }, - }) -}) - -test('with string as condition', () => { - const list = [ { id : 1 }, { id : 2 }, { id : 10 }, { id : 'a' } ] - const standardResult = indexBy(obj => obj.id, list) - const suggestionResult = indexBy('id', list) - - expect(standardResult).toEqual(suggestionResult) -}) - -test('with string - bad path', () => { - const list = [ - { - a : { - b : 1, - c : 2, - }, - }, - { a : { c : 4 } }, - {}, - { - a : { - b : 10, - c : 20, - }, - }, - ] - - const result = indexBy('a.b', list) - const expected = { - 1 : { - a : { - b : 1, - c : 2, - }, - }, - 10 : { - a : { - b : 10, - c : 20, - }, - }, - undefined : {}, - } - - expect(result).toEqual(expected) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {indexBy} from 'rambda' - -const list = [{a: {b: '1'}}, {a: {c: '2'}}, {a: {b: '3'}}] - -describe('indexBy', () => { - it('happy', () => { - const result = indexBy(x => x.a.b, list) - const curriedResult = indexBy(x => x.a.b)(list) - result.foo?.a.b // $ExpectType string | undefined - curriedResult // $ExpectType { [x: string]: any; } - }) - - it('with string', () => { - const result = indexBy('a.b', list) - const curriedResult = indexBy('a.b')(list) - result.foo?.a.b // $ExpectType string | undefined - curriedResult // $ExpectType { [key: string]: any; } - }) - - it('with interface', () => { - interface Foo { - a: string, - } - const interfaceList = [{a: 'foo'}, {a: 'bar'}] - const result = indexBy(x => { - x.a // $ExpectType string - return x.a - }, interfaceList) - const curriedResult = indexBy(x => { - x.a // $ExpectType string - return x.a - })(interfaceList) - result // $ExpectType { [x: string]: Foo; } - curriedResult // $ExpectType { [x: string]: Foo; } - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#indexBy) - -### indexOf - -```typescript - -indexOf(valueToFind: T, list: T[]): number -``` - -It returns the index of the first element of `list` equals to `valueToFind`. - -If there is no such element, it returns `-1`. - -> :boom: It uses `R.equals` for list of objects/arrays or native `indexOf` for any other case. - -```javascript -const list = [0, 1, 2, 3] - -const result = [ - R.indexOf(2, list), - R.indexOf(0, list) -] -// => [2, -1] -``` - -Try this R.indexOf example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -indexOf(valueToFind: T, list: T[]): number; -indexOf(valueToFind: T): (list: T[]) => number; -``` - -
- -
- -R.indexOf source - -```javascript -import { _indexOf } from './equals.js' - -export function indexOf(valueToFind, list){ - if (arguments.length === 1){ - return _list => _indexOf(valueToFind, _list) - } - - return _indexOf(valueToFind, list) +function Bar(x, y){ + this.x = x + this.y = y +} +Bar.prototype = new Foo() +Bar.prototype.getX = function (){ + return 'prototype getX' } -``` - -
- -
- -Tests - -```javascript -import { indexOf as indexOfRamda } from 'ramda' - -import { compareCombinations } from './_internals/testUtils.js' -import { indexOf } from './indexOf.js' -test('with NaN', () => { - expect(indexOf(NaN, [ NaN ])).toBe(0) +test('returns a function', () => { + expect(typeof bind(add)(Foo)).toBe('function') }) -test('will throw with bad input', () => { - expect(indexOfRamda([], true)).toBe(-1) - expect(() => indexOf([], true)).toThrow() +test('returns a function bound to the specified context object', () => { + const f = new Foo(12) + function isFoo(){ + return this instanceof Foo + } + const isFooBound = bind(isFoo, f) + expect(isFoo()).toBeFalse() + expect(isFooBound()).toBeTrue() }) -test('without list of objects - no R.equals', () => { - expect(indexOf(3, [ 1, 2, 3, 4 ])).toBe(2) - expect(indexOf(10)([ 1, 2, 3, 4 ])).toBe(-1) +test('works with built-in types', () => { + const abc = bind(String.prototype.toLowerCase, 'ABCDEFG') + expect(typeof abc).toBe('function') + expect(abc()).toBe('abcdefg') }) -test('list of objects uses R.equals', () => { - const listOfObjects = [ { a : 1 }, { b : 2 }, { c : 3 } ] - expect(indexOf({ c : 4 }, listOfObjects)).toBe(-1) - expect(indexOf({ c : 3 }, listOfObjects)).toBe(2) +test('works with user-defined types', () => { + const f = new Foo(12) + function getX(){ + return this.x + } + const getXFooBound = bind(getX, f) + expect(getXFooBound()).toBe(12) }) -test('list of arrays uses R.equals', () => { - const listOfLists = [ [ 1 ], [ 2, 3 ], [ 2, 3, 4 ], [ 2, 3 ], [ 1 ], [] ] - expect(indexOf([], listOfLists)).toBe(5) - expect(indexOf([ 1 ], listOfLists)).toBe(0) - expect(indexOf([ 2, 3, 4 ], listOfLists)).toBe(2) - expect(indexOf([ 2, 3, 5 ], listOfLists)).toBe(-1) +test('works with plain objects', () => { + const pojso = { x : 100 } + function incThis(){ + return this.x + 1 + } + const incPojso = bind(incThis, pojso) + expect(typeof incPojso).toBe('function') + expect(incPojso()).toBe(101) }) -test('with string as iterable', () => { - expect(() => indexOf('a', 'abc')).toThrowWithMessage(Error, - 'Cannot read property \'indexOf\' of abc') - expect(indexOfRamda('a', 'abc')).toBe(0) +test('does not interfere with existing object methods', () => { + const b = new Bar('a', 'b') + function getX(){ + return this.x + } + const getXBarBound = bind(getX, b) + expect(b.getX()).toBe('prototype getX') + expect(getXBarBound()).toBe('a') }) -export const possibleTargets = [ - x => x > 2, - /foo/, - 'foo', - { a : 1 }, - true, - 3, - null, - /bar/g, - NaN, - undefined, - 4, - [], - [ [] ], - [ [ 1 ], [ 2 ] ], - { a : 1 }, - { a : 2 }, - Promise.resolve(1), -] - -export const possibleIterables = [ - [ - 1, - 2, - new Boolean(true), - false, - true, - new String('foo'), - new Number(3), - null, - undefined, - ], - [ /foo/g, /bar/, /bar/g, NaN ], - [ 1, 2, 3 ], - [ 1, [ [], [] ] ], - [ { a : 3 }, { a : 2 }, { a : 1 } ], - {}, - null, - undefined, - true, - 'foo', -] +test('preserves arity', () => { + const f0 = function (){ + return 0 + } + const f1 = function (a){ + return a + } + const f2 = function (a, b){ + return a + b + } + const f3 = function ( + a, b, c + ){ + return a + b + c + } -describe('brute force', () => { - compareCombinations({ - fn : indexOf, - fnRamda : indexOfRamda, - firstInput : possibleTargets, - secondInput : possibleIterables, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 34, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 51, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 170, - } - `) - }, - }) + expect(bind(f0, {})).toHaveLength(0) + expect(bind(f1, {})).toHaveLength(1) + expect(bind(f2, {})).toHaveLength(2) + expect(bind(f3, {})).toHaveLength(3) }) ``` @@ -12640,69 +2222,62 @@ describe('brute force', () => { TypeScript test ```typescript -import {indexOf} from 'rambda' +import {bind} from 'rambda' -describe('R.indexOf', () => { +class Foo {} +function isFoo(this: T): boolean { + return this instanceof Foo +} + +describe('R.bind', () => { it('happy', () => { - const list = [1, 2, 3] - const result = indexOf(1, list) - const curriedResult = indexOf(1)(list) + const foo = new Foo() + const result = bind(isFoo, foo)() - result // $ExpectType number - curriedResult // $ExpectType number + result // $ExpectType boolean }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#indexOf) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#bind) -### init +### both ```typescript -init(input: T): T extends readonly [...infer U, any] ? U : [...T] +both(pred1: Pred, pred2: Pred): Pred ``` -It returns all but the last element of list or string `input`. +It returns a function with `input` argument. -```javascript -const result = [ - R.init([1, 2, 3]) , - R.init('foo') // => 'fo' -] -// => [[1, 2], 'fo'] -``` +This function will return `true`, if both `firstCondition` and `secondCondition` return `true` when `input` is passed as their argument. -Try this R.init example in Rambda REPL +Try this R.both example in Rambda REPL
All TypeScript definitions ```typescript -init(input: T): T extends readonly [...infer U, any] ? U : [...T]; -init(input: string): string; +both(pred1: Pred, pred2: Pred): Pred; +both(pred1: Predicate, pred2: Predicate): Predicate; +both(pred1: Predicate): (pred2: Predicate) => Predicate; +both(pred1: Pred): (pred2: Pred) => Pred; ```
-R.init source +R.both source ```javascript -import baseSlice from './_internals/baseSlice.js' - -export function init(listOrString){ - if (typeof listOrString === 'string') return listOrString.slice(0, -1) +export function both(f, g){ + if (arguments.length === 1) return _g => both(f, _g) - return listOrString.length ? - baseSlice( - listOrString, 0, -1 - ) : - [] + return (...input) => f(...input) && g(...input) } ``` @@ -12713,21 +2288,47 @@ export function init(listOrString){ Tests ```javascript -import { init } from './init.js' +import { both } from './both.js' -test('with array', () => { - expect(init([ 1, 2, 3 ])).toEqual([ 1, 2 ]) - expect(init([ 1, 2 ])).toEqual([ 1 ]) - expect(init([ 1 ])).toEqual([]) - expect(init([])).toEqual([]) - expect(init([])).toEqual([]) - expect(init([ 1 ])).toEqual([]) +const firstFn = val => val > 0 +const secondFn = val => val < 10 + +test('with curry', () => { + expect(both(firstFn)(secondFn)(17)).toBeFalse() }) -test('with string', () => { - expect(init('foo')).toBe('fo') - expect(init('f')).toBe('') - expect(init('')).toBe('') +test('without curry', () => { + expect(both(firstFn, secondFn)(7)).toBeTrue() +}) + +test('with multiple inputs', () => { + const between = function ( + a, b, c + ){ + return a < b && b < c + } + const total20 = function ( + a, b, c + ){ + return a + b + c === 20 + } + const fn = both(between, total20) + expect(fn( + 5, 7, 8 + )).toBeTrue() +}) + +test('skip evaluation of the second expression', () => { + let effect = 'not evaluated' + const F = function (){ + return false + } + const Z = function (){ + effect = 'Z got evaluated' + } + both(F, Z)() + + expect(effect).toBe('not evaluated') }) ``` @@ -12738,488 +2339,334 @@ test('with string', () => { TypeScript test ```typescript -import {init} from 'rambda' - -describe('R.init', () => { - it('with string', () => { - const result = init('foo') +import {both} from 'rambda' - result // $ExpectType string +describe('R.both', () => { + it('with passed type', () => { + const fn = both( + x => x > 1, + x => x % 2 === 0 + ) + fn // $ExpectType Predicate + const result = fn(2) // $ExpectType boolean + result // $ExpectType boolean }) - it('with list - one type', () => { - const result = init([1, 2, 3]) - - result // $ExpectType number[] + it('with passed type - curried', () => { + const fn = both(x => x > 1)(x => x % 2 === 0) + fn // $ExpectType Predicate + const result = fn(2) + result // $ExpectType boolean }) - it('with list - mixed types', () => { - const result = init([1, 2, 3, 'foo', 'bar']) - - result // $ExpectType (string | number)[] + it('no type passed', () => { + const fn = both( + x => { + x // $ExpectType any + return x > 1 + }, + x => { + x // $ExpectType any + return x % 2 === 0 + } + ) + const result = fn(2) + result // $ExpectType boolean }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#init) - -### innerJoin - -```typescript - -innerJoin( - pred: (a: T1, b: T2) => boolean, -): (list1: T1[], list2: T2[]) => T1[] -``` - -It returns a new list by applying a `predicate` function to all elements of `list1` and `list2` and keeping only these elements where `predicate` returns `true`. - -```javascript -const list1 = [1, 2, 3, 4, 5] -const list2 = [4, 5, 6] -const predicate = (x, y) => x >= y -const result = R.innerJoin(predicate, list1, list2) -// => [4, 5] -``` - -Try this R.innerJoin example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -innerJoin( - pred: (a: T1, b: T2) => boolean, -): (list1: T1[], list2: T2[]) => T1[]; -innerJoin( - pred: (a: T1, b: T2) => boolean, - list1: T1[], -): (list2: T2[]) => T1[]; -innerJoin(pred: (a: T1, b: T2) => boolean, list1: T1[], list2: T2[]): T1[]; + it('no type passed - curried', () => { + const fn = both((x: number) => { + x // $ExpectType number + return x > 1 + })((x: number) => { + x // $ExpectType number + return x % 2 === 0 + }) + const result = fn(2) + result // $ExpectType boolean + }) +}) ```
-
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#both) -R.innerJoin source +### call -```javascript -import { curry } from './curry.js' +Try this R.call example in Rambda REPL -function _includesWith( - pred, x, list -){ - let idx = 0 - const len = list.length +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#call) - while (idx < len){ - if (pred(x, list[ idx ])) - return true +### chain - idx += 1 - } +```typescript - return false -} -function _filter(fn, list){ - let idx = 0 - const len = list.length - const result = [] +chain(fn: (n: T) => U[], list: T[]): U[] +``` - while (idx < len){ - if (fn(list[ idx ])) - result[ result.length ] = list[ idx ] +The method is also known as `flatMap`. - idx += 1 - } +Try this R.chain example in Rambda REPL - return result -} +
-export function innerJoinFn( - pred, xs, ys -){ - return _filter(x => _includesWith( - pred, x, ys - ), xs) -} +All TypeScript definitions -export const innerJoin = curry(innerJoinFn) +```typescript +chain(fn: (n: T) => U[], list: T[]): U[]; +chain(fn: (n: T) => U[]): (list: T[]) => U[]; ```
-Tests +R.chain source ```javascript -import { innerJoin } from './innerJoin.js' +export function chain(fn, list){ + if (arguments.length === 1){ + return _list => chain(fn, _list) + } -const a = { - id: 1, - name: 'a', -} -const b = { - id: 2, - name: 'b', -} -const c = { - id: 3, - name: 'c', + return [].concat(...list.map(fn)) } -const f = innerJoin((r, id) => r.id === id) - -test('only returns elements from the first list', () => { - expect(f([a, b, c], [])).toEqual([]) - expect(f([a, b, c], [1])).toEqual([a]) - expect(f([a, b, c], [1, 2])).toEqual([a, b]) - expect(f([a, b, c], [1, 2, 3])).toEqual([a, b, c]) - expect(f([a, b, c], [1, 2, 3, 4])).toEqual([a, b, c]) -}) - -test('does not remove duplicates', () => { - expect(f([a, a, a], [1, 2, 3])).toEqual([a, a, a]) - expect(f([a, b, c], [1, 1, 1])).toEqual([a]) -}) - -test('readme example', () => { - const list1 = [1, 2, 3, 4, 5] - const list2 = [4, 5, 6] - const predicate = (x, y) => x >= y - const result = innerJoin(predicate, list1, list2) - expect(result).toEqual([4, 5]) -}) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#innerJoin) - -### insert - -```typescript +
-insert(index: number): (itemToInsert: T, list: T[]) => T[] -``` +Tests ```javascript -const list = ['a', 'b', 'c', 'd', 'e']; -const result = R.insert(2, 'x', list); -// => ['a', 'b', 'x', 'c', 'd', 'e'] -``` - -Try this R.insert example in Rambda REPL - -
+import { chain as chainRamda } from 'ramda' -All TypeScript definitions +import { chain } from './chain.js' -```typescript -insert(index: number): (itemToInsert: T, list: T[]) => T[]; -insert(index: number, itemToInsert: T): (list: T[]) => T[]; -insert(index: number, itemToInsert: T, list: T[]): T[]; -``` +const duplicate = n => [ n, n ] -
+test('happy', () => { + const fn = x => [ x * 2 ] + const list = [ 1, 2, 3 ] -
+ const result = chain(fn, list) -R.insert source + expect(result).toEqual([ 2, 4, 6 ]) +}) -```javascript -import { curry } from './curry.js' +test('maps then flattens one level', () => { + expect(chain(duplicate, [ 1, 2, 3 ])).toEqual([ 1, 1, 2, 2, 3, 3 ]) +}) -export function insertFn(indexToInsert, valueToInsert, array) { - return [ - ...array.slice(0, indexToInsert), - valueToInsert, - ...array.slice(indexToInsert), - ] -} +test('maps then flattens one level - curry', () => { + expect(chain(duplicate)([ 1, 2, 3 ])).toEqual([ 1, 1, 2, 2, 3, 3 ]) +}) -export const insert = curry(insertFn) -``` +test('flattens only one level', () => { + const nest = n => [ [ n ] ] + expect(chain(nest, [ 1, 2, 3 ])).toEqual([ [ 1 ], [ 2 ], [ 3 ] ]) +}) -
+test('can compose', () => { + function dec(x){ + return [ x - 1 ] + } + function times2(x){ + return [ x * 2 ] + } -
+ const mdouble = chain(times2) + const mdec = chain(dec) + expect(mdec(mdouble([ 10, 20, 30 ]))).toEqual([ 19, 39, 59 ]) +}) -Tests +test('@types/ramda broken test', () => { + const score = { + maths : 90, + physics : 80, + } -```javascript -import { insert } from './insert'; + const calculateTotal = score => { + const { maths, physics } = score -it('inserts an element into the given list', () => { - const list = ['a', 'b', 'c', 'd', 'e']; - expect(insert(2, 'x', list)).toEqual(['a', 'b', 'x', 'c', 'd', 'e']); -}); + return maths + physics + } -it('inserts another list as an element', () => { - const list = ['a', 'b', 'c', 'd', 'e']; - expect(insert(2, ['s', 't'], list)).toEqual([ - 'a', - 'b', - ['s', 't'], - 'c', - 'd', - 'e', - ]); -}); + const assocTotalToScore = (total, score) => ({ + ...score, + total, + }) -it('appends to the end of the list if the index is too large', () => { - const list = ['a', 'b', 'c', 'd', 'e']; - expect(insert(8, 'z', list)).toEqual(['a', 'b', 'c', 'd', 'e', 'z']); -}); + const calculateAndAssocTotalToScore = chainRamda(assocTotalToScore, + calculateTotal) + expect(() => + calculateAndAssocTotalToScore(score)).toThrowErrorMatchingInlineSnapshot('"fn(...) is not a function"') +}) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#insert) +
-### insertAll +TypeScript test ```typescript +import {chain} from 'rambda' -insertAll(index: number): (itemsToInsert: T[], list: T[]) => T[] -``` - -```javascript -const list = ['a', 'b', 'c', 'd', 'e']; -const result = R.insertAll(2, ['x', 'y', 'z'], list); -// => ['a', 'b', 'x', 'y', 'z', 'c', 'd', 'e'] -``` - -Try this R.insertAll example in Rambda REPL - -
+const list = [1, 2, 3] +const fn = (x: number) => [`${x}`, `${x}`] -All TypeScript definitions +describe('R.chain', () => { + it('without passing type', () => { + const result = chain(fn, list) + result // $ExpectType string[] -```typescript -insertAll(index: number): (itemsToInsert: T[], list: T[]) => T[]; -insertAll(index: number, itemsToInsert: T[]): (list: T[]) => T[]; -insertAll(index: number, itemsToInsert: T[], list: T[]): T[]; + const curriedResult = chain(fn)(list) + curriedResult // $ExpectType string[] + }) +}) ```
-
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#chain) -R.insertAll source +### clamp -```javascript -import { curry } from './curry.js'; +Restrict a number `input` to be within `min` and `max` limits. -export function insertAllFn(index, listToInsert, list) { - return [...list.slice(0, index), ...listToInsert, ...list.slice(index)]; -} +If `input` is bigger than `max`, then the result is `max`. -export const insertAll = curry(insertAllFn); -``` +If `input` is smaller than `min`, then the result is `min`. -
+Try this R.clamp example in Rambda REPL -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#clamp) -Tests +### clone -```javascript -import { insertAll } from './insertAll'; - -it('inserts a list of elements into the given list', () => { - const list = ['a', 'b', 'c', 'd', 'e']; - expect(insertAll(2, ['x', 'y', 'z'], list)).toEqual([ - 'a', - 'b', - 'x', - 'y', - 'z', - 'c', - 'd', - 'e', - ]); -}); +It creates a deep copy of the `input`, which may contain (nested) Arrays and Objects, Numbers, Strings, Booleans and Dates. -it('appends to the end of the list if the index is too large', () => { - const list = ['a', 'b', 'c', 'd', 'e']; - expect(insertAll(8, ['p', 'q', 'r'], list)).toEqual([ - 'a', - 'b', - 'c', - 'd', - 'e', - 'p', - 'q', - 'r', - ]); -}); -``` +Try this R.clone example in Rambda REPL -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#clone) -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#insertAll) +### collectBy -### intersection +Try this R.collectBy example in Rambda REPL -```typescript +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#collectBy) -intersection(listA: T[], listB: T[]): T[] -``` +### comparator -It loops through `listA` and `listB` and returns the intersection of the two according to `R.equals`. +It returns a comparator function that can be used in `sort` method. -> :boom: There is slight difference between Rambda and Ramda implementation. Ramda.intersection(['a', 'b', 'c'], ['c', 'b']) result is "[ 'c', 'b' ]", but Rambda result is "[ 'b', 'c' ]". +Try this R.comparator example in Rambda REPL -```javascript -const listA = [ { id : 1 }, { id : 2 }, { id : 3 }, { id : 4 } ] -const listB = [ { id : 3 }, { id : 4 }, { id : 5 }, { id : 6 } ] +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#comparator) -const result = R.intersection(listA, listB) -// => [{ id : 3 }, { id : 4 }] -``` +### complement -Try this R.intersection example in Rambda REPL +It returns `inverted` version of `origin` function that accept `input` as argument. -
+The return value of `inverted` is the negative boolean value of `origin(input)`. -All TypeScript definitions +Try this R.complement example in Rambda REPL -```typescript -intersection(listA: T[], listB: T[]): T[]; -intersection(listA: T[]): (listB: T[]) => T[]; -``` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#complement) -
+### compose -
+It performs right-to-left function composition. -R.intersection source +Try this R.compose example in Rambda REPL -```javascript -import { filter } from './filter.js' -import { includes } from './includes.js' +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#compose) -export function intersection(listA, listB){ - if (arguments.length === 1) return _list => intersection(listA, _list) +### composeWith - return filter(x => includes(x, listA), listB) -} -``` +Try this R.composeWith example in Rambda REPL -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#composeWith) -
+### concat -Tests +It returns a new string or array, which is the result of merging `x` and `y`. -```javascript -import { intersection as intersectionRamda } from 'ramda' +Try this R.concat example in Rambda REPL -import { intersection } from './intersection.js' +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#concat) -test('intersection', () => { - const list1 = [ 1, 2, 3, 4 ] - const list2 = [ 3, 4, 5, 6 ] - expect(intersection(list1)(list2)).toEqual([ 3, 4 ]) +### cond - expect(intersection([], [])).toEqual([]) -}) +It takes list with `conditions` and returns a new function `fn` that expects `input` as argument. -test('intersection with objects', () => { - const list1 = [ { id : 1 }, { id : 2 }, { id : 3 }, { id : 4 } ] - const list2 = [ { id : 3 }, { id : 4 }, { id : 5 }, { id : 6 } ] - expect(intersection(list1)(list2)).toEqual([ { id : 3 }, { id : 4 } ]) -}) +This function will start evaluating the `conditions` in order to find the first winner(order of conditions matter). -test('order is the same as in Ramda', () => { - const list = [ 'a', 'b', 'c', 'd' ] +The winner is this condition, which left side returns `true` when `input` is its argument. Then the evaluation of the right side of the winner will be the final result. - expect(intersectionRamda(list, [ 'b', 'c' ])).toEqual([ 'b', 'c' ]) - expect(intersection(list, [ 'b', 'c' ])).toEqual([ 'b', 'c' ]) +If no winner is found, then `fn` returns `undefined`. - expect(intersection(list, [ 'c', 'b' ])).toEqual([ 'c', 'b' ]) - expect(intersectionRamda(list, [ 'c', 'b' ])).toEqual([ 'c', 'b' ]) -}) -``` +Try this R.cond example in Rambda REPL -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#cond) -
+### converge -TypeScript test +Accepts a converging function and a list of branching functions and returns a new function. When invoked, this new function is applied to some arguments, each branching function is applied to those same arguments. The results of each branching function are passed as arguments to the converging function to produce the return value. -```typescript -import {intersection} from 'rambda' +Try this R.converge example in Rambda REPL -const list1 = [1, 2, 3] -const list2 = [1, 3, 5] +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#converge) -describe('R.intersection', () => { - it('happy', () => { - const result = intersection(list1, list2) - result // $ExpectType number[] +### count - const curriedResult = intersection(list1)(list2) - curriedResult // $ExpectType number[] - }) -}) -``` +It counts how many times `predicate` function returns `true`, when supplied with iteration of `list`. -
+Try this R.count example in Rambda REPL -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#intersection) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#count) -### intersperse +### countBy ```typescript -intersperse(separator: T, list: T[]): T[] +countBy(transformFn: (x: T) => any, list: T[]): Record ``` -It adds a `separator` between members of `list`. - -```javascript -const list = [ 0, 1, 2, 3 ] -const separator = '|' -const result = intersperse(separator, list) -// => [0, '|', 1, '|', 2, '|', 3] -``` +It counts elements in a list after each instance of the input list is passed through `transformFn` function. -Try this R.intersperse example in Rambda REPL +Try this R.countBy example in Rambda REPL
All TypeScript definitions ```typescript -intersperse(separator: T, list: T[]): T[]; -intersperse(separator: T): (list: T[]) => T[]; +countBy(transformFn: (x: T) => any, list: T[]): Record; +countBy(transformFn: (x: T) => any): (list: T[]) => Record; ```
-R.intersperse source +R.countBy source ```javascript -export function intersperse(separator, list){ - if (arguments.length === 1) return _list => intersperse(separator, _list) - - let index = -1 - const len = list.length - const willReturn = [] +export function countBy(fn, list){ + if (arguments.length === 1){ + return _list => countBy(fn, _list) + } + const willReturn = {} - while (++index < len){ - if (index === len - 1){ - willReturn.push(list[ index ]) + list.forEach(item => { + const key = fn(item) + if (!willReturn[ key ]){ + willReturn[ key ] = 1 } else { - willReturn.push(list[ index ], separator) + willReturn[ key ]++ } - } + }) return willReturn } @@ -13232,21 +2679,17 @@ export function intersperse(separator, list){ Tests ```javascript -import { intersperse } from './intersperse.js' +import { countBy } from './countBy.js' -test('intersperse', () => { - const list = [ { id : 1 }, { id : 2 }, { id : 10 }, { id : 'a' } ] - expect(intersperse('!', list)).toEqual([ - { id : 1 }, - '!', - { id : 2 }, - '!', - { id : 10 }, - '!', - { id : 'a' }, - ]) +const list = [ 'a', 'A', 'b', 'B', 'c', 'C' ] - expect(intersperse('!')([])).toEqual([]) +test('happy', () => { + const result = countBy(x => x.toLowerCase(), list) + expect(result).toEqual({ + a : 2, + b : 2, + c : 2, + }) }) ``` @@ -13257,69 +2700,93 @@ test('intersperse', () => { TypeScript test ```typescript -import {intersperse} from 'rambda' +import {countBy} from 'rambda' + +const transformFn = (x: string) => x.toLowerCase() +const list = ['a', 'A', 'b', 'B', 'c', 'C'] -describe('R.intersperse', () => { +describe('R.countBy', () => { it('happy', () => { - const result = intersperse(1, [1, 2, 3]) - result // $ExpectType number[] + const result = countBy(transformFn, list) + + result // $ExpectType Record }) it('curried', () => { - const result = intersperse('|')(['foo', 'bar']) - result // $ExpectType string[] + const result = countBy(transformFn)(list) + + result // $ExpectType Record }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#intersperse) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#countBy) -### is +### curry + +It expects a function as input and returns its curried version. + +Try this R.curry example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#curry) + +### curryN + +It returns a curried equivalent of the provided function, with the specified arity. + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#curryN) + +### dec + +It decrements a number. + +Try this R.dec example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dec) + +### defaultTo ```typescript -is any>(targetPrototype: C, val: any): val is ReturnType +defaultTo(defaultValue: T, input: T | null | undefined): T ``` -It returns `true` if `x` is instance of `targetPrototype`. +It returns `defaultValue`, if all of `inputArguments` are `undefined`, `null` or `NaN`. -```javascript -const result = [ - R.is(String, 'foo'), - R.is(Array, 1) -] -// => [true, false] -``` +Else, it returns the first truthy `inputArguments` instance(from left to right). -Try this R.is example in Rambda REPL +Try this R.defaultTo example in Rambda REPL
All TypeScript definitions ```typescript -is any>(targetPrototype: C, val: any): val is ReturnType; -is any>(targetPrototype: C, val: any): val is InstanceType; -is any>(targetPrototype: C): (val: any) => val is ReturnType; -is any>(targetPrototype: C): (val: any) => val is InstanceType; +defaultTo(defaultValue: T, input: T | null | undefined): T; +defaultTo(defaultValue: T): (input: T | null | undefined) => T; ```
-R.is source +R.defaultTo source ```javascript -export function is(targetPrototype, x){ - if (arguments.length === 1) return _x => is(targetPrototype, _x) - +function isFalsy(input){ return ( - x != null && x.constructor === targetPrototype || - x instanceof targetPrototype + input === undefined || input === null || Number.isNaN(input) === true ) } + +export function defaultTo(defaultArgument, input){ + if (arguments.length === 1){ + return _input => defaultTo(defaultArgument, _input) + } + + return isFalsy(input) ? defaultArgument : input +} ```
@@ -13329,50 +2796,30 @@ export function is(targetPrototype, x){ Tests ```javascript -import { is } from './is.js' +import { defaultTo } from './defaultTo.js' -test('works with built-in types', () => { - expect(is(Array, undefined)).toBeFalse() - expect(is(Array)([])).toBeTrue() - expect(is(Boolean, new Boolean(false))).toBeTrue() - expect(is(Date, new Date())).toBeTrue() - expect(is(Function, () => {})).toBeTrue() - expect(is(Number, new Number(0))).toBeTrue() - expect(is(Object, {})).toBeTrue() - expect(is(RegExp, /(?:)/)).toBeTrue() - expect(is(String, new String(''))).toBeTrue() +test('with undefined', () => { + expect(defaultTo('foo')(undefined)).toBe('foo') }) -test('works with user-defined types', () => { - function Foo(){} - function Bar(){} - Bar.prototype = new Foo() - - const foo = new Foo() - const bar = new Bar() +test('with null', () => { + expect(defaultTo('foo')(null)).toBe('foo') +}) - expect(is(Foo, foo)).toBeTrue() - expect(is(Bar, bar)).toBeTrue() - expect(is(Foo, bar)).toBeTrue() - expect(is(Bar, foo)).toBeFalse() +test('with NaN', () => { + expect(defaultTo('foo')(NaN)).toBe('foo') }) -test('does not coerce', () => { - expect(is(Boolean, 1)).toBeFalse() - expect(is(Number, '1')).toBeFalse() - expect(is(Number, false)).toBeFalse() +test('with empty string', () => { + expect(defaultTo('foo', '')).toBe('') }) -test('recognizes primitives as their object equivalents', () => { - expect(is(Boolean, false)).toBeTrue() - expect(is(Number, 0)).toBeTrue() - expect(is(String, '')).toBeTrue() +test('with false', () => { + expect(defaultTo('foo', false)).toBeFalse() }) -test('does not consider primitives to be instances of Object', () => { - expect(is(Object, false)).toBeFalse() - expect(is(Object, 0)).toBeFalse() - expect(is(Object, '')).toBeFalse() +test('when inputArgument passes initial check', () => { + expect(defaultTo('foo', 'bar')).toBe('bar') }) ``` @@ -13383,75 +2830,66 @@ test('does not consider primitives to be instances of Object', () => { TypeScript test ```typescript -import {is} from 'rambda' +import {defaultTo} from 'rambda' -describe('R.is', () => { +describe('R.defaultTo with Ramda spec', () => { it('happy', () => { - const result = is(String, 'foo') - result // $ExpectType boolean + const result = defaultTo('foo', '') + result // $ExpectType "" | "foo" }) - it('curried', () => { - const result = is(Number)(1) - result // $ExpectType boolean + it('with explicit type', () => { + const result = defaultTo('foo', null) + result // $ExpectType string }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#is) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#defaultTo) -### isEmpty +### descend + +Try this R.descend example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#descend) + +### difference ```typescript -isEmpty(x: T): boolean +difference(a: T[], b: T[]): T[] ``` -It returns `true` if `x` is `empty`. +It returns the uniq set of all elements in the first list `a` not contained in the second list `b`. -```javascript -const result = [ - R.isEmpty(''), - R.isEmpty({ x : 0 }) -] -// => [true, false] -``` +`R.equals` is used to determine equality. -Try this R.isEmpty example in Rambda REPL +Try this R.difference example in Rambda REPL
All TypeScript definitions ```typescript -isEmpty(x: T): boolean; +difference(a: T[], b: T[]): T[]; +difference(a: T[]): (b: T[]) => T[]; ```
-R.isEmpty source +R.difference source ```javascript -import { type } from './type.js' - -export function isEmpty(input){ - const inputType = type(input) - if ([ 'Undefined', 'NaN', 'Number', 'Null' ].includes(inputType)) - return false - if (!input) return true - - if (inputType === 'Object'){ - return Object.keys(input).length === 0 - } +import { includes } from './includes.js' +import { uniq } from './uniq.js' - if (inputType === 'Array'){ - return input.length === 0 - } +export function difference(a, b){ + if (arguments.length === 1) return _b => difference(a, _b) - return false + return uniq(a).filter(aInstance => !includes(aInstance, b)) } ``` @@ -13462,21 +2900,33 @@ export function isEmpty(input){ Tests ```javascript -import { isEmpty } from './isEmpty.js' +import { difference as differenceRamda } from 'ramda' -test('happy', () => { - expect(isEmpty(undefined)).toBeFalse() - expect(isEmpty('')).toBeTrue() - expect(isEmpty(null)).toBeFalse() - expect(isEmpty(' ')).toBeFalse() - expect(isEmpty(new RegExp(''))).toBeFalse() - expect(isEmpty([])).toBeTrue() - expect(isEmpty([ [] ])).toBeFalse() - expect(isEmpty({})).toBeTrue() - expect(isEmpty({ x : 0 })).toBeFalse() - expect(isEmpty(0)).toBeFalse() - expect(isEmpty(NaN)).toBeFalse() - expect(isEmpty([ '' ])).toBeFalse() +import { difference } from './difference.js' + +test('difference', () => { + const a = [ 1, 2, 3, 4 ] + const b = [ 3, 4, 5, 6 ] + expect(difference(a)(b)).toEqual([ 1, 2 ]) + + expect(difference([], [])).toEqual([]) +}) + +test('difference with objects', () => { + const a = [ { id : 1 }, { id : 2 }, { id : 3 }, { id : 4 } ] + const b = [ { id : 3 }, { id : 4 }, { id : 5 }, { id : 6 } ] + expect(difference(a, b)).toEqual([ { id : 1 }, { id : 2 } ]) +}) + +test('no duplicates in first list', () => { + const M2 = [ 1, 2, 3, 4, 1, 2, 3, 4 ] + const N2 = [ 3, 3, 4, 4, 5, 5, 6, 6 ] + expect(difference(M2, N2)).toEqual([ 1, 2 ]) +}) + +test('should use R.equals', () => { + expect(difference([ 1 ], [ 1 ])).toHaveLength(0) + expect(differenceRamda([ NaN ], [ NaN ])).toHaveLength(0) }) ``` @@ -13487,57 +2937,88 @@ test('happy', () => { TypeScript test ```typescript -import {isEmpty} from 'rambda' +import {difference} from 'rambda' -describe('R.isEmpty', () => { +const list1 = [1, 2, 3] +const list2 = [1, 2, 4] + +describe('R.difference', () => { it('happy', () => { - const result = isEmpty('foo') - result // $ExpectType boolean + const result = difference(list1, list2) + + result // $ExpectType number[] + }) + it('curried', () => { + const result = difference(list1)(list2) + + result // $ExpectType number[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#isEmpty) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#difference) -### isNil +### differenceWith ```typescript -isNil(x: any): x is null | undefined -``` - -It returns `true` if `x` is either `null` or `undefined`. - -```javascript -const result = [ - R.isNil(null), - R.isNil(1), -] -// => [true, false] +differenceWith( + pred: (a: T1, b: T2) => boolean, + list1: T1[], + list2: T2[], +): T1[] ``` -Try this R.isNil example in Rambda REPL +Try this R.differenceWith example in Rambda REPL
All TypeScript definitions ```typescript -isNil(x: any): x is null | undefined; +differenceWith( + pred: (a: T1, b: T2) => boolean, + list1: T1[], + list2: T2[], +): T1[]; +differenceWith( + pred: (a: T1, b: T2) => boolean, +): (list1: T1[], list2: T2[]) => T1[]; +differenceWith( + pred: (a: T1, b: T2) => boolean, + list1: T1[], +): (list2: T2[]) => T1[]; ```
-R.isNil source +R.differenceWith source ```javascript -export function isNil(x){ - return x === undefined || x === null +import { curry } from './curry.js' +import { _indexOf } from './equals.js' + +export function differenceWithFn( + fn, a, b +){ + const willReturn = [] + const [ first, second ] = a.length >= b.length ? [ a, b ] : [ b, a ] + + first.forEach(item => { + const hasItem = second.some(secondItem => fn(item, secondItem)) + if (!hasItem && _indexOf(item, willReturn) === -1){ + willReturn.push(item) + } + }) + + return willReturn } + +export const differenceWith = curry(differenceWithFn) ```
@@ -13547,77 +3028,82 @@ export function isNil(x){ Tests ```javascript -import { isNil } from './isNil.js' +import { differenceWith } from './differenceWith.js'; -test('happy', () => { - expect(isNil(null)).toBeTrue() +const fn = (a, b) => a.x === b.x; - expect(isNil(undefined)).toBeTrue() +test('same length of list', () => { + const result = differenceWith(fn, [{ x: 1 }, { x: 2 }], [{ x: 1 }, { x: 3 }]); + expect(result).toEqual([{ x: 2 }]); +}); - expect(isNil([])).toBeFalse() -}) +test('different length of list', () => { + const foo = [{ x: 1 }, { x: 2 }, { x: 3 }]; + const bar = [{ x: 3 }, { x: 4 }]; + const result = differenceWith(fn, foo, bar); + expect(result).toEqual([{ x: 1 }, { x: 2 }]); +}); ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#isNil) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#differenceWith) -### isNotEmpty +### dissoc -```typescript +It returns a new object that does not contain property `prop`. -isNotEmpty(value: T[]): value is NonEmptyArray -``` +Try this R.dissoc example in Rambda REPL -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dissoc) -All TypeScript definitions +### dissocPath -```typescript -isNotEmpty(value: T[]): value is NonEmptyArray; -isNotEmpty(value: readonly T[]): value is ReadonlyNonEmptyArray; -isNotEmpty(value: any): boolean; -``` +Try this R.dissocPath example in Rambda REPL -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dissocPath) -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#isNotEmpty) +### divide -### isNotNil +Try this R.divide example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#divide) + +### drop ```typescript -isNotNil(value: T): value is NonNullable +drop(howMany: number, input: T[]): T[] ``` -```javascript -const result = [ - R.isNotNil(null), - R.isNotNil(undefined), - R.isNotNil([]), -] -// => [false, false, true] -``` +It returns `howMany` items dropped from beginning of list or string `input`. -Try this R.isNotNil example in Rambda REPL +Try this R.drop example in Rambda REPL
All TypeScript definitions ```typescript -isNotNil(value: T): value is NonNullable; +drop(howMany: number, input: T[]): T[]; +drop(howMany: number, input: string): string; +drop(howMany: number): { + (input: T[]): T[]; + (input: string): string; +}; ```
-R.isNotNil source +R.drop source ```javascript -export function isNotNil(input) { - return input != null +export function drop(howManyToDrop, listOrString){ + if (arguments.length === 1) return _list => drop(howManyToDrop, _list) + + return listOrString.slice(howManyToDrop > 0 ? howManyToDrop : 0) } ``` @@ -13628,58 +3114,113 @@ export function isNotNil(input) { Tests ```javascript -import { isNotNil } from './isNotNil' +import assert from 'assert' + +import { drop } from './drop.js' + +test('with array', () => { + expect(drop(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'baz' ]) + expect(drop(3, [ 'foo', 'bar', 'baz' ])).toEqual([]) + expect(drop(4, [ 'foo', 'bar', 'baz' ])).toEqual([]) +}) + +test('with string', () => { + expect(drop(3, 'rambda')).toBe('bda') +}) + +test('with non-positive count', () => { + expect(drop(0, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) + expect(drop(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) + expect(drop(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) +}) + +test('should return copy', () => { + const xs = [ 1, 2, 3 ] -test('tests a value for `null` or `undefined`', () => { - expect(isNotNil(void 0)).toBe(false) - expect(isNotNil(undefined)).toBe(false) - expect(isNotNil(null)).toBe(false) - expect(isNotNil([])).toBe(true) - expect(isNotNil({})).toBe(true) - expect(isNotNil(0)).toBe(true) - expect(isNotNil('')).toBe(true) + assert.notStrictEqual(drop(0, xs), xs) + assert.notStrictEqual(drop(-1, xs), xs) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#isNotNil) +
-### join +TypeScript test ```typescript +import {drop} from 'rambda' -join(glue: string, list: T[]): string +const list = [1, 2, 3, 4] +const str = 'foobar' +const howMany = 2 + +describe('R.drop - array', () => { + it('happy', () => { + const result = drop(howMany, list) + result // $ExpectType number[] + }) + it('curried', () => { + const result = drop(howMany)(list) + result // $ExpectType number[] + }) +}) + +describe('R.drop - string', () => { + it('happy', () => { + const result = drop(howMany, str) + result // $ExpectType string + }) + it('curried', () => { + const result = drop(howMany)(str) + result // $ExpectType string + }) +}) ``` -It returns a string of all `list` instances joined with a `glue`. +
-```javascript -R.join('-', [1, 2, 3]) // => '1-2-3' +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#drop) + +### dropLast + +```typescript + +dropLast(howMany: number, input: T[]): T[] ``` -Try this R.join example in Rambda REPL +It returns `howMany` items dropped from the end of list or string `input`. + +Try this R.dropLast example in Rambda REPL
All TypeScript definitions ```typescript -join(glue: string, list: T[]): string; -join(glue: string): (list: T[]) => string; +dropLast(howMany: number, input: T[]): T[]; +dropLast(howMany: number, input: string): string; +dropLast(howMany: number): { + (input: T[]): T[]; + (input: string): string; +}; ```
-R.join source +R.dropLast source ```javascript -export function join(glue, list){ - if (arguments.length === 1) return _list => join(glue, _list) +export function dropLast(howManyToDrop, listOrString){ + if (arguments.length === 1){ + return _listOrString => dropLast(howManyToDrop, _listOrString) + } - return list.join(glue) + return howManyToDrop > 0 ? + listOrString.slice(0, -howManyToDrop) : + listOrString.slice() } ``` @@ -13690,16 +3231,31 @@ export function join(glue, list){ Tests ```javascript -import { join } from './join.js' +import assert from 'assert' -test('curry', () => { - expect(join('|')([ 'foo', 'bar', 'baz' ])).toBe('foo|bar|baz') +import { dropLast } from './dropLast.js' - expect(join('|', [ 1, 2, 3 ])).toBe('1|2|3') +test('with array', () => { + expect(dropLast(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'foo' ]) + expect(dropLast(3, [ 'foo', 'bar', 'baz' ])).toEqual([]) + expect(dropLast(4, [ 'foo', 'bar', 'baz' ])).toEqual([]) +}) - const spacer = join(' ') +test('with string', () => { + expect(dropLast(3, 'rambda')).toBe('ram') +}) - expect(spacer([ 'a', 2, 3.4 ])).toBe('a 2 3.4') +test('with non-positive count', () => { + expect(dropLast(0, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) + expect(dropLast(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) + expect(dropLast(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) +}) + +test('should return copy', () => { + const xs = [ 1, 2, 3 ] + + assert.notStrictEqual(dropLast(0, xs), xs) + assert.notStrictEqual(dropLast(-1, xs), xs) }) ``` @@ -13710,11 +3266,30 @@ test('curry', () => { TypeScript test ```typescript -import {join} from 'rambda' +import {dropLast} from 'rambda' -describe('R.join', () => { +const list = [1, 2, 3, 4] +const str = 'foobar' +const howMany = 2 + +describe('R.dropLast - array', () => { it('happy', () => { - const result = join('|', [1, 2, 3]) + const result = dropLast(howMany, list) + result // $ExpectType number[] + }) + it('curried', () => { + const result = dropLast(howMany)(list) + result // $ExpectType number[] + }) +}) + +describe('R.dropLast - string', () => { + it('happy', () => { + const result = dropLast(howMany, str) + result // $ExpectType string + }) + it('curried', () => { + const result = dropLast(howMany)(str) result // $ExpectType string }) }) @@ -13722,49 +3297,59 @@ describe('R.join', () => {
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#join) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropLast) -### juxt +### dropLastWhile -```typescript +Try this R.dropLastWhile example in Rambda REPL -juxt(fns: [(...a: A) => R1]): (...a: A) => [R1] -``` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropLastWhile) + +### dropRepeats -It applies list of function to a list of inputs. +```typescript -```javascript -const getRange = juxt([ Math.min, Math.max, Math.min ]) -const result = getRange( - 3, 4, 9, -3 -) -// => [-3, 9, -3] +dropRepeats(list: T[]): T[] ``` -Try this R.juxt example in Rambda REPL +It removes any successive duplicates according to `R.equals`. + +Try this R.dropRepeats example in Rambda REPL
All TypeScript definitions ```typescript -juxt(fns: [(...a: A) => R1]): (...a: A) => [R1]; -juxt(fns: [(...a: A) => R1, (...a: A) => R2]): (...a: A) => [R1, R2]; -juxt(fns: [(...a: A) => R1, (...a: A) => R2, (...a: A) => R3]): (...a: A) => [R1, R2, R3]; -juxt(fns: [(...a: A) => R1, (...a: A) => R2, (...a: A) => R3, (...a: A) => R4]): (...a: A) => [R1, R2, R3, R4]; -juxt(fns: [(...a: A) => R1, (...a: A) => R2, (...a: A) => R3, (...a: A) => R4, (...a: A) => R5]): (...a: A) => [R1, R2, R3, R4, R5]; -juxt(fns: Array<(...args: A) => U>): (...args: A) => U[]; +dropRepeats(list: T[]): T[]; ```
-R.juxt source +R.dropRepeats source ```javascript -export function juxt(listOfFunctions){ - return (...args) => listOfFunctions.map(fn => fn(...args)) +import { isArray } from './_internals/isArray.js' +import { equals } from './equals.js' + +export function dropRepeats(list){ + if (!isArray(list)){ + throw new Error(`${ list } is not a list`) + } + + const toReturn = [] + + list.reduce((prev, current) => { + if (!equals(prev, current)){ + toReturn.push(current) + } + + return current + }, undefined) + + return toReturn } ``` @@ -13775,14 +3360,47 @@ export function juxt(listOfFunctions){ Tests ```javascript -import { juxt } from './juxt.js' +import { dropRepeats as dropRepeatsRamda } from 'ramda' + +import { compareCombinations } from './_internals/testUtils.js' +import { add } from './add.js' +import { dropRepeats } from './dropRepeats.js' + +const list = [ 1, 2, 2, 2, 3, 4, 4, 5, 5, 3, 2, 2, { a : 1 }, { a : 1 } ] +const listClean = [ 1, 2, 3, 4, 5, 3, 2, { a : 1 } ] test('happy', () => { - const fn = juxt([ Math.min, Math.max, Math.min ]) - const result = fn( - 3, 4, 9, -3 - ) - expect(result).toEqual([ -3, 9, -3 ]) + const result = dropRepeats(list) + expect(result).toEqual(listClean) +}) + +const possibleLists = [ + [ add(1), async () => {}, [ 1 ], [ 1 ], [ 2 ], [ 2 ] ], + [ add(1), add(1), add(2) ], + [], + 1, + /foo/g, + Promise.resolve(1), +] + +describe('brute force', () => { + compareCombinations({ + firstInput : possibleLists, + callback : errorsCounters => { + expect(errorsCounters).toMatchInlineSnapshot(` + { + "ERRORS_MESSAGE_MISMATCH": 0, + "ERRORS_TYPE_MISMATCH": 0, + "RESULTS_MISMATCH": 1, + "SHOULD_NOT_THROW": 3, + "SHOULD_THROW": 0, + "TOTAL_TESTS": 6, + } + `) + }, + fn : dropRepeats, + fnRamda : dropRepeatsRamda, + }) }) ``` @@ -13793,54 +3411,77 @@ test('happy', () => { TypeScript test ```typescript -import {juxt} from 'rambda' +import {dropRepeats} from 'rambda' -describe('R.juxt', () => { +describe('R.dropRepeats', () => { it('happy', () => { - const fn = juxt([Math.min, Math.max]) - const result = fn(3, 4, 9, -3) - result // $ExpectType [number, number] + const result = dropRepeats([1, 2, 2, 3]) + + result // $ExpectType number[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#juxt) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeats) -### keys +### dropRepeatsBy + +
Try this R.dropRepeatsBy example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeatsBy) + +### dropRepeatsWith + +Try this R.dropRepeatsWith example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeatsWith) + +### dropWhile + +Try this R.dropWhile example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropWhile) + +### either ```typescript -keys(x: T): (keyof T & string)[] +either(firstPredicate: Pred, secondPredicate: Pred): Pred ``` -It applies `Object.keys` over `x` and returns its keys. +It returns a new `predicate` function from `firstPredicate` and `secondPredicate` inputs. -```javascript -R.keys({a:1, b:2}) // => ['a', 'b'] -``` +This `predicate` function will return `true`, if any of the two input predicates return `true`. -Try this R.keys example in Rambda REPL +Try this R.either example in Rambda REPL
All TypeScript definitions ```typescript -keys(x: T): (keyof T & string)[]; -keys(x: T): string[]; +either(firstPredicate: Pred, secondPredicate: Pred): Pred; +either(firstPredicate: Predicate, secondPredicate: Predicate): Predicate; +either(firstPredicate: Predicate): (secondPredicate: Predicate) => Predicate; +either(firstPredicate: Pred): (secondPredicate: Pred) => Pred; ```
-R.keys source +R.either source ```javascript -export function keys(x){ - return Object.keys(x) +export function either(firstPredicate, secondPredicate){ + if (arguments.length === 1){ + return _secondPredicate => either(firstPredicate, _secondPredicate) + } + + return (...input) => + Boolean(firstPredicate(...input) || secondPredicate(...input)) } ``` @@ -13851,138 +3492,169 @@ export function keys(x){ Tests ```javascript -import { keys } from './keys.js' +import { either } from './either.js' -test('happy', () => { - expect(keys({ a : 1 })).toEqual([ 'a' ]) +test('with multiple inputs', () => { + const between = function ( + a, b, c + ){ + return a < b && b < c + } + const total20 = function ( + a, b, c + ){ + return a + b + c === 20 + } + const fn = either(between, total20) + expect(fn( + 7, 8, 5 + )).toBeTrue() }) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#keys) - -### last -```typescript - -last(str: ''): undefined -``` - -It returns the last element of `input`, as the `input` can be either a string or an array. It returns `undefined` if array has length of 0. +test('skip evaluation of the second expression', () => { + let effect = 'not evaluated' + const F = function (){ + return true + } + const Z = function (){ + effect = 'Z got evaluated' + } + either(F, Z)() -```javascript -const result = [ - R.last([1, 2, 3]), - R.last('foo'), -] -// => [3, 'o'] -``` + expect(effect).toBe('not evaluated') +}) -Try this R.last example in Rambda REPL +test('case 1', () => { + const firstFn = val => val > 0 + const secondFn = val => val * 5 > 10 -
+ expect(either(firstFn, secondFn)(1)).toBeTrue() +}) -All TypeScript definitions +test('case 2', () => { + const firstFn = val => val > 0 + const secondFn = val => val === -10 + const fn = either(firstFn)(secondFn) -```typescript -last(str: ''): undefined; -last(str: string): string; -last(list: readonly[]): undefined; -last(list: never[]): undefined; -last(array: T): LastArrayElement; -last(array: T): LastArrayElement; -last(str: string): string | undefined; + expect(fn(-10)).toBeTrue() +}) ```
-R.last source +TypeScript test -```javascript -export function last(listOrString){ - if (typeof listOrString === 'string'){ - return listOrString[ listOrString.length - 1 ] || '' - } +```typescript +import {either} from 'rambda' - return listOrString[ listOrString.length - 1 ] -} +describe('R.either', () => { + it('with passed type', () => { + const fn = either( + x => x > 1, + x => x % 2 === 0 + ) + fn // $ExpectType Predicate + const result = fn(2) // $ExpectType boolean + result // $ExpectType boolean + }) + it('with passed type - curried', () => { + const fn = either(x => x > 1)(x => x % 2 === 0) + fn // $ExpectType Predicate + const result = fn(2) + result // $ExpectType boolean + }) + it('no type passed', () => { + const fn = either( + x => { + x // $ExpectType any + return x > 1 + }, + x => { + x // $ExpectType any + return x % 2 === 0 + } + ) + const result = fn(2) + result // $ExpectType boolean + }) + it('no type passed - curried', () => { + const fn = either((x: number) => { + x // $ExpectType number + return x > 1 + })((x: number) => { + x // $ExpectType number + return x % 2 === 0 + }) + const result = fn(2) + result // $ExpectType boolean + }) +}) ```
-
- -Tests - -```javascript -import { last } from './last.js' - -test('with list', () => { - expect(last([ 1, 2, 3 ])).toBe(3) - expect(last([])).toBeUndefined() -}) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#either) -test('with string', () => { - expect(last('abc')).toBe('c') - expect(last('')).toBe('') -}) -``` +### empty -
+Try this R.empty example in Rambda REPL -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#last) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#empty) -### lastIndexOf +### endsWith ```typescript -lastIndexOf(target: T, list: T[]): number +endsWith(question: T, str: string): boolean ``` -It returns the last index of `target` in `list` array. - -`R.equals` is used to determine equality between `target` and members of `list`. - -If there is no such index, then `-1` is returned. - -```javascript -const list = [1, 2, 3, 1, 2, 3] -const result = [ - R.lastIndexOf(2, list), - R.lastIndexOf(4, list), -] -// => [4, -1] -``` +When iterable is a string, then it behaves as `String.prototype.endsWith`. +When iterable is a list, then it uses R.equals to determine if the target list ends in the same way as the given target. -Try this R.lastIndexOf example in Rambda REPL +Try this R.endsWith example in Rambda REPL
All TypeScript definitions ```typescript -lastIndexOf(target: T, list: T[]): number; -lastIndexOf(target: T): (list: T[]) => number; +endsWith(question: T, str: string): boolean; +endsWith(question: T): (str: string) => boolean; +endsWith(question: T[], list: T[]): boolean; +endsWith(question: T[]): (list: T[]) => boolean; ```
-R.lastIndexOf source +R.endsWith source ```javascript -import { _lastIndexOf } from './equals.js' +import { isArray } from './_internals/isArray.js' +import { equals } from './equals.js' -export function lastIndexOf(valueToFind, list){ - if (arguments.length === 1){ - return _list => _lastIndexOf(valueToFind, _list) +export function endsWith(target, iterable){ + if (arguments.length === 1) return _iterable => endsWith(target, _iterable) + + if (typeof iterable === 'string'){ + return iterable.endsWith(target) } + if (!isArray(target)) return false - return _lastIndexOf(valueToFind, list) + const diff = iterable.length - target.length + let correct = true + const filtered = target.filter((x, index) => { + if (!correct) return false + const result = equals(x, iterable[ index + diff ]) + if (!result) correct = false + + return result + }) + + return filtered.length === target.length } ``` @@ -13993,60 +3665,58 @@ export function lastIndexOf(valueToFind, list){ Tests ```javascript -import { lastIndexOf as lastIndexOfRamda } from 'ramda' +import { endsWith as endsWithRamda } from 'ramda' import { compareCombinations } from './_internals/testUtils.js' -import { possibleIterables, possibleTargets } from './indexOf.spec.js' -import { lastIndexOf } from './lastIndexOf.js' - -test('with NaN', () => { - expect(lastIndexOf(NaN, [ NaN ])).toBe(0) -}) - -test('will throw with bad input', () => { - expect(lastIndexOfRamda([], true)).toBe(-1) - expect(() => indexOf([], true)).toThrowErrorMatchingInlineSnapshot('"indexOf is not defined"') -}) +import { endsWith } from './endsWith.js' -test('without list of objects - no R.equals', () => { - expect(lastIndexOf(3, [ 1, 2, 3, 4 ])).toBe(2) - expect(lastIndexOf(10)([ 1, 2, 3, 4 ])).toBe(-1) +test('with string', () => { + expect(endsWith('bar', 'foo-bar')).toBeTrue() + expect(endsWith('baz')('foo-bar')).toBeFalse() }) -test('list of objects uses R.equals', () => { - const listOfObjects = [ { a : 1 }, { b : 2 }, { c : 3 } ] - expect(lastIndexOf({ c : 4 }, listOfObjects)).toBe(-1) - expect(lastIndexOf({ c : 3 }, listOfObjects)).toBe(2) +test('use R.equals with array', () => { + const list = [ { a : 1 }, { a : 2 }, { a : 3 } ] + expect(endsWith({ a : 3 }, list)).toBeFalse(), + expect(endsWith([ { a : 3 } ], list)).toBeTrue() + expect(endsWith([ { a : 2 }, { a : 3 } ], list)).toBeTrue() + expect(endsWith(list, list)).toBeTrue() + expect(endsWith([ { a : 1 } ], list)).toBeFalse() }) -test('list of arrays uses R.equals', () => { - const listOfLists = [ [ 1 ], [ 2, 3 ], [ 2, 3, 4 ], [ 2, 3 ], [ 1 ], [] ] - expect(lastIndexOf([], listOfLists)).toBe(5) - expect(lastIndexOf([ 1 ], listOfLists)).toBe(4) - expect(lastIndexOf([ 2, 3, 4 ], listOfLists)).toBe(2) - expect(lastIndexOf([ 2, 3, 5 ], listOfLists)).toBe(-1) -}) +export const possibleTargets = [ + NaN, + [ NaN ], + /foo/, + [ /foo/ ], + Promise.resolve(1), + [ Promise.resolve(1) ], + Error('foo'), + [ Error('foo') ], +] -test('with string as iterable', () => { - expect(() => lastIndexOf('a', 'abc')).toThrowErrorMatchingInlineSnapshot('"Cannot read property \'indexOf\' of abc"') - expect(lastIndexOfRamda('a', 'abc')).toBe(0) -}) +export const possibleIterables = [ + [ Promise.resolve(1), Promise.resolve(2) ], + [ /foo/, /bar/ ], + [ NaN ], + [ Error('foo'), Error('bar') ], +] describe('brute force', () => { compareCombinations({ - fn : lastIndexOf, - fnRamda : lastIndexOfRamda, + fn : endsWith, + fnRamda : endsWithRamda, firstInput : possibleTargets, secondInput : possibleIterables, callback : errorsCounters => { expect(errorsCounters).toMatchInlineSnapshot(` { "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 34, + "ERRORS_TYPE_MISMATCH": 0, "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 51, + "SHOULD_NOT_THROW": 0, "SHOULD_THROW": 0, - "TOTAL_TESTS": 170, + "TOTAL_TESTS": 32, } `) }, @@ -14061,756 +3731,832 @@ describe('brute force', () => { TypeScript test ```typescript -import {lastIndexOf} from 'rambda' +import {endsWith} from 'rambda' -const list = [1, 2, 3] +describe('R.endsWith - array', () => { + const target = [{a: 2}] + const input = [{a: 1}, {a: 2}] + it('happy', () => { + const result = endsWith(target, input) + result // $ExpectType boolean + }) + it('curried', () => { + const result = endsWith(target)(input) + result // $ExpectType boolean + }) +}) -describe('R.lastIndexOf', () => { +describe('R.endsWith - string', () => { + const target = 'bar' + const input = 'foo bar' it('happy', () => { - const result = lastIndexOf(2, list) - result // $ExpectType number + const result = endsWith(target, input) + result // $ExpectType boolean }) it('curried', () => { - const result = lastIndexOf(2)(list) - result // $ExpectType number + const result = endsWith(target)(input) + result // $ExpectType boolean }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lastIndexOf) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#endsWith) -### length +### eqBy -```typescript +Try this R.eqBy example in Rambda REPL -length(input: T[]): number -``` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#eqBy) -It returns the `length` property of list or string `input`. +### eqProps -```javascript -const result = [ - R.length([1, 2, 3, 4]), - R.length('foo'), -] -// => [4, 3] +It returns `true` if property `prop` in `obj1` is equal to property `prop` in `obj2` according to `R.equals`. + +Try this R.eqProps example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#eqProps) + +### equals + +```typescript + +equals(x: T, y: T): boolean ``` -Try this R.length example in Rambda REPL +It deeply compares `x` and `y` and returns `true` if they are equal. + +Try this R.equals example in Rambda REPL
All TypeScript definitions ```typescript -length(input: T[]): number; +equals(x: T, y: T): boolean; +equals(x: T): (y: T) => boolean; ```
-R.length source +R.equals source ```javascript import { isArray } from './_internals/isArray.js' +import { type } from './type.js' -export function length(x){ - if (isArray(x)) return x.length - if (typeof x === 'string') return x.length +export function _lastIndexOf(valueToFind, list){ + if (!isArray(list)) + throw new Error(`Cannot read property 'indexOf' of ${ list }`) - return NaN + const typeOfValue = type(valueToFind) + if (![ 'Array', 'NaN', 'Object', 'RegExp' ].includes(typeOfValue)) + return list.lastIndexOf(valueToFind) + + const { length } = list + let index = length + let foundIndex = -1 + + while (--index > -1 && foundIndex === -1) + if (equals(list[ index ], valueToFind)) + foundIndex = index + + return foundIndex } -``` -
+export function _indexOf(valueToFind, list){ + if (!isArray(list)) + throw new Error(`Cannot read property 'indexOf' of ${ list }`) + + const typeOfValue = type(valueToFind) + if (![ 'Array', 'NaN', 'Object', 'RegExp' ].includes(typeOfValue)) + return list.indexOf(valueToFind) + + let index = -1 + let foundIndex = -1 + const { length } = list + + while (++index < length && foundIndex === -1) + if (equals(list[ index ], valueToFind)) + foundIndex = index + + return foundIndex +} + +function _arrayFromIterator(iter){ + const list = [] + let next + while (!(next = iter.next()).done) + list.push(next.value) + + return list +} -
+function _compareSets(a, b){ + if (a.size !== b.size) + return false -Tests + const aList = _arrayFromIterator(a.values()) + const bList = _arrayFromIterator(b.values()) -```javascript -import { length as lengthRamda } from 'ramda' + const filtered = aList.filter(aInstance => _indexOf(aInstance, bList) === -1) -import { length } from './length.js' + return filtered.length === 0 +} -test('happy', () => { - expect(length('foo')).toBe(3) - expect(length([ 1, 2, 3 ])).toBe(3) - expect(length([])).toBe(0) -}) +function compareErrors(a, b){ + if (a.message !== b.message) return false + if (a.toString !== b.toString) return false -test('with empty string', () => { - expect(length('')).toBe(0) -}) + return a.toString() === b.toString() +} -test('with bad input returns NaN', () => { - expect(length(0)).toBeNaN() - expect(length({})).toBeNaN() - expect(length(null)).toBeNaN() - expect(length(undefined)).toBeNaN() -}) +function parseDate(maybeDate){ + if (!maybeDate.toDateString) return [ false ] -test('with length as property', () => { - const input1 = { length : '123' } - const input2 = { length : null } - const input3 = { length : '' } + return [ true, maybeDate.getTime() ] +} - expect(length(input1)).toBeNaN() - expect(lengthRamda(input1)).toBeNaN() - expect(length(input2)).toBeNaN() - expect(lengthRamda(input2)).toBeNaN() - expect(length(input3)).toBeNaN() - expect(lengthRamda(input3)).toBeNaN() -}) -``` +function parseRegex(maybeRegex){ + if (maybeRegex.constructor !== RegExp) return [ false ] -
+ return [ true, maybeRegex.toString() ] +} -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#length) +export function equals(a, b){ + if (arguments.length === 1) return _b => equals(a, _b) -### lens + if (Object.is(a, b)) return true -```typescript + const aType = type(a) -lens(getter: (s: S) => A, setter: (a: A, s: S) => S): Lens -``` + if (aType !== type(b)) return false + if (aType === 'Function') + return a.name === undefined ? false : a.name === b.name -It returns a `lens` for the given `getter` and `setter` functions. + if ([ 'NaN', 'Null', 'Undefined' ].includes(aType)) return true -The `getter` **gets** the value of the focus; the `setter` **sets** the value of the focus. + if ([ 'BigInt', 'Number' ].includes(aType)){ + if (Object.is(-0, a) !== Object.is(-0, b)) return false -The setter should not mutate the data structure. + return a.toString() === b.toString() + } -```javascript -const xLens = R.lens(R.prop('x'), R.assoc('x')); + if ([ 'Boolean', 'String' ].includes(aType)) + return a.toString() === b.toString() -R.view(xLens, {x: 1, y: 2}) // => 1 -R.set(xLens, 4, {x: 1, y: 2}) // => {x: 4, y: 2} -R.over(xLens, R.negate, {x: 1, y: 2}) // => {x: -1, y: 2} -``` + if (aType === 'Array'){ + const aClone = Array.from(a) + const bClone = Array.from(b) -Try this R.lens example in Rambda REPL + if (aClone.toString() !== bClone.toString()) + return false -
+ let loopArrayFlag = true + aClone.forEach((aCloneInstance, aCloneIndex) => { + if (loopArrayFlag) + if ( + aCloneInstance !== bClone[ aCloneIndex ] && + !equals(aCloneInstance, bClone[ aCloneIndex ]) + ) + loopArrayFlag = false -All TypeScript definitions + }) -```typescript -lens(getter: (s: S) => A, setter: (a: A, s: S) => S): Lens; -``` + return loopArrayFlag + } -
+ const aRegex = parseRegex(a) + const bRegex = parseRegex(b) -
+ if (aRegex[ 0 ]) + return bRegex[ 0 ] ? aRegex[ 1 ] === bRegex[ 1 ] : false + else if (bRegex[ 0 ]) return false -R.lens source + const aDate = parseDate(a) + const bDate = parseDate(b) -```javascript -export function lens(getter, setter){ - return function (functor){ - return function (target){ - return functor(getter(target)).map(focus => setter(focus, target)) - } - } -} -``` + if (aDate[ 0 ]) + return bDate[ 0 ] ? aDate[ 1 ] === bDate[ 1 ] : false + else if (bDate[ 0 ]) return false -
+ if (a instanceof Error){ + if (!(b instanceof Error)) return false -
+ return compareErrors(a, b) + } -TypeScript test + if (aType === 'Set') + return _compareSets(a, b) -```typescript -import {lens, assoc, lensProp, view, lensIndex, over, lensPath} from 'rambda' + if (aType === 'Object'){ + const aKeys = Object.keys(a) -interface Input { - foo: string, -} -const testObject: Input = { - foo: 'Jazz', -} + if (aKeys.length !== Object.keys(b).length) + return false -describe('R.lens', () => { - it('happy', () => { - const fn = lens((x: Input) => { - x.foo // $ExpectType string - return x.foo - }, assoc('name')) - fn // $ExpectType Lens - }) -}) + let loopObjectFlag = true + aKeys.forEach(aKeyInstance => { + if (loopObjectFlag){ + const aValue = a[ aKeyInstance ] + const bValue = b[ aKeyInstance ] -describe('R.lensProp', () => { - it('happy', () => { - const result = view(lensProp('foo'), testObject) - result // $ExpectType string - }) - it('issue 740', () => { - // @ts-expect-error - over(lensProp('x'), (n) => String(n), {x: 1}) - }) -}) + if (aValue !== bValue && !equals(aValue, bValue)) + loopObjectFlag = false -describe('R.lensIndex', () => { - const testList: Input[] = [{foo: 'bar'}, {foo: 'baz'}] - it('happy', () => { - const result = view(lensIndex(0), testList) - result // $ExpectType Input - result.foo // $ExpectType string - }) -}) + } + }) -describe('R.lensPath', () => { - const path = lensPath(['bar', 'a']) - it('happy', () => { - const result = view(path, testObject) - result // $ExpectType string - }) -}) + return loopObjectFlag + } -describe('R.view', () => { - const fooLens = lens((x: Input) => { - return x.foo - }, assoc('foo')) - it('happt', () => { - const result = view(fooLens, testObject) - result // $ExpectType string - }) -}) + return false +} ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lens) - -### lensIndex +
-```typescript +Tests -lensIndex(n: number): Lens -``` +```javascript +import { equals as equalsRamda } from 'ramda' -It returns a lens that focuses on specified `index`. +import { compareCombinations } from './_internals/testUtils.js' +import { variousTypes } from './benchmarks/_utils.js' +import { equals } from './equals.js' -```javascript -const list = ['a', 'b', 'c'] -const headLens = R.lensIndex(0) +test('compare functions', () => { + function foo(){} + function bar(){} + const baz = () => {} -R.view(headLens, list) // => 'a' -R.set(headLens, 'x', list) // => ['x', 'b', 'c'] -R.over(headLens, R.toUpper, list) // => ['A', 'b', 'c'] -``` + const expectTrue = equals(foo, foo) + const expectFalseFirst = equals(foo, bar) + const expectFalseSecond = equals(foo, baz) -Try this R.lensIndex example in Rambda REPL + expect(expectTrue).toBeTrue() + expect(expectFalseFirst).toBeFalse() + expect(expectFalseSecond).toBeFalse() +}) -
+test('with array of objects', () => { + const list1 = [ { a : 1 }, [ { b : 2 } ] ] + const list2 = [ { a : 1 }, [ { b : 2 } ] ] + const list3 = [ { a : 1 }, [ { b : 3 } ] ] -All TypeScript definitions + expect(equals(list1, list2)).toBeTrue() + expect(equals(list1, list3)).toBeFalse() +}) -```typescript -lensIndex(n: number): Lens; -lensIndex(n: N): Lens; -``` +test('with regex', () => { + expect(equals(/s/, /s/)).toBeTrue() + expect(equals(/s/, /d/)).toBeFalse() + expect(equals(/a/gi, /a/gi)).toBeTrue() + expect(equals(/a/gim, /a/gim)).toBeTrue() + expect(equals(/a/gi, /a/i)).toBeFalse() +}) -
+test('not a number', () => { + expect(equals([ NaN ], [ NaN ])).toBeTrue() +}) -
+test('new number', () => { + expect(equals(new Number(0), new Number(0))).toBeTrue() + expect(equals(new Number(0), new Number(1))).toBeFalse() + expect(equals(new Number(1), new Number(0))).toBeFalse() +}) -R.lensIndex source +test('new string', () => { + expect(equals(new String(''), new String(''))).toBeTrue() + expect(equals(new String(''), new String('x'))).toBeFalse() + expect(equals(new String('x'), new String(''))).toBeFalse() + expect(equals(new String('foo'), new String('foo'))).toBeTrue() + expect(equals(new String('foo'), new String('bar'))).toBeFalse() + expect(equals(new String('bar'), new String('foo'))).toBeFalse() +}) -```javascript -import { lens } from './lens.js' -import { nth } from './nth.js' -import { update } from './update.js' +test('new Boolean', () => { + expect(equals(new Boolean(true), new Boolean(true))).toBeTrue() + expect(equals(new Boolean(false), new Boolean(false))).toBeTrue() + expect(equals(new Boolean(true), new Boolean(false))).toBeFalse() + expect(equals(new Boolean(false), new Boolean(true))).toBeFalse() +}) -export function lensIndex(index){ - return lens(nth(index), update(index)) -} -``` +test('new Error', () => { + expect(equals(new Error('XXX'), {})).toBeFalse() + expect(equals(new Error('XXX'), new TypeError('XXX'))).toBeFalse() + expect(equals(new Error('XXX'), new Error('YYY'))).toBeFalse() + expect(equals(new Error('XXX'), new Error('XXX'))).toBeTrue() + expect(equals(new Error('XXX'), new TypeError('YYY'))).toBeFalse() + expect(equals(new Error('XXX'), new Error('XXX'))).toBeTrue() +}) -
+test('with dates', () => { + expect(equals(new Date(0), new Date(0))).toBeTrue() + expect(equals(new Date(1), new Date(1))).toBeTrue() + expect(equals(new Date(0), new Date(1))).toBeFalse() + expect(equals(new Date(1), new Date(0))).toBeFalse() + expect(equals(new Date(0), {})).toBeFalse() + expect(equals({}, new Date(0))).toBeFalse() +}) -
+test('ramda spec', () => { + expect(equals({}, {})).toBeTrue() -Tests + expect(equals({ + a : 1, + b : 2, + }, + { + a : 1, + b : 2, + })).toBeTrue() -```javascript -import { compose } from './compose.js' -import { keys } from './keys.js' -import { lensIndex } from './lensIndex.js' -import { over } from './over.js' -import { set } from './set.js' -import { view } from './view.js' + expect(equals({ + a : 2, + b : 3, + }, + { + a : 2, + b : 3, + })).toBeTrue() -const testList = [ { a : 1 }, { b : 2 }, { c : 3 } ] + expect(equals({ + a : 2, + b : 3, + }, + { + a : 3, + b : 3, + })).toBeFalse() -test('focuses list element at the specified index', () => { - expect(view(lensIndex(0), testList)).toEqual({ a : 1 }) + expect(equals({ + a : 2, + b : 3, + c : 1, + }, + { + a : 2, + b : 3, + })).toBeFalse() }) -test('returns undefined if the specified index does not exist', () => { - expect(view(lensIndex(10), testList)).toBeUndefined() +test('works with boolean tuple', () => { + expect(equals([ true, false ], [ true, false ])).toBeTrue() + expect(equals([ true, false ], [ true, true ])).toBeFalse() }) -test('sets the list value at the specified index', () => { - expect(set( - lensIndex(0), 0, testList - )).toEqual([ 0, { b : 2 }, { c : 3 } ]) -}) +test('works with equal objects within array', () => { + const objFirst = { + a : { + b : 1, + c : 2, + d : [ 1 ], + }, + } + const objSecond = { + a : { + b : 1, + c : 2, + d : [ 1 ], + }, + } -test('applies function to the value at the specified list index', () => { - expect(over( - lensIndex(2), keys, testList - )).toEqual([ { a : 1 }, { b : 2 }, [ 'c' ] ]) + const x = [ 1, 2, objFirst, null, '', [] ] + const y = [ 1, 2, objSecond, null, '', [] ] + expect(equals(x, y)).toBeTrue() }) -test('can be composed', () => { - const nestedList = [ 0, [ 10, 11, 12 ], 1, 2 ] - const composedLens = compose(lensIndex(1), lensIndex(0)) +test('works with different objects within array', () => { + const objFirst = { a : { b : 1 } } + const objSecond = { a : { b : 2 } } - expect(view(composedLens, nestedList)).toBe(10) + const x = [ 1, 2, objFirst, null, '', [] ] + const y = [ 1, 2, objSecond, null, '', [] ] + expect(equals(x, y)).toBeFalse() }) -test('set s (get s) === s', () => { - expect(set( - lensIndex(0), view(lensIndex(0), testList), testList - )).toEqual(testList) -}) +test('works with undefined as second argument', () => { + expect(equals(1, undefined)).toBeFalse() -test('get (set s v) === v', () => { - expect(view(lensIndex(0), set( - lensIndex(0), 0, testList - ))).toBe(0) + expect(equals(undefined, undefined)).toBeTrue() }) -test('get (set(set s v1) v2) === v2', () => { - expect(view(lensIndex(0), - set( - lensIndex(0), 11, set( - lensIndex(0), 10, testList - ) - ))).toBe(11) +test('compare sets', () => { + const toCompareDifferent = new Set([ { a : 1 }, { a : 2 } ]) + const toCompareSame = new Set([ { a : 1 }, { a : 2 }, { a : 1 } ]) + const testSet = new Set([ { a : 1 }, { a : 2 }, { a : 1 } ]) + expect(equals(toCompareSame, testSet)).toBeTruthy() + expect(equals(toCompareDifferent, testSet)).toBeFalsy() + expect(equalsRamda(toCompareSame, testSet)).toBeTruthy() + expect(equalsRamda(toCompareDifferent, testSet)).toBeFalsy() }) -``` - -
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lensIndex) +test('compare simple sets', () => { + const testSet = new Set([ '2', '3', '3', '2', '1' ]) + expect(equals(new Set([ '3', '2', '1' ]), testSet)).toBeTruthy() + expect(equals(new Set([ '3', '2', '0' ]), testSet)).toBeFalsy() +}) -### lensPath +test('various examples', () => { + expect(equals([ 1, 2, 3 ])([ 1, 2, 3 ])).toBeTrue() -```typescript + expect(equals([ 1, 2, 3 ], [ 1, 2 ])).toBeFalse() -lensPath(path: [K0]): Lens -``` + expect(equals(1, 1)).toBeTrue() -It returns a lens that focuses on specified `path`. + expect(equals(1, '1')).toBeFalse() -```javascript -const lensPath = R.lensPath(['x', 0, 'y']) -const input = {x: [{y: 2, z: 3}, {y: 4, z: 5}]} + expect(equals({}, {})).toBeTrue() -R.view(lensPath, input) // => 2 + expect(equals({ + a : 1, + b : 2, + }, + { + a : 1, + b : 2, + })).toBeTrue() -R.set(lensPath, 1, input) -// => {x: [{y: 1, z: 3}, {y: 4, z: 5}]} + expect(equals({ + a : 1, + b : 2, + }, + { + a : 1, + b : 1, + })).toBeFalse() -R.over(xHeadYLens, R.negate, input) -// => {x: [{y: -2, z: 3}, {y: 4, z: 5}]} -``` + expect(equals({ + a : 1, + b : false, + }, + { + a : 1, + b : 1, + })).toBeFalse() -
Try this R.lensPath example in Rambda REPL + expect(equals({ + a : 1, + b : 2, + }, + { + a : 1, + b : 2, + c : 3, + })).toBeFalse() -
+ expect(equals({ + x : { + a : 1, + b : 2, + }, + }, + { + x : { + a : 1, + b : 2, + c : 3, + }, + })).toBeFalse() -All TypeScript definitions + expect(equals({ + a : 1, + b : 2, + }, + { + a : 1, + b : 3, + })).toBeFalse() -```typescript -lensPath(path: [K0]): Lens; -lensPath( - path: [K0, K1], -): Lens; -lensPath< - S, - K0 extends keyof S = keyof S, - K1 extends keyof S[K0] = keyof S[K0], - K2 extends keyof S[K0][K1] = keyof S[K0][K1] ->(path: [K0, K1, K2]): Lens; -lensPath< - S, - K0 extends keyof S = keyof S, - K1 extends keyof S[K0] = keyof S[K0], - K2 extends keyof S[K0][K1] = keyof S[K0][K1], - K3 extends keyof S[K0][K1][K2] = keyof S[K0][K1][K2] ->(path: [K0, K1, K2, K3]): Lens; -lensPath< - S, - K0 extends keyof S = keyof S, - K1 extends keyof S[K0] = keyof S[K0], - K2 extends keyof S[K0][K1] = keyof S[K0][K1], - K3 extends keyof S[K0][K1][K2] = keyof S[K0][K1][K2], - K4 extends keyof S[K0][K1][K2][K3] = keyof S[K0][K1][K2][K3] ->(path: [K0, K1, K2, K3, K4]): Lens; -lensPath< - S, - K0 extends keyof S = keyof S, - K1 extends keyof S[K0] = keyof S[K0], - K2 extends keyof S[K0][K1] = keyof S[K0][K1], - K3 extends keyof S[K0][K1][K2] = keyof S[K0][K1][K2], - K4 extends keyof S[K0][K1][K2][K3] = keyof S[K0][K1][K2][K3], - K5 extends keyof S[K0][K1][K2][K3][K4] = keyof S[K0][K1][K2][K3][K4] ->(path: [K0, K1, K2, K3, K4, K5]): Lens; -lensPath(path: Path): Lens; -``` - -
- -
- -R.lensPath source - -```javascript -import { assocPath } from './assocPath.js' -import { lens } from './lens.js' -import { path } from './path.js' + expect(equals({ a : { b : { c : 1 } } }, { a : { b : { c : 1 } } })).toBeTrue() -export function lensPath(key){ - return lens(path(key), assocPath(key)) -} -``` + expect(equals({ a : { b : { c : 1 } } }, { a : { b : { c : 2 } } })).toBeFalse() -
+ expect(equals({ a : {} }, { a : {} })).toBeTrue() -
+ expect(equals('', '')).toBeTrue() -Tests + expect(equals('foo', 'foo')).toBeTrue() -```javascript -import { compose } from './compose.js' -import { identity } from './identity.js' -import { inc } from './inc.js' -import { lensPath } from './lensPath.js' -import { lensProp } from './lensProp.js' -import { over } from './over.js' -import { set } from './set.js' -import { view } from './view.js' + expect(equals('foo', 'bar')).toBeFalse() -const testObj = { - a : [ { b : 1 }, { b : 2 } ], - d : 3, -} + expect(equals(0, false)).toBeFalse() -test('view', () => { - expect(view(lensPath('d'), testObj)).toBe(3) - expect(view(lensPath('a.0.b'), testObj)).toBe(1) - // this is different to ramda, as ramda will return a clone of the input object - expect(view(lensPath(''), testObj)).toBeUndefined() -}) + expect(equals(/\s/g, null)).toBeFalse() -test('set', () => { - expect(set( - lensProp('d'), 0, testObj - )).toEqual({ - a : [ { b : 1 }, { b : 2 } ], - d : 0, - }) - expect(set( - lensPath('a.0.b'), 0, testObj - )).toEqual({ - a : [ { b : 0 }, { b : 2 } ], - d : 3, - }) - expect(set( - lensPath('a.0.X'), 0, testObj - )).toEqual({ - a : [ - { - b : 1, - X : 0, - }, - { b : 2 }, - ], - d : 3, - }) - expect(set( - lensPath([]), 0, testObj - )).toBe(0) + expect(equals(null, null)).toBeTrue() + + expect(equals(false)(null)).toBeFalse() }) -test('over', () => { - expect(over( - lensPath('d'), inc, testObj - )).toEqual({ - a : [ { b : 1 }, { b : 2 } ], - d : 4, - }) - expect(over( - lensPath('a.1.b'), inc, testObj - )).toEqual({ - a : [ { b : 1 }, { b : 3 } ], - d : 3, - }) - expect(over( - lensProp('X'), identity, testObj - )).toEqual({ - a : [ { b : 1 }, { b : 2 } ], - d : 3, - X : undefined, - }) - expect(over( - lensPath('a.0.X'), identity, testObj - )).toEqual({ - a : [ - { - b : 1, - X : undefined, - }, - { b : 2 }, - ], - d : 3, - }) +test('with custom functions', () => { + function foo(){ + return 1 + } + foo.prototype.toString = () => '' + const result = equals(foo, foo) + + expect(result).toBeTrue() }) -test('compose', () => { - const composedLens = compose(lensPath('a'), lensPath('1.b')) - expect(view(composedLens, testObj)).toBe(2) +test('with classes', () => { + class Foo{} + const foo = new Foo() + const result = equals(foo, foo) + + expect(result).toBeTrue() }) -test('set s (get s) === s', () => { - expect(set( - lensPath([ 'd' ]), view(lensPath([ 'd' ]), testObj), testObj - )).toEqual(testObj) - expect(set( - lensPath([ 'a', 0, 'b' ]), - view(lensPath([ 'a', 0, 'b' ]), testObj), - testObj - )).toEqual(testObj) +test('with negative zero', () => { + expect(equals(-0, -0)).toBeTrue() + expect(equals(-0, 0)).toBeFalse() + expect(equals(0, 0)).toBeTrue() + expect(equals(-0, 1)).toBeFalse() }) -test('get (set s v) === v', () => { - expect(view(lensPath([ 'd' ]), set( - lensPath([ 'd' ]), 0, testObj - ))).toBe(0) - expect(view(lensPath([ 'a', 0, 'b' ]), set( - lensPath([ 'a', 0, 'b' ]), 0, testObj - ))).toBe(0) +test('with big int', () => { + const a = BigInt(9007199254740991) + const b = BigInt(9007199254740991) + const c = BigInt(7007199254740991) + expect(equals(a, b)).toBeTrue() + expect(equals(a, c)).toBeFalse() }) -test('get (set(set s v1) v2) === v2', () => { - const p = [ 'd' ] - const q = [ 'a', 0, 'b' ] - expect(view(lensPath(p), set( - lensPath(p), 11, set( - lensPath(p), 10, testObj - ) - ))).toBe(11) - expect(view(lensPath(q), set( - lensPath(q), 11, set( - lensPath(q), 10, testObj - ) - ))).toBe(11) +describe('brute force', () => { + compareCombinations({ + callback : errorsCounters => { + expect(errorsCounters).toMatchInlineSnapshot(` +{ + "ERRORS_MESSAGE_MISMATCH": 0, + "ERRORS_TYPE_MISMATCH": 0, + "RESULTS_MISMATCH": 0, + "SHOULD_NOT_THROW": 0, + "SHOULD_THROW": 0, + "TOTAL_TESTS": 289, +} +`) + }, + firstInput : variousTypes, + fn : equals, + fnRamda : equalsRamda, + secondInput : variousTypes, + }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lensPath) +
-### lensProp +TypeScript test ```typescript +import {equals} from 'rambda' -lensProp(prop: K): Lens +describe('R.equals', () => { + it('happy', () => { + const result = equals(4, 1) + result // $ExpectType boolean + }) + it('with object', () => { + const foo = {a: 1} + const bar = {a: 2} + const result = equals(foo, bar) + result // $ExpectType boolean + }) + it('curried', () => { + const result = equals(4)(1) + + result // $ExpectType boolean + }) +}) ``` -It returns a lens that focuses on specified property `prop`. +
-```javascript -const xLens = R.lensProp('x'); -const input = {x: 1, y: 2} +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#equals) -R.view(xLens, input) // => 1 +### evolve -R.set(xLens, 4, input) -// => {x: 4, y: 2} +```typescript -R.over(xLens, R.negate, input) -// => {x: -1, y: 2} +evolve(rules: ((x: T) => U)[], list: T[]): U[] ``` -Try this R.lensProp example in Rambda REPL +It takes object or array of functions as set of rules. These `rules` are applied to the `iterable` input to produce the result. + +Try this R.evolve example in Rambda REPL
All TypeScript definitions ```typescript -lensProp(prop: K): Lens; +evolve(rules: ((x: T) => U)[], list: T[]): U[]; +evolve(rules: ((x: T) => U)[]) : (list: T[]) => U[]; +evolve>(rules: E, obj: V): Evolve; +evolve(rules: E): >(obj: V) => Evolve; ```
-R.lensProp source +R.evolve source ```javascript -import { assoc } from './assoc.js' -import { lens } from './lens.js' -import { prop } from './prop.js' - -export function lensProp(key){ - return lens(prop(key), assoc(key)) -} -``` - -
- -
- -Tests +import { isArray } from './_internals/isArray.js' +import { mapArray, mapObject } from './map.js' +import { type } from './type.js' -```javascript -import { compose } from './compose.js' -import { identity } from './identity.js' -import { inc } from './inc.js' -import { lensProp } from './lensProp.js' -import { over } from './over.js' -import { set } from './set.js' -import { view } from './view.js' +export function evolveArray(rules, list){ + return mapArray( + (x, i) => { + if (type(rules[ i ]) === 'Function'){ + return rules[ i ](x) + } -const testObj = { - a : 1, - b : 2, - c : 3, + return x + }, + list, + true + ) } -test('focuses object the specified object property', () => { - expect(view(lensProp('a'), testObj)).toBe(1) -}) - -test('returns undefined if the specified property does not exist', () => { - expect(view(lensProp('X'), testObj)).toBeUndefined() -}) - -test('sets the value of the object property specified', () => { - expect(set( - lensProp('a'), 0, testObj - )).toEqual({ - a : 0, - b : 2, - c : 3, - }) -}) - -test('adds the property to the object if it doesn\'t exist', () => { - expect(set( - lensProp('d'), 4, testObj - )).toEqual({ - a : 1, - b : 2, - c : 3, - d : 4, - }) -}) +export function evolveObject(rules, iterable){ + return mapObject((x, prop) => { + if (type(x) === 'Object'){ + const typeRule = type(rules[ prop ]) + if (typeRule === 'Function'){ + return rules[ prop ](x) + } + if (typeRule === 'Object'){ + return evolve(rules[ prop ], x) + } -test('applies function to the value of the specified object property', () => { - expect(over( - lensProp('a'), inc, testObj - )).toEqual({ - a : 2, - b : 2, - c : 3, - }) -}) + return x + } + if (type(rules[ prop ]) === 'Function'){ + return rules[ prop ](x) + } -test('applies function to undefined and adds the property if it doesn\'t exist', () => { - expect(over( - lensProp('X'), identity, testObj - )).toEqual({ - a : 1, - b : 2, - c : 3, - X : undefined, - }) -}) + return x + }, iterable) +} -test('can be composed', () => { - const nestedObj = { - a : { b : 1 }, - c : 2, +export function evolve(rules, iterable){ + if (arguments.length === 1){ + return _iterable => evolve(rules, _iterable) } - const composedLens = compose(lensProp('a'), lensProp('b')) + const rulesType = type(rules) + const iterableType = type(iterable) - expect(view(composedLens, nestedObj)).toBe(1) -}) + if (iterableType !== rulesType){ + throw new Error('iterableType !== rulesType') + } -test('set s (get s) === s', () => { - expect(set( - lensProp('a'), view(lensProp('a'), testObj), testObj - )).toEqual(testObj) -}) + if (![ 'Object', 'Array' ].includes(rulesType)){ + throw new Error(`'iterable' and 'rules' are from wrong type ${ rulesType }`) + } -test('get (set s v) === v', () => { - expect(view(lensProp('a'), set( - lensProp('a'), 0, testObj - ))).toBe(0) -}) + if (iterableType === 'Object'){ + return evolveObject(rules, iterable) + } -test('get (set(set s v1) v2) === v2', () => { - expect(view(lensProp('a'), - set( - lensProp('a'), 11, set( - lensProp('a'), 10, testObj - ) - ))).toBe(11) -}) + return evolveArray(rules, iterable) +} ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lensProp) - -### lt - -```typescript +
-lt(x: T, y: U): boolean -``` +Tests ```javascript -const result = [R.lt(2, 1), R.lt(2, 3)] -// => [false, true] -``` - -Try this R.lt example in Rambda REPL +import { evolve as evolveRamda } from 'ramda' -
+import { add } from '../rambda.js' +import { compareCombinations, compareToRamda } from './_internals/testUtils.js' +import { evolve } from './evolve.js' -All TypeScript definitions +test('happy', () => { + const rules = { + foo : add(1), + nested : { bar : x => Object.keys(x).length }, + } + const input = { + a : 1, + foo : 2, + nested : { bar : { z : 3 } }, + } + const result = evolve(rules, input) + expect(result).toEqual({ + a : 1, + foo : 3, + nested : { bar : 1 }, + }) +}) -```typescript -lt(x: T, y: U): boolean; -lt(x: T): (y: U) => boolean; -``` +test('nested rule is wrong', () => { + const rules = { + foo : add(1), + nested : { bar : 10 }, + } + const input = { + a : 1, + foo : 2, + nested : { bar : { z : 3 } }, + } + const result = evolve(rules)(input) + expect(result).toEqual({ + a : 1, + foo : 3, + nested : { bar : { z : 3 } }, + }) +}) -
+test('is recursive', () => { + const rules = { + nested : { + second : add(-1), + third : add(1), + }, + } + const object = { + first : 1, + nested : { + second : 2, + third : 3, + }, + } + const expected = { + first : 1, + nested : { + second : 1, + third : 4, + }, + } + const result = evolve(rules, object) + expect(result).toEqual(expected) +}) -
+test('ignores primitive values', () => { + const rules = { + n : 2, + m : 'foo', + } + const object = { + n : 0, + m : 1, + } + const expected = { + n : 0, + m : 1, + } + const result = evolve(rules, object) + expect(result).toEqual(expected) +}) -R.lt source +test('with array', () => { + const rules = [ add(1), add(-1) ] + const list = [ 100, 1400 ] + const expected = [ 101, 1399 ] + const result = evolve(rules, list) + expect(result).toEqual(expected) +}) -```javascript -export function lt(a, b){ - if (arguments.length === 1) - return _b => lt(a, _b) +const rulesObject = { a : add(1) } +const rulesList = [ add(1) ] +const possibleIterables = [ null, undefined, '', 42, [], [ 1 ], { a : 1 } ] +const possibleRules = [ ...possibleIterables, rulesList, rulesObject ] - return a < b -} +describe('brute force', () => { + compareCombinations({ + firstInput : possibleRules, + callback : errorsCounters => { + expect(errorsCounters).toMatchInlineSnapshot(` + { + "ERRORS_MESSAGE_MISMATCH": 0, + "ERRORS_TYPE_MISMATCH": 4, + "RESULTS_MISMATCH": 0, + "SHOULD_NOT_THROW": 51, + "SHOULD_THROW": 0, + "TOTAL_TESTS": 63, + } + `) + }, + secondInput : possibleIterables, + fn : evolve, + fnRamda : evolveRamda, + }) +}) ```
@@ -14820,184 +4566,158 @@ export function lt(a, b){ TypeScript test ```typescript -import {lt} from 'rambda' +import {evolve, add} from 'rambda' -describe('R.lt', () => { +describe('R.evolve', () => { it('happy', () => { - const result = lt(1, 2) - const curriedResult = lt(2)(3) - result // $ExpectType boolean - curriedResult // $ExpectType boolean + const input = { + foo: 2, + nested: { + a: 1, + bar: 3, + }, + } + const rules = { + foo: add(1), + nested: { + a: add(-1), + bar: add(1), + }, + } + const result = evolve(rules, input) + const curriedResult = evolve(rules)(input) + + result.nested.a // $ExpectType number + curriedResult.nested.a // $ExpectType number + result.nested.bar // $ExpectType number + result.foo // $ExpectType number + }) + it('with array', () => { + const rules = [String, String] + const input = [100, 1400] + const result = evolve(rules, input) + const curriedResult = evolve(rules)(input) + result // $ExpectType string[] + curriedResult // $ExpectType string[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lt) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#evolve) -### lte +### F ```typescript -lte(x: T, y: U): boolean -``` - -```javascript -const result = [R.lte(2, 1), R.lte(2, 2), R.lte(2, 3)] -// => [false, true, true] +F(): boolean ``` -Try this R.lte example in Rambda REPL +Try this R.F example in Rambda REPL
All TypeScript definitions ```typescript -lte(x: T, y: U): boolean; -lte(x: T): (y: U) => boolean; +F(): boolean; ```
-R.lte source +R.F source ```javascript -export function lte(a, b){ - if (arguments.length === 1) - return _b => lte(a, _b) - - return a <= b +export function F(){ + return false } ```
-
- -TypeScript test - -```typescript -import {lte} from 'rambda' - -describe('R.lte', () => { - it('happy', () => { - const result = lte(1, 2) - const curriedResult = lte(2)(3) - result // $ExpectType boolean - curriedResult // $ExpectType boolean - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lte) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#F) -### map +### filter ```typescript -map(fn: ObjectIterator, iterable: Dictionary): Dictionary +filter(predicate: Predicate): (input: T[]) => T[] ``` -It returns the result of looping through `iterable` with `fn`. - -It works with both array and object. - -> :boom: Unlike Ramda's `map`, here property and input object are passed as arguments to `fn`, when `iterable` is an object. - -```javascript -const fn = x => x * 2 -const fnWhenObject = (val, prop)=>{ - return `${prop}-${val}` -} - -const iterable = [1, 2] -const obj = {a: 1, b: 2} - -const result = [ - R.map(fn, iterable), - R.map(fnWhenObject, obj) -] -// => [ [2, 4], {a: 'a-1', b: 'b-2'}] -``` +It filters list or object `input` using a `predicate` function. -Try this R.map example in Rambda REPL +Try this R.filter example in Rambda REPL
All TypeScript definitions - -```typescript -map(fn: ObjectIterator, iterable: Dictionary): Dictionary; -map(fn: Iterator, iterable: T[]): U[]; -map(fn: Iterator): (iterable: T[]) => U[]; -map(fn: ObjectIterator): (iterable: Dictionary) => Dictionary; -map(fn: Iterator): (iterable: T[]) => T[]; -map(fn: Iterator, iterable: T[]): T[]; + +```typescript +filter(predicate: Predicate): (input: T[]) => T[]; +filter(predicate: Predicate, input: T[]): T[]; +filter(predicate: ObjectPredicate): (x: Dictionary) => Dictionary; +filter(predicate: ObjectPredicate, x: Dictionary): Dictionary; ```
-R.map source +R.filter source ```javascript -import { INCORRECT_ITERABLE_INPUT } from './_internals/constants.js' import { isArray } from './_internals/isArray.js' -import { keys } from './_internals/keys.js' - -export function mapArray( - fn, list, isIndexed = false -){ - let index = 0 - const willReturn = Array(list.length) - while (index < list.length){ - willReturn[ index ] = isIndexed ? fn(list[ index ], index) : fn(list[ index ]) +export function filterObject(predicate, obj){ + const willReturn = {} - index++ + for (const prop in obj){ + if (predicate( + obj[ prop ], prop, obj + )){ + willReturn[ prop ] = obj[ prop ] + } } return willReturn } -export function mapObject(fn, obj){ - if (arguments.length === 1){ - return _obj => mapObject(fn, _obj) - } +export function filterArray( + predicate, list, indexed = false +){ let index = 0 - const objKeys = keys(obj) - const len = objKeys.length - const willReturn = {} + const len = list.length + const willReturn = [] while (index < len){ - const key = objKeys[ index ] - willReturn[ key ] = fn( - obj[ key ], key, obj - ) + const predicateResult = indexed ? + predicate(list[ index ], index) : + predicate(list[ index ]) + if (predicateResult){ + willReturn.push(list[ index ]) + } + index++ } return willReturn } -export const mapObjIndexed = mapObject - -export function map(fn, iterable){ - if (arguments.length === 1) return _iterable => map(fn, _iterable) +export function filter(predicate, iterable){ + if (arguments.length === 1) + return _iterable => filter(predicate, _iterable) if (!iterable){ - throw new Error(INCORRECT_ITERABLE_INPUT) + throw new Error('Incorrect iterable input') } - if (isArray(iterable)) return mapArray(fn, iterable) + if (isArray(iterable)) return filterArray( + predicate, iterable, false + ) - return mapObject(fn, iterable) + return filterObject(predicate, iterable) } ``` @@ -15008,188 +4728,66 @@ export function map(fn, iterable){ Tests ```javascript -import { map as mapRamda } from 'ramda' +import { filter as filterRamda } from 'ramda' -import { map } from './map.js' +import { filter } from './filter.js' +import { T } from './T.js' -const double = x => x * 2 +const sampleObject = { + a : 1, + b : 2, + c : 3, + d : 4, +} -describe('with array', () => { - it('happy', () => { - expect(map(double, [ 1, 2, 3 ])).toEqual([ 2, 4, 6 ]) - }) +test('happy', () => { + const isEven = n => n % 2 === 0 - it('curried', () => { - expect(map(double)([ 1, 2, 3 ])).toEqual([ 2, 4, 6 ]) - }) + expect(filter(isEven, [ 1, 2, 3, 4 ])).toEqual([ 2, 4 ]) + expect(filter(isEven, { + a : 1, + b : 2, + d : 3, + })).toEqual({ b : 2 }) }) -describe('with object', () => { +test('predicate when input is object', () => { const obj = { a : 1, b : 2, } + const predicate = ( + val, prop, inputObject + ) => { + expect(inputObject).toEqual(obj) + expect(typeof prop).toBe('string') - it('happy', () => { - expect(map(double, obj)).toEqual({ - a : 2, - b : 4, - }) - }) - - it('property as second and input object as third argument', () => { - const obj = { - a : 1, - b : 2, - } - const iterator = ( - val, prop, inputObject - ) => { - expect(prop).toBeString() - expect(inputObject).toEqual(obj) - - return val * 2 - } - - expect(map(iterator)(obj)).toEqual({ - a : 2, - b : 4, - }) - }) -}) - -test('bad inputs difference between Ramda and Rambda', () => { - expect(() => map(double, null)).toThrowErrorMatchingInlineSnapshot('"Incorrect iterable input"') - expect(() => map(double)(undefined)).toThrowErrorMatchingInlineSnapshot('"Incorrect iterable input"') - expect(() => mapRamda(double, null)).toThrowErrorMatchingInlineSnapshot('"Cannot read properties of null (reading \'fantasy-land/map\')"') - expect(() => - mapRamda(double, undefined)).toThrowErrorMatchingInlineSnapshot('"Cannot read properties of undefined (reading \'fantasy-land/map\')"') -}) -``` - -
- -
- -TypeScript test - -```typescript -import {map} from 'rambda' - -describe('R.map with arrays', () => { - it('iterable returns the same type as the input', () => { - const result = map( - (x: number) => { - x // $ExpectType number - return x + 2 - }, - [1, 2, 3] - ) - result // $ExpectType number[] - }) - it('iterable returns the same type as the input - curried', () => { - const result = map((x: number) => { - x // $ExpectType number - return x + 2 - })([1, 2, 3]) - result // $ExpectType number[] - }) - it('iterable returns different type as the input', () => { - const result = map( - (x: number) => { - x // $ExpectType number - return String(x) - }, - [1, 2, 3] - ) - result // $ExpectType string[] - }) -}) - -describe('R.map with objects', () => { - it('iterable with all three arguments - curried', () => { - // It requires dummy third typing argument - // in order to identify compared to curry typings for arrays - // ============================================ - const result = map((a, b, c) => { - a // $ExpectType number - b // $ExpectType string - c // $ExpectType Dictionary - return `${a}` - })({a: 1, b: 2}) - result // $ExpectType Dictionary - }) - it('iterable with all three arguments', () => { - const result = map( - (a, b, c) => { - a // $ExpectType number - b // $ExpectType string - c // $ExpectType Dictionary - return `${a}` - }, - {a: 1, b: 2} - ) - result // $ExpectType Dictionary - }) - it('iterable with property argument', () => { - const result = map( - (a, b) => { - a // $ExpectType number - b // $ExpectType string - return `${a}` - }, - {a: 1, b: 2} - ) - result // $ExpectType Dictionary - }) - it('iterable with no property argument', () => { - const result = map( - a => { - a // $ExpectType number - return `${a}` - }, - {a: 1, b: 2} - ) - result // $ExpectType Dictionary - }) + return val < 2 + } + expect(filter(predicate, obj)).toEqual({ a : 1 }) }) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#map) - -### mapObjIndexed - -```typescript - -mapObjIndexed(fn: ObjectIterator, iterable: Dictionary): Dictionary -``` - -It works the same way as `R.map` does for objects. It is added as Ramda also has this method. - -```javascript -const fn = (val, prop) => { - return `${prop}-${val}` -} - -const obj = {a: 1, b: 2} - -const result = R.mapObjIndexed(fn, obj) -// => {a: 'a-1', b: 'b-2'} -``` - -Try this R.mapObjIndexed example in Rambda REPL -
+test('with object', () => { + const isEven = n => n % 2 === 0 + const result = filter(isEven, sampleObject) + const expectedResult = { + b : 2, + d : 4, + } -All TypeScript definitions + expect(result).toEqual(expectedResult) +}) -```typescript -mapObjIndexed(fn: ObjectIterator, iterable: Dictionary): Dictionary; -mapObjIndexed(fn: ObjectIterator, iterable: Dictionary): Dictionary; -mapObjIndexed(fn: ObjectIterator): (iterable: Dictionary) => Dictionary; -mapObjIndexed(fn: ObjectIterator): (iterable: Dictionary) => Dictionary; +test('bad inputs difference between Ramda and Rambda', () => { + expect(() => filter(T, null)).toThrowWithMessage(Error, + 'Incorrect iterable input') + expect(() => filter(T)(undefined)).toThrowWithMessage(Error, + 'Incorrect iterable input') + expect(() => filterRamda(T, null)).toThrowWithMessage(TypeError, + 'Cannot read properties of null (reading \'fantasy-land/filter\')') + expect(() => filterRamda(T, undefined)).toThrowWithMessage(TypeError, + 'Cannot read properties of undefined (reading \'fantasy-land/filter\')') +}) ```
@@ -15199,95 +4797,99 @@ mapObjIndexed(fn: ObjectIterator): (iterable: Dictionary) => Dict TypeScript test ```typescript -import {mapObjIndexed} from 'rambda' +import {filter} from 'rambda' -const obj = {a: 1, b: 2, c: 3} +const list = [1, 2, 3] +const obj = {a: 1, b: 2} -describe('R.mapObjIndexed', () => { - it('without type transform', () => { - const result = mapObjIndexed((x, prop, obj) => { +describe('R.filter with array', () => { + it('happy', () => { + const result = filter(x => { x // $ExpectType number - prop // $ExpectType string - obj // $ExpectType Dictionary - return x + 2 - }, obj) - result // $ExpectType Dictionary + return x > 1 + }, list) + result // $ExpectType number[] }) - it('without type transform - curried', () => { - const result = mapObjIndexed((x, prop, obj) => { + it('curried', () => { + const result = filter(x => { x // $ExpectType number - prop // $ExpectType string - obj // $ExpectType Dictionary - return x + 2 - })(obj) - result // $ExpectType Dictionary + return x > 1 + })(list) + result // $ExpectType number[] }) - it('change of type', () => { - const result = mapObjIndexed((x, prop, obj) => { - x // $ExpectType number +}) + +describe('R.filter with objects', () => { + it('happy', () => { + const result = filter((val, prop, origin) => { + val // $ExpectType number prop // $ExpectType string - obj // $ExpectType Dictionary - return String(x + 2) + origin // $ExpectType Dictionary + + return val > 1 }, obj) - result // $ExpectType Dictionary + result // $ExpectType Dictionary }) - it('change of type - curried', () => { - const result = mapObjIndexed((x, prop, obj) => { - x // $ExpectType number + it('curried version requires second dummy type', () => { + const result = filter((val, prop, origin) => { + val // $ExpectType number prop // $ExpectType string - obj // $ExpectType Dictionary - return String(x + 2) + origin // $ExpectType Dictionary + + return val > 1 })(obj) - result // $ExpectType Dictionary + result // $ExpectType Dictionary }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mapObjIndexed) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#filter) -### match +### find ```typescript -match(regExpression: RegExp, str: string): string[] +find(predicate: (x: T) => boolean, list: T[]): T | undefined ``` -Curried version of `String.prototype.match` which returns empty array, when there is no match. +It returns the first element of `list` that satisfy the `predicate`. -```javascript -const result = [ - R.match('a', 'foo'), - R.match(/([a-z]a)/g, 'bananas') -] -// => [[], ['ba', 'na', 'na']] -``` +If there is no such element, it returns `undefined`. -Try this R.match example in Rambda REPL +Try this R.find example in Rambda REPL
All TypeScript definitions ```typescript -match(regExpression: RegExp, str: string): string[]; -match(regExpression: RegExp): (str: string) => string[]; +find(predicate: (x: T) => boolean, list: T[]): T | undefined; +find(predicate: (x: T) => boolean): (list: T[]) => T | undefined; ```
-R.match source +R.find source ```javascript -export function match(pattern, input){ - if (arguments.length === 1) return _input => match(pattern, _input) +export function find(predicate, list){ + if (arguments.length === 1) return _list => find(predicate, _list) - const willReturn = input.match(pattern) + let index = 0 + const len = list.length - return willReturn === null ? [] : willReturn + while (index < len){ + const x = list[ index ] + if (predicate(x)){ + return x + } + + index++ + } } ``` @@ -15298,26 +4900,23 @@ export function match(pattern, input){ Tests ```javascript -import { equals } from './equals.js' -import { match } from './match.js' +import { find } from './find.js' +import { propEq } from './propEq.js' -test('happy', () => { - expect(match(/a./g)('foo bar baz')).toEqual([ 'ar', 'az' ]) -}) +const list = [ { a : 1 }, { a : 2 }, { a : 3 } ] -test('fallback', () => { - expect(match(/a./g)('foo')).toEqual([]) +test('happy', () => { + const fn = propEq(2, 'a') + expect(find(fn, list)).toEqual({ a : 2 }) }) -test('with string', () => { - expect(match('a', 'foo')).toEqual([]) - expect(equals(match('o', 'foo'), [ 'o' ])).toBeTrue() +test('with curry', () => { + const fn = propEq(4, 'a') + expect(find(fn)(list)).toBeUndefined() }) -test('throwing', () => { - expect(() => { - match(/a./g, null) - }).toThrowErrorMatchingInlineSnapshot('"Cannot read properties of null (reading \'match\')"') +test('with empty list', () => { + expect(find(() => true, [])).toBeUndefined() }) ``` @@ -15328,72 +4927,70 @@ test('throwing', () => { TypeScript test ```typescript -import {match} from 'rambda' +import {find} from 'rambda' -const str = 'foo bar' +const list = [1, 2, 3] -describe('R.match', () => { +describe('R.find', () => { it('happy', () => { - const result = match(/foo/, str) - result // $ExpectType string[] + const predicate = (x: number) => x > 2 + const result = find(predicate, list) + result // $ExpectType number | undefined }) it('curried', () => { - const result = match(/foo/)(str) - result // $ExpectType string[] + const predicate = (x: number) => x > 2 + const result = find(predicate)(list) + result // $ExpectType number | undefined }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#match) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#find) -### mathMod +### findIndex ```typescript -mathMod(x: number, y: number): number +findIndex(predicate: (x: T) => boolean, list: T[]): number ``` -`R.mathMod` behaves like the modulo operator should mathematically, unlike the `%` operator (and by extension, `R.modulo`). So while `-17 % 5` is `-2`, `mathMod(-17, 5)` is `3`. - -> :boom: Explanation is taken from `Ramda` documentation site. +It returns the index of the first element of `list` satisfying the `predicate` function. -```javascript -const result = [ - R.mathMod(-17, 5), - R.mathMod(17, 5), - R.mathMod(17, -5), - R.mathMod(17, 0) -] -// => [3, 2, NaN, NaN] -``` +If there is no such element, then `-1` is returned. -Try this R.mathMod example in Rambda REPL +Try this R.findIndex example in Rambda REPL
All TypeScript definitions ```typescript -mathMod(x: number, y: number): number; -mathMod(x: number): (y: number) => number; +findIndex(predicate: (x: T) => boolean, list: T[]): number; +findIndex(predicate: (x: T) => boolean): (list: T[]) => number; ```
-R.mathMod source +R.findIndex source ```javascript -import { isInteger } from './_internals/isInteger.js' +export function findIndex(predicate, list){ + if (arguments.length === 1) return _list => findIndex(predicate, _list) + + const len = list.length + let index = -1 -export function mathMod(x, y){ - if (arguments.length === 1) return _y => mathMod(x, _y) - if (!isInteger(x) || !isInteger(y) || y < 1) return NaN + while (++index < len){ + if (predicate(list[ index ])){ + return index + } + } - return (x % y + y) % y + return -1 } ``` @@ -15404,17 +5001,15 @@ export function mathMod(x, y){ Tests ```javascript -import { mathMod } from './mathMod.js' +import { findIndex } from './findIndex.js' +import { propEq } from './propEq.js' + +const list = [ { a : 1 }, { a : 2 }, { a : 3 } ] test('happy', () => { - expect(mathMod(-17)(5)).toBe(3) - expect(mathMod(17, 5)).toBe(2) - expect(mathMod(17, -5)).toBeNaN() - expect(mathMod(17, 0)).toBeNaN() - expect(mathMod('17', 5)).toBeNaN() - expect(mathMod({}, 2)).toBeNaN() - expect(mathMod([], 2)).toBeNaN() - expect(mathMod(Symbol(), 2)).toBeNaN() + expect(findIndex(propEq(2, 'a'), list)).toBe(1) + expect(findIndex(propEq(1, 'a'))(list)).toBe(0) + expect(findIndex(propEq(4, 'a'))(list)).toBe(-1) }) ``` @@ -15425,18 +5020,19 @@ test('happy', () => { TypeScript test ```typescript -import {mathMod} from 'rambda' +import {findIndex} from 'rambda' -const first = 1 -const second = 2 +const list = [1, 2, 3] -describe('R.mathMod', () => { +describe('R.findIndex', () => { it('happy', () => { - const result = mathMod(first, second) + const predicate = (x: number) => x > 2 + const result = findIndex(predicate, list) result // $ExpectType number }) it('curried', () => { - const result = mathMod(first, second) + const predicate = (x: number) => x > 2 + const result = findIndex(predicate)(list) result // $ExpectType number }) }) @@ -15444,47 +5040,49 @@ describe('R.mathMod', () => {
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mathMod) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#findIndex) -### max +### findLast ```typescript -max(x: T, y: T): T +findLast(fn: (x: T) => boolean, list: T[]): T | undefined ``` -It returns the greater value between `x` and `y`. +It returns the last element of `list` satisfying the `predicate` function. -```javascript -const result = [ - R.max(5, 7), - R.max('bar', 'foo'), -] -// => [7, 'foo'] -``` +If there is no such element, then `undefined` is returned. -Try this R.max example in Rambda REPL +Try this R.findLast example in Rambda REPL
All TypeScript definitions ```typescript -max(x: T, y: T): T; -max(x: T): (y: T) => T; +findLast(fn: (x: T) => boolean, list: T[]): T | undefined; +findLast(fn: (x: T) => boolean): (list: T[]) => T | undefined; ```
-R.max source +R.findLast source ```javascript -export function max(x, y){ - if (arguments.length === 1) return _y => max(x, _y) +export function findLast(predicate, list){ + if (arguments.length === 1) return _list => findLast(predicate, _list) + + let index = list.length + + while (--index >= 0){ + if (predicate(list[ index ])){ + return list[ index ] + } + } - return y > x ? y : x + return undefined } ``` @@ -15492,18 +5090,55 @@ export function max(x, y){
-Tests +Tests + +```javascript +import { findLast } from './findLast.js' + +test('happy', () => { + const result = findLast(x => x > 1, [ 1, 1, 1, 2, 3, 4, 1 ]) + expect(result).toBe(4) + + expect(findLast(x => x === 0, [ 0, 1, 1, 2, 3, 4, 1 ])).toBe(0) +}) + +test('with curry', () => { + expect(findLast(x => x > 1)([ 1, 1, 1, 2, 3, 4, 1 ])).toBe(4) +}) + +const obj1 = { x : 100 } +const obj2 = { x : 200 } +const a = [ 11, 10, 9, 'cow', obj1, 8, 7, 100, 200, 300, obj2, 4, 3, 2, 1, 0 ] +const even = function (x){ + return x % 2 === 0 +} +const gt100 = function (x){ + return x > 100 +} +const isStr = function (x){ + return typeof x === 'string' +} +const xGt100 = function (o){ + return o && o.x > 100 +} + +test('ramda 1', () => { + expect(findLast(even, a)).toBe(0) + expect(findLast(gt100, a)).toBe(300) + expect(findLast(isStr, a)).toBe('cow') + expect(findLast(xGt100, a)).toEqual(obj2) +}) -```javascript -import { max } from './max.js' +test('ramda 2', () => { + expect(findLast(even, [ 'zing' ])).toBeUndefined() +}) -test('with number', () => { - expect(max(2, 1)).toBe(2) +test('ramda 3', () => { + expect(findLast(even, [ 2, 3, 5 ])).toBe(2) }) -test('with string', () => { - expect(max('foo')('bar')).toBe('foo') - expect(max('bar')('baz')).toBe('baz') +test('ramda 4', () => { + expect(findLast(even, [])).toBeUndefined() }) ``` @@ -15514,78 +5149,70 @@ test('with string', () => { TypeScript test ```typescript -import {max} from 'rambda' +import {findLast} from 'rambda' -const first = 1 -const second = 2 +const list = [1, 2, 3] -describe('R.max', () => { +describe('R.findLast', () => { it('happy', () => { - const result = max(first, second) - result // $ExpectType 1 | 2 + const predicate = (x: number) => x > 2 + const result = findLast(predicate, list) + result // $ExpectType number | undefined }) it('curried', () => { - const result = max(first, second) - result // $ExpectType 1 | 2 - }) - it('curried - cann pass type', () => { - const result = max(first, second) - result // $ExpectType number - }) - it('can pass type', () => { - const result = max(first, second) - result // $ExpectType number + const predicate = (x: number) => x > 2 + const result = findLast(predicate)(list) + result // $ExpectType number | undefined }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#max) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#findLast) -### maxBy +### findLastIndex ```typescript -maxBy(compareFn: (input: T) => Ord, x: T, y: T): T +findLastIndex(predicate: (x: T) => boolean, list: T[]): number ``` -It returns the greater value between `x` and `y` according to `compareFn` function. - -```javascript -const compareFn = Math.abs +It returns the index of the last element of `list` satisfying the `predicate` function. -R.maxBy(compareFn, 5, -7) // => -7 -``` +If there is no such element, then `-1` is returned. -Try this R.maxBy example in Rambda REPL +Try this R.findLastIndex example in Rambda REPL
All TypeScript definitions ```typescript -maxBy(compareFn: (input: T) => Ord, x: T, y: T): T; -maxBy(compareFn: (input: T) => Ord, x: T): (y: T) => T; -maxBy(compareFn: (input: T) => Ord): (x: T) => (y: T) => T; +findLastIndex(predicate: (x: T) => boolean, list: T[]): number; +findLastIndex(predicate: (x: T) => boolean): (list: T[]) => number; ```
-R.maxBy source +R.findLastIndex source ```javascript -import { curry } from './curry.js' +export function findLastIndex(fn, list){ + if (arguments.length === 1) return _list => findLastIndex(fn, _list) -export function maxByFn( - compareFn, x, y -){ - return compareFn(y) > compareFn(x) ? y : x -} + let index = list.length + + while (--index >= 0){ + if (fn(list[ index ])){ + return index + } + } -export const maxBy = curry(maxByFn) + return -1 +} ```
@@ -15595,17 +5222,53 @@ export const maxBy = curry(maxByFn) Tests ```javascript -import { maxBy } from './maxBy.js' +import { findLastIndex } from './findLastIndex.js' test('happy', () => { - expect(maxBy( - Math.abs, -5, 2 - )).toBe(-5) + const result = findLastIndex(x => x > 1, [ 1, 1, 1, 2, 3, 4, 1 ]) + + expect(result).toBe(5) + + expect(findLastIndex(x => x === 0, [ 0, 1, 1, 2, 3, 4, 1 ])).toBe(0) }) -test('curried', () => { - expect(maxBy(Math.abs)(2, -5)).toBe(-5) - expect(maxBy(Math.abs)(2)(-5)).toBe(-5) +test('with curry', () => { + expect(findLastIndex(x => x > 1)([ 1, 1, 1, 2, 3, 4, 1 ])).toBe(5) +}) + +const obj1 = { x : 100 } +const obj2 = { x : 200 } +const a = [ 11, 10, 9, 'cow', obj1, 8, 7, 100, 200, 300, obj2, 4, 3, 2, 1, 0 ] +const even = function (x){ + return x % 2 === 0 +} +const gt100 = function (x){ + return x > 100 +} +const isStr = function (x){ + return typeof x === 'string' +} +const xGt100 = function (o){ + return o && o.x > 100 +} + +test('ramda 1', () => { + expect(findLastIndex(even, a)).toBe(15) + expect(findLastIndex(gt100, a)).toBe(9) + expect(findLastIndex(isStr, a)).toBe(3) + expect(findLastIndex(xGt100, a)).toBe(10) +}) + +test('ramda 2', () => { + expect(findLastIndex(even, [ 'zing' ])).toBe(-1) +}) + +test('ramda 3', () => { + expect(findLastIndex(even, [ 2, 3, 5 ])).toBe(0) +}) + +test('ramda 4', () => { + expect(findLastIndex(even, [])).toBe(-1) }) ``` @@ -15616,59 +5279,68 @@ test('curried', () => { TypeScript test ```typescript -import {maxBy} from 'rambda' +import {findLastIndex} from 'rambda' -const compareFn = (x: number) => x % 2 === 0 ? 1 : -1 -const first = 1 -const second = 2 +const list = [1, 2, 3] -describe('R.maxBy', () => { +describe('R.findLastIndex', () => { it('happy', () => { - const result = maxBy(compareFn, first, second) - result // $ExpectType 1 | 2 + const predicate = (x: number) => x > 2 + const result = findLastIndex(predicate, list) + result // $ExpectType number + }) + it('curried', () => { + const predicate = (x: number) => x > 2 + const result = findLastIndex(predicate)(list) + result // $ExpectType number }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#maxBy) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#findLastIndex) -### mean +### flatten ```typescript -mean(list: number[]): number +flatten(list: any[]): T[] ``` -It returns the mean value of `list` input. - -```javascript -R.mean([ 2, 7 ]) -// => 4.5 -``` +It deeply flattens an array. -Try this R.mean example in Rambda REPL +Try this R.flatten example in Rambda REPL
All TypeScript definitions ```typescript -mean(list: number[]): number; +flatten(list: any[]): T[]; ```
-R.mean source +R.flatten source ```javascript -import { sum } from './sum.js' +import { isArray } from './_internals/isArray.js' -export function mean(list){ - return sum(list) / list.length +export function flatten(list, input){ + const willReturn = input === undefined ? [] : input + + for (let i = 0; i < list.length; i++){ + if (isArray(list[ i ])){ + flatten(list[ i ], willReturn) + } else { + willReturn.push(list[ i ]) + } + } + + return willReturn } ``` @@ -15679,14 +5351,21 @@ export function mean(list){ Tests ```javascript -import { mean } from './mean.js' +import { flatten } from './flatten.js' test('happy', () => { - expect(mean([ 2, 7 ])).toBe(4.5) + expect(flatten([ 1, 2, 3, [ [ [ [ [ 4 ] ] ] ] ] ])).toEqual([ 1, 2, 3, 4 ]) + + expect(flatten([ 1, [ 2, [ [ 3 ] ] ], [ 4 ] ])).toEqual([ 1, 2, 3, 4 ]) + + expect(flatten([ 1, [ 2, [ [ [ 3 ] ] ] ], [ 4 ] ])).toEqual([ 1, 2, 3, 4 ]) + + expect(flatten([ 1, 2, [ 3, 4 ], 5, [ 6, [ 7, 8, [ 9, [ 10, 11 ], 12 ] ] ] ])).toEqual([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]) }) -test('with NaN', () => { - expect(mean([])).toBeNaN() +test('readme example', () => { + const result = flatten([ 1, 2, [ 3, 30, [ 300 ] ], [ 4 ] ]) + expect(result).toEqual([ 1, 2, 3, 30, 300, 4 ]) }) ``` @@ -15697,86 +5376,143 @@ test('with NaN', () => { TypeScript test ```typescript -import {mean} from 'rambda' +import {flatten} from 'rambda' -describe('R.mean', () => { +describe('flatten', () => { it('happy', () => { - const result = mean([1, 2, 3]) - - result // $ExpectType number + const result = flatten([1, 2, [3, [4]]]) + result // $ExpectType number[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mean) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#flatten) -### median +### flip -```typescript +It returns function which calls `fn` with exchanged first and second argument. -median(list: number[]): number -``` +Try this R.flip example in Rambda REPL -It returns the median value of `list` input. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#flip) -```javascript -R.median([ 7, 2, 10, 9 ]) // => 8 +### forEach + +```typescript + +forEach(fn: Iterator, list: T[]): T[] ``` -Try this R.median example in Rambda REPL +It applies `iterable` function over all members of `list` and returns `list`. + +Try this R.forEach example in Rambda REPL
All TypeScript definitions ```typescript -median(list: number[]): number; +forEach(fn: Iterator, list: T[]): T[]; +forEach(fn: Iterator): (list: T[]) => T[]; +forEach(fn: ObjectIterator, list: Dictionary): Dictionary; +forEach(fn: ObjectIterator): (list: Dictionary) => Dictionary; ```
-R.median source +R.forEach source ```javascript -import { mean } from './mean.js' +import { isArray } from './_internals/isArray.js' +import { forEachObjIndexedFn } from './forEachObjIndexed.js' -export function median(list){ - const len = list.length - if (len === 0) return NaN - const width = 2 - len % 2 - const idx = (len - width) / 2 +export function forEach(fn, iterable){ + if (arguments.length === 1) return _list => forEach(fn, _list) + if (iterable === undefined) return - return mean(Array.prototype.slice - .call(list, 0) - .sort((a, b) => { - if (a === b) return 0 + if (isArray(iterable)){ + let index = 0 + const len = iterable.length + + while (index < len){ + fn(iterable[ index ]) + index++ + } + } else return forEachObjIndexedFn(fn, iterable) + + return iterable +} +``` + +
+ +
+ +Tests + +```javascript +import { forEach } from './forEach.js' +import { type } from './type.js' + +test('happy', () => { + const sideEffect = {} + forEach(x => sideEffect[ `foo${ x }` ] = x + 10)([ 1, 2 ]) + + expect(sideEffect).toEqual({ + foo1 : 11, + foo2 : 12, + }) +}) + +test('iterate over object', () => { + const obj = { + a : 1, + b : [ 1, 2 ], + c : { d : 7 }, + f : 'foo', + } + const result = {} + const returned = forEach(( + val, prop, inputObj + ) => { + expect(type(inputObj)).toBe('Object') + result[ prop ] = `${ prop }-${ type(val) }` + })(obj) - return a < b ? -1 : 1 - }) - .slice(idx, idx + width)) -} -``` + const expected = { + a : 'a-Number', + b : 'b-Array', + c : 'c-Object', + f : 'f-String', + } -
+ expect(result).toEqual(expected) + expect(returned).toEqual(obj) +}) -
+test('with empty list', () => { + const list = [] + const result = forEach(x => x * x)(list) -Tests + expect(result).toEqual(list) +}) -```javascript -import { median } from './median.js' +test('with wrong input', () => { + const list = undefined + const result = forEach(x => x * x)(list) -test('happy', () => { - expect(median([ 2 ])).toBe(2) - expect(median([ 7, 2, 10, 2, 9 ])).toBe(7) + expect(result).toBeUndefined() }) -test('with empty array', () => { - expect(median([])).toBeNaN() +test('returns the input', () => { + const list = [ 1, 2, 3 ] + const result = forEach(x => x * x)(list) + + expect(result).toEqual(list) }) ``` @@ -15787,106 +5523,131 @@ test('with empty array', () => { TypeScript test ```typescript -import {median} from 'rambda' +import {forEach} from 'rambda' -describe('R.median', () => { +const list = [1, 2, 3] +const obj = {a: 1, b: 2} + +describe('R.forEach with arrays', () => { it('happy', () => { - const result = median([1, 2, 3]) + const result = forEach(a => { + a // $ExpectType number + }, list) + result // $ExpectType number[] + }) + it('curried require an explicit typing', () => { + const result = forEach(a => { + a // $ExpectType number + })(list) + result // $ExpectType number[] + }) +}) - result // $ExpectType number +describe('R.forEach with objects', () => { + it('happy', () => { + const result = forEach((a, b, c) => { + a // $ExpectType number + b // $ExpectType string + c // $ExpectType Dictionary + return `${a}` + }, obj) + result // $ExpectType Dictionary + }) + it('curried require an input typing and a dummy third typing', () => { + // Required in order all typings to work + const result = forEach((a, b, c) => { + a // $ExpectType number + b // $ExpectType string + c // $ExpectType Dictionary + })(obj) + result // $ExpectType Dictionary + }) + it('iterator without property', () => { + const result = forEach(a => { + a // $ExpectType number + }, obj) + result // $ExpectType Dictionary }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#median) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#forEach) -### merge +### forEachObjIndexed -```typescript +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#forEachObjIndexed) -merge(target: A, newProps: B): A & B -export function merge(target: any): (newProps: any) => Output -``` +### fromPairs -Same as `R.mergeRight`. +It transforms a `listOfPairs` to an object. -
+Try this R.fromPairs example in Rambda REPL -All TypeScript definitions +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#fromPairs) -```typescript -merge(target: A, newProps: B): A & B -merge(target: any): (newProps: any) => Output; -``` +### groupBy -
+It splits `list` according to a provided `groupFn` function and returns an object. -
+Try this R.groupBy example in Rambda REPL -R.merge source +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#groupBy) -```javascript -export { mergeRight as merge } from './mergeRight.js' -``` +### groupWith -
+It returns separated version of list or string `input`, where separation is done with equality `compareFn` function. -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#merge) +Try this R.groupWith example in Rambda REPL -### mergeAll +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#groupWith) -```typescript +### gt -mergeAll(list: object[]): T -``` +Try this R.gt example in Rambda REPL -It merges all objects of `list` array sequentially and returns the result. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#gt) -```javascript -const list = [ - {a: 1}, - {b: 2}, - {c: 3} -] -const result = R.mergeAll(list) -const expected = { - a: 1, - b: 2, - c: 3 -} -// => `result` is equal to `expected` +### gte + +Try this R.gte example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#gte) + +### has + +```typescript + +has(prop: string, obj: T): boolean ``` -Try this R.mergeAll example in Rambda REPL +It returns `true` if `obj` has property `prop`. + +Try this R.has example in Rambda REPL
All TypeScript definitions ```typescript -mergeAll(list: object[]): T; -mergeAll(list: object[]): object; +has(prop: string, obj: T): boolean; +has(prop: string): (obj: T) => boolean; ```
-R.mergeAll source +R.has source ```javascript -import { map } from './map.js' -import { mergeRight } from './mergeRight.js' +export function has(prop, obj){ + if (arguments.length === 1) return _obj => has(prop, _obj) -export function mergeAll(arr){ - let willReturn = {} - map(val => { - willReturn = mergeRight(willReturn, val) - }, arr) + if (!obj) return false - return willReturn + return obj.hasOwnProperty(prop) } ``` @@ -15897,47 +5658,19 @@ export function mergeAll(arr){ Tests ```javascript -import { mergeAll } from './mergeAll.js' - -test('case 1', () => { - const arr = [ { a : 1 }, { b : 2 }, { c : 3 } ] - const expectedResult = { - a : 1, - b : 2, - c : 3, - } - expect(mergeAll(arr)).toEqual(expectedResult) -}) +import { has } from './has.js' -test('case 2', () => { - expect(mergeAll([ { foo : 1 }, { bar : 2 }, { baz : 3 } ])).toEqual({ - foo : 1, - bar : 2, - baz : 3, - }) +test('happy', () => { + expect(has('a')({ a : 1 })).toBeTrue() + expect(has('b', { a : 1 })).toBeFalse() }) -describe('acts as if nil values are simply empty objects', () => { - it('if the first object is nil', () => { - expect(mergeAll([ null, { foo : 1 }, { foo : 2 }, { bar : 2 } ])).toEqual({ - foo : 2, - bar : 2, - }) - }) - - it('if the last object is nil', () => { - expect(mergeAll([ { foo : 1 }, { foo : 2 }, { bar : 2 }, undefined ])).toEqual({ - foo : 2, - bar : 2, - }) - }) - - it('if an intermediate object is nil', () => { - expect(mergeAll([ { foo : 1 }, { foo : 2 }, null, { bar : 2 } ])).toEqual({ - foo : 2, - bar : 2, - }) - }) +test('with non-object', () => { + expect(has('a', undefined)).toBeFalse() + expect(has('a', null)).toBeFalse() + expect(has('a', true)).toBeFalse() + expect(has('a', '')).toBeFalse() + expect(has('a', /a/)).toBeFalse() }) ``` @@ -15948,67 +5681,71 @@ describe('acts as if nil values are simply empty objects', () => { TypeScript test ```typescript -import {mergeAll} from 'rambda' - -describe('R.mergeAll', () => { - it('with passing type', () => { - interface Output { - foo: number, - bar: number, - } - const result = mergeAll([{foo: 1}, {bar: 2}]) - result.foo // $ExpectType number - result.bar // $ExpectType number - }) +import {has} from 'rambda' - it('without passing type', () => { - const result = mergeAll([{foo: 1}, {bar: 2}]) - result // $ExpectType unknown +describe('R.has', () => { + it('happy', () => { + const result = has('foo', {a: 1}) + const curriedResult = has('bar')({a: 1}) + result // $ExpectType boolean + curriedResult // $ExpectType boolean }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeAll) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#has) -### mergeDeepLeft +### hasIn + +Try this R.hasIn example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#hasIn) + +### hasPath ```typescript -mergeDeepLeft(newProps: object, target: object): Output +hasPath( + path: string | string[], + input: object +): boolean ``` -```javascript -const result = R.mergeDeepLeft( - {a: {b: 1}}, - {a: {b: 2, c: 3}} -) -// => {a: {b: 1, c: 3}} -``` +It will return true, if `input` object has truthy `path`(calculated with `R.path`). -Try this R.mergeDeepLeft example in Rambda REPL +Try this R.hasPath example in Rambda REPL
All TypeScript definitions ```typescript -mergeDeepLeft(newProps: object, target: object): Output; -mergeDeepLeft(newProps: object): (target: object) => Output; +hasPath( + path: string | string[], + input: object +): boolean; +hasPath( + path: string | string[] +): (input: object) => boolean; ```
-R.mergeDeepLeft source +R.hasPath source ```javascript -import { mergeDeepRight } from './mergeDeepRight.js'; +import { path } from './path.js' + +export function hasPath(pathInput, obj){ + if (arguments.length === 1){ + return objHolder => hasPath(pathInput, objHolder) + } -export function mergeDeepLeft(newProps, target) { - return mergeDeepRight(target, newProps); + return path(pathInput, obj) !== undefined } ``` @@ -16019,99 +5756,95 @@ export function mergeDeepLeft(newProps, target) { Tests ```javascript -import { mergeDeepLeft } from './mergeDeepLeft'; +import { hasPath } from './hasPath.js' -it('takes two objects, recursively merges their own properties and returns a new object', () => { - const a = { w: 1, x: 2, y: { z: 3 } }; - const b = { a: 4, b: 5, c: { d: 6 } }; - expect(mergeDeepLeft(a, b)).toEqual({ - w: 1, - x: 2, - y: { z: 3 }, - a: 4, - b: 5, - c: { d: 6 }, - }); -}); +test('when true', () => { + const path = 'a.b' + const obj = { a : { b : [] } } -it('overrides properties in the second object with properties in the first object', () => { - const a = { a: { b: 1, c: 2 }, y: 0 }; - const b = { a: { b: 3, d: 4 }, z: 0 }; - expect(mergeDeepLeft(a, b)).toEqual({ a: { b: 1, c: 2, d: 4 }, y: 0, z: 0 }); -}); + const result = hasPath(path)(obj) + const expectedResult = true -it('is not destructive', () => { - const a = { w: 1, x: { y: 2 } }; - const res = mergeDeepLeft(a, { x: { y: 3 } }); - expect(a).not.toBe(res); - expect(a.x).not.toBe(res.x); - expect(res).toEqual({ w: 1, x: { y: 2 } }); -}); + expect(result).toEqual(expectedResult) +}) -it('reports only own properties', () => { - const a = { w: 1, x: { y: 2 } }; - function Cla() {} - Cla.prototype.y = 5; - expect(mergeDeepLeft({ x: new Cla() }, a)).toEqual({ w: 1, x: { y: 2 } }); - expect(mergeDeepLeft(a, { x: new Cla() })).toEqual({ w: 1, x: { y: 2 } }); -}); +test('when false', () => { + const path = 'a.b' + const obj = {} + + const result = hasPath(path, obj) + const expectedResult = false + + expect(result).toEqual(expectedResult) +}) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeDeepLeft) +
-### mergeDeepRight +TypeScript test ```typescript +import {hasPath} from 'rambda' -mergeDeepRight(target: object, newProps: object): Output +describe('R.hasPath', () => { + it('string path', () => { + const obj = {a: {b: 1}} + const result = hasPath('a.b', obj) + const curriedResult = hasPath('a.c')(obj) + result // $ExpectType boolean + curriedResult // $ExpectType boolean + }) + it('array path', () => { + const obj = {a: {b: 1}} + const result = hasPath(['a', 'b'], obj) + const curriedResult = hasPath(['a', 'c'])(obj) + result // $ExpectType boolean + curriedResult // $ExpectType boolean + }) +}) ``` -Creates a new object with the own properties of the first object merged with the own properties of the second object. If a key exists in both objects: +
- - and both values are objects, the two values will be recursively merged - - otherwise the value from the second object will be used. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#hasPath) + +### head + +```typescript + +head(str: string): string +``` + +It returns the first element of list or string `input`. It returns `undefined` if array has length of 0. + +Try this R.head example in Rambda REPL
All TypeScript definitions ```typescript -mergeDeepRight(target: object, newProps: object): Output; -mergeDeepRight(target: object): (newProps: object) => Output; +head(str: string): string; +head(str: ''): undefined; +head(list: readonly[]): undefined; +head(list: never[]): undefined; +head(array: T): FirstArrayElement +head(array: T): FirstArrayElement ```
-R.mergeDeepRight source +R.head source ```javascript -import { clone } from './clone.js' -import { type } from './type.js' - -export function mergeDeepRight(target, source){ - if (arguments.length === 1){ - return sourceHolder => mergeDeepRight(target, sourceHolder) - } - - const willReturn = clone(target) - - Object.keys(source).forEach(key => { - if (type(source[ key ]) === 'Object'){ - if (type(target[ key ]) === 'Object'){ - willReturn[ key ] = mergeDeepRight(target[ key ], source[ key ]) - } else { - willReturn[ key ] = source[ key ] - } - } else { - willReturn[ key ] = source[ key ] - } - }) +export function head(listOrString){ + if (typeof listOrString === 'string') return listOrString[ 0 ] || '' - return willReturn + return listOrString[ 0 ] } ``` @@ -16122,201 +5855,109 @@ export function mergeDeepRight(target, source){ Tests ```javascript -import { mergeDeepRight } from './mergeDeepRight.js' - -const student = { - name : 'foo', - age : 10, - contact : { - a : 1, - email : 'foo@example.com', - }, -} -const teacher = { - age : 40, - contact : { email : 'baz@example.com' }, - songs : { title : 'Remains the same' }, -} - -test('when merging object with lists inside them', () => { - const a = { - a : [ 1, 2, 3 ], - b : [ 4, 5, 6 ], - } - const b = { - a : [ 7, 8, 9 ], - b : [ 10, 11, 12 ], - } - const result = mergeDeepRight(a, b) - const expected = { - a : [ 7, 8, 9 ], - b : [ 10, 11, 12 ], - } - expect(result).toEqual(expected) -}) - -test('happy', () => { - const result = mergeDeepRight(student, teacher) - const curryResult = mergeDeepRight(student)(teacher) - const expected = { - age : 40, - name : 'foo', - contact : { - a : 1, - email : 'baz@example.com', - }, - songs : { title : 'Remains the same' }, - } +import { head } from './head.js' - expect(result).toEqual(expected) - expect(curryResult).toEqual(expected) +test('head', () => { + expect(head([ 'fi', 'fo', 'fum' ])).toBe('fi') + expect(head([])).toBeUndefined() + expect(head('foo')).toBe('f') + expect(head('')).toBe('') }) +``` -test('issue 650', () => { - expect(Object.keys(mergeDeepRight({ a : () => {} }, { b : () => {} }))).toEqual([ - 'a', - 'b', - ]) -}) +
-test('ramda compatible test 1', () => { - const a = { - w : 1, - x : 2, - y : { z : 3 }, - } - const b = { - a : 4, - b : 5, - c : { d : 6 }, - } - const result = mergeDeepRight(a, b) - const expected = { - w : 1, - x : 2, - y : { z : 3 }, - a : 4, - b : 5, - c : { d : 6 }, - } +
- expect(result).toEqual(expected) -}) +TypeScript test -test('ramda compatible test 2', () => { - const a = { - a : { - b : 1, - c : 2, - }, - y : 0, - } - const b = { - a : { - b : 3, - d : 4, - }, - z : 0, - } - const result = mergeDeepRight(a, b) - const expected = { - a : { - b : 3, - c : 2, - d : 4, - }, - y : 0, - z : 0, - } +```typescript +import { + emptyList, + emptyString, + mixedList, + mixedListConst, + numberList, + numberListConst, + string, +} from '_internals/typescriptTestUtils' +import {head, last} from 'rambda' - expect(result).toEqual(expected) -}) +describe('R.head', () => { + it('string', () => { + head(string) // $ExpectType string + last(string) // $ExpectType string + }) + it('empty string', () => { + head(emptyString) // $ExpectType undefined + last(emptyString) // $ExpectType undefined + }) + it('array', () => { + head(numberList) // $ExpectType number + head(numberListConst) // $ExpectType 1 -test('ramda compatible test 3', () => { - const a = { - w : 1, - x : { y : 2 }, - } - const result = mergeDeepRight(a, { x : { y : 3 } }) - const expected = { - w : 1, - x : { y : 3 }, - } - expect(result).toEqual(expected) -}) + last(numberList) // $ExpectType number + last(numberListConst) // $ExpectType 3 + }) + it('empty array', () => { + const list = [] as const + head(emptyList) // $ExpectType undefined + head(list) // $ExpectType undefined + last(emptyList) // $ExpectType undefined + last(list) // $ExpectType undefined + }) -test('functions are not discarded', () => { - const obj = { foo : () => {} } - expect(typeof mergeDeepRight(obj, {}).foo).toBe('function') + it('mixed', () => { + head(mixedList) // $ExpectType string | number + head(mixedListConst) // $ExpectType 1 + last(mixedList) // $ExpectType string | number + last(mixedListConst) // $ExpectType "bar" + }) }) ```
-
- -TypeScript test +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#head) -```typescript -import {mergeDeepRight} from 'rambda' +### identical -interface Output { - foo: { - bar: number, - }, -} +It returns `true` if its arguments `a` and `b` are identical. -describe('R.mergeDeepRight', () => { - const result = mergeDeepRight({foo: {bar: 1}}, {foo: {bar: 2}}) - result.foo.bar // $ExpectType number -}) -``` +Otherwise, it returns `false`. -
+Try this R.identical example in Rambda REPL -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeDeepRight) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#identical) -### mergeLeft +### identity ```typescript -mergeLeft(newProps: object, target: object): Output +identity(input: T): T ``` -Same as `R.merge`, but in opposite direction. - -```javascript -const result = R.mergeLeft( - {a: 10}, - {a: 1, b: 2} -) -// => {a:10, b: 2} -``` +It just passes back the supplied `input` argument. -Try this R.mergeLeft example in Rambda REPL +Try this R.identity example in Rambda REPL
All TypeScript definitions ```typescript -mergeLeft(newProps: object, target: object): Output; -mergeLeft(newProps: object): (target: object) => Output; +identity(input: T): T; ```
-R.mergeLeft source +R.identity source ```javascript -import { mergeRight } from './mergeRight.js' - -export function mergeLeft(x, y){ - if (arguments.length === 1) return _y => mergeLeft(x, _y) - - return mergeRight(y, x) +export function identity(x){ + return x } ``` @@ -16327,33 +5968,12 @@ export function mergeLeft(x, y){ Tests ```javascript -import { mergeLeft } from './mergeLeft.js' - -const obj = { - foo : 1, - bar : 2, -} +import { identity } from './identity.js' test('happy', () => { - expect(mergeLeft({ bar : 20 }, obj)).toEqual({ - foo : 1, - bar : 20, - }) -}) - -test('curry', () => { - expect(mergeLeft({ baz : 3 })(obj)).toEqual({ - foo : 1, - bar : 2, - baz : 3, - }) -}) - -test('when undefined or null instead of object', () => { - expect(mergeLeft(null, undefined)).toEqual({}) - expect(mergeLeft(obj, null)).toEqual(obj) - expect(mergeLeft(obj, undefined)).toEqual(obj) - expect(mergeLeft(undefined, obj)).toEqual(obj) + expect(identity(7)).toBe(7) + expect(identity(true)).toBeTrue() + expect(identity({ a : 1 })).toEqual({ a : 1 }) }) ``` @@ -16364,71 +5984,74 @@ test('when undefined or null instead of object', () => { TypeScript test ```typescript -import {mergeLeft} from 'rambda' - -interface Output { - foo: number, - bar: number, -} - -describe('R.mergeLeft', () => { - const result = mergeLeft({foo: 1}, {bar: 2}) - const curriedResult = mergeLeft({foo: 1})({bar: 2}) +import {identity} from 'rambda' - result.foo // $ExpectType number - result.bar // $ExpectType number - curriedResult.bar // $ExpectType number +describe('R.identity', () => { + it('happy', () => { + const result = identity(4) + result // $ExpectType 4 + }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeLeft) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#identity) -### mergeRight +### ifElse ```typescript -mergeRight(target: A, newProps: B): A & B -export function mergeRight(target: any): (newProps: any) => Output +ifElse( + pred: (a: T) => a is TFiltered, + onTrue: (a: TFiltered) => TOnTrueResult, + onFalse: (a: Exclude) => TOnFalseResult, +): (a: T) => TOnTrueResult | TOnFalseResult ``` -It creates a copy of `target` object with overwritten `newProps` properties. Previously known as `R.merge` but renamed after Ramda did the same. - -```javascript -const target = { 'foo': 0, 'bar': 1 } -const newProps = { 'foo': 7 } +It expects `condition`, `onTrue` and `onFalse` functions as inputs and it returns a new function with example name of `fn`. -const result = R.mergeRight(target, newProps) -// => { 'foo': 7, 'bar': 1 } -``` +When `fn`` is called with `input` argument, it will return either `onTrue(input)` or `onFalse(input)` depending on `condition(input)` evaluation. -Try this R.mergeRight example in Rambda REPL +Try this R.ifElse example in Rambda REPL
All TypeScript definitions ```typescript -mergeRight(target: A, newProps: B): A & B -mergeRight(target: any): (newProps: any) => Output; +ifElse( + pred: (a: T) => a is TFiltered, + onTrue: (a: TFiltered) => TOnTrueResult, + onFalse: (a: Exclude) => TOnFalseResult, +): (a: T) => TOnTrueResult | TOnFalseResult; +ifElse(fn: (...args: TArgs) => boolean, onTrue: (...args: TArgs) => TOnTrueResult, onFalse: (...args: TArgs) => TOnFalseResult): (...args: TArgs) => TOnTrueResult | TOnFalseResult; ```
-R.mergeRight source +R.ifElse source ```javascript -export function mergeRight(target, newProps){ - if (arguments.length === 1) - return _newProps => mergeRight(target, _newProps) +import { curry } from './curry.js' - return Object.assign( - {}, target || {}, newProps || {} - ) +function ifElseFn( + condition, onTrue, onFalse +){ + return (...input) => { + const conditionResult = + typeof condition === 'boolean' ? condition : condition(...input) + if (Boolean(conditionResult) ){ + return onTrue(...input) + } + + return onFalse(...input) + } } + +export const ifElse = curry(ifElseFn) ```
@@ -16438,65 +6061,109 @@ export function mergeRight(target, newProps){ Tests ```javascript -import { mergeRight } from './mergeRight.js' +import { always } from './always.js' +import { has } from './has.js' +import { identity } from './identity.js' +import { ifElse } from './ifElse.js' +import { prop } from './prop.js' +import * as R from 'ramda' -const obj = { - foo : 1, - bar : 2, +const condition = has('foo') +const v = function (a){ + return typeof a === 'number' } +const t = function (a){ + return a + 1 +} +const ifFn = x => prop('foo', x).length +const elseFn = () => false test('happy', () => { - expect(mergeRight(obj, { bar : 20 })).toEqual({ - foo : 1, - bar : 20, - }) + const fn = ifElse(condition, ifFn)(elseFn) + + expect(fn({ foo : 'bar' })).toBe(3) + expect(fn({ fo : 'bar' })).toBeFalse() }) -test('curry', () => { - expect(mergeRight(obj)({ baz : 3 })).toEqual({ - foo : 1, - bar : 2, - baz : 3, - }) +test('ramda spec', () => { + const ifIsNumber = ifElse(v) + expect(ifIsNumber(t, identity)(15)).toBe(16) + expect(ifIsNumber(t, identity)('hello')).toBe('hello') }) -/** - * https://github.com/selfrefactor/rambda/issues/77 - */ -test('when undefined or null instead of object', () => { - expect(mergeRight(null, undefined)).toEqual({}) - expect(mergeRight(obj, null)).toEqual(obj) - expect(mergeRight(obj, undefined)).toEqual(obj) - expect(mergeRight(undefined, obj)).toEqual(obj) +test('pass all arguments', () => { + const identity = function (a){ + return a + } + const v = function (){ + return true + } + const onTrue = function (a, b){ + expect(a).toBe(123) + expect(b).toBe('abc') + } + ifElse( + v, onTrue, identity + )(123, 'abc') }) -test('with function inside object', () => { - const result = mergeRight({ a : 1 }, { b : () => 1 }) - expect(typeof result.b).toBe('function') +test('accept constant as condition', () => { + const fn = ifElse(true)(always(true))(always(false)) + + expect(fn()).toBeTrue() }) -describe('acts as if nil values are simply empty objects', () => { - const a = { - w : 1, - x : 2, - } - const b = { - w : 100, - y : 3, - z : 4, - } +test('accept constant as condition - case 2', () => { + const fn = ifElse( + false, always(true), always(false) + ) - it('if the first object is nil', () => { - expect(mergeRight(null, b)).toEqual(b) - }) + expect(fn()).toBeFalse() +}) + +test('curry 1', () => { + const fn = ifElse(condition, ifFn)(elseFn) + + expect(fn({ foo : 'bar' })).toBe(3) + expect(fn({ fo : 'bar' })).toBeFalse() +}) + +test('curry 2', () => { + const fn = ifElse(condition)(ifFn)(elseFn) + + expect(fn({ foo : 'bar' })).toBe(3) + expect(fn({ fo : 'bar' })).toBeFalse() +}) + +test('simple arity of 1', () => { + const condition = x => x > 5 + const onTrue = x => x + 1 + const onFalse = x => x + 10 + const result = ifElse( + condition, onTrue, onFalse + )(1) + expect(result).toBe(11) +}) + +test('simple arity of 2', () => { + const condition = (x, y) => x + y > 5 + const onTrue = (x, y) => x + y + 1 + const onFalse = (x, y) => x + y + 10 + const result = ifElse( + condition, onTrue, onFalse + )(1, 10) + expect(result).toBe(12) +}) - it('if the second object is nil', () => { - expect(mergeRight(a, undefined)).toEqual(a) - }) +test('bug 750', () => { + const value = 34; - it('if both objects are nil', () => { - expect(mergeRight(null, undefined)).toEqual({}) - }) + let result = ifElse( + R.identity, + R.always('true'), + R.always('false') + )(value) + expect(result).toBe('true') }) ``` @@ -16507,92 +6174,108 @@ describe('acts as if nil values are simply empty objects', () => { TypeScript test ```typescript -import {mergeRight} from 'rambda' - -interface Output { - foo: number, - bar: number, -} +import {ifElse} from 'rambda' -describe('R.mergeRight', () => { - const result = mergeRight({foo: 1}, {bar: 2}) - const curriedResult = mergeRight({foo: 1})({bar: 2}) +describe('R.ifElse', () => { + it('happy', () => { + const condition = (x: number) => x > 5 + const onTrue = (x: number) => `foo${x}` + const onFalse = (x: number) => `bar${x}` + const fn = ifElse(condition, onTrue, onFalse) + fn // $ExpectType (x: number) => string + const result = fn(3) + result // $ExpectType string + }) + it('arity of 2', () => { + const condition = (x: number, y: string) => x + y.length > 5 + const onTrue = (x: number, y: string) => `foo${x}-${y}` + const onFalse = (x: number, y: string) => `bar${x}-${y}` + const fn = ifElse(condition, onTrue, onFalse) + fn // $ExpectType (x: number, y: string) => string + const result = fn(3, 'hello') + result // $ExpectType string + }) + test('DefinitelyTyped#59291', () => { + const getLengthIfStringElseDouble = ifElse( + (a: string | number): a is string => true, + a => a.length, + a => a * 2 + ) - result.foo // $ExpectType number - result.bar // $ExpectType number - curriedResult.bar // $ExpectType number + getLengthIfStringElseDouble('foo') // $ExpectType number + getLengthIfStringElseDouble(3) // $ExpectType number + const result = ifElse( + (a: { + foo?: string, + bar: number | string, + }): a is {foo: string, bar: string} => true, + (a): [string, string] => [a.foo, a.bar], + (a): [string | undefined, string | number] => [a.foo, a.bar] + ) + result // $ExpectType (a: { foo?: string | undefined; bar: string | number; }) => [string, string] | [string | undefined, string | number] + }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeRight) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#ifElse) -### mergeWith +### inc + +It increments a number. + +Try this R.inc example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#inc) + +### includes ```typescript -mergeWith(fn: (x: any, z: any) => any, a: Record, b: Record): Record +includes(valueToFind: T, input: string): boolean ``` -It takes two objects and a function, which will be used when there is an overlap between the keys. +If `input` is string, then this method work as native `String.includes`. -```javascript -const result = R.mergeWith( - R.concat, - {values : [ 10, 20 ]}, - {values : [ 15, 35 ]} -) -// => [ 10, 20, 15, 35 ] -``` +If `input` is array, then `R.equals` is used to define if `valueToFind` belongs to the list. -Try this R.mergeWith example in Rambda REPL +Try this R.includes example in Rambda REPL
All TypeScript definitions ```typescript -mergeWith(fn: (x: any, z: any) => any, a: Record, b: Record): Record; -mergeWith(fn: (x: any, z: any) => any, a: Record, b: Record): Output; -mergeWith(fn: (x: any, z: any) => any, a: Record): (b: Record) => Record; -mergeWith(fn: (x: any, z: any) => any, a: Record): (b: Record) => Output; -mergeWith(fn: (x: any, z: any) => any): (a: U, b: V) => Record; -mergeWith(fn: (x: any, z: any) => any): (a: U, b: V) => Output; +includes(valueToFind: T, input: string): boolean; +includes(valueToFind: T): (input: string) => boolean; +includes(valueToFind: T, input: T[]): boolean; +includes(valueToFind: T): (input: T[]) => boolean; ```
-R.mergeWith source +R.includes source ```javascript -import { curry } from './curry.js' - -export function mergeWithFn( - mergeFn, aInput, bInput -){ - const a = aInput ?? {} - const b = bInput ?? {} - const willReturn = {} - - Object.keys(a).forEach(key => { - if (b[ key ] === undefined) willReturn[ key ] = a[ key ] - else willReturn[ key ] = mergeFn(a[ key ], b[ key ]) - }) - - Object.keys(b).forEach(key => { - if (willReturn[ key ] !== undefined) return +import { isArray } from './_internals/isArray.js' +import { _indexOf } from './equals.js' - if (a[ key ] === undefined) willReturn[ key ] = b[ key ] - else willReturn[ key ] = mergeFn(a[ key ], b[ key ]) - }) +export function includes(valueToFind, iterable){ + if (arguments.length === 1) + return _iterable => includes(valueToFind, _iterable) + if (typeof iterable === 'string'){ + return iterable.includes(valueToFind) + } + if (!iterable){ + throw new TypeError(`Cannot read property \'indexOf\' of ${ iterable }`) + } + if (!isArray(iterable)) return false - return willReturn + return _indexOf(valueToFind, iterable) > -1 } - -export const mergeWith = curry(mergeWithFn) ```
@@ -16602,157 +6285,154 @@ export const mergeWith = curry(mergeWithFn) Tests ```javascript -import { concat } from './concat.js' -import { mergeWithFn } from './mergeWith.js' +import { includes as includesRamda } from 'ramda' -test('happy', () => { - const result = mergeWithFn( - concat, - { - a : true, - values : [ 10, 20 ], - }, - { - b : true, - values : [ 15, 35 ], - } - ) - const expected = { - a : true, - b : true, - values : [ 10, 20, 15, 35 ], - } - expect(result).toEqual(expected) -}) +import { includes } from './includes.js' -// https://github.com/ramda/ramda/pull/3222/files#diff-d925d9188b478d2f1d4b26012c6dddac374f9e9d7a336604d654b9a113bfc857 -describe('acts as if nil values are simply empty objects', () => { - it('if the first object is nil and the second empty', () => { - expect(mergeWithFn( - concat, undefined, {} - )).toEqual({}) - }) +test('with string as iterable', () => { + const str = 'foo bar' - it('if the first object is empty and the second nil', () => { - expect(mergeWithFn( - concat, {}, null - )).toEqual({}) - }) + expect(includes('bar')(str)).toBeTrue() + expect(includesRamda('bar')(str)).toBeTrue() + expect(includes('never', str)).toBeFalse() + expect(includesRamda('never', str)).toBeFalse() +}) - it('if both objects are nil', () => { - expect(mergeWithFn( - concat, undefined, null - )).toEqual({}) - }) +test('with array as iterable', () => { + const arr = [ 1, 2, 3 ] - it('if the first object is not empty and the second is nil', () => { - expect(mergeWithFn( - concat, { a : 'a' }, null - )).toEqual({ a : 'a' }) - }) + expect(includes(2)(arr)).toBeTrue() + expect(includesRamda(2)(arr)).toBeTrue() - it('if the first object is nil and the second is not empty', () => { - expect(mergeWithFn( - concat, undefined, { a : 'a' } - )).toEqual({ a : 'a' }) - }) + expect(includes(4, arr)).toBeFalse() + expect(includesRamda(4, arr)).toBeFalse() }) -``` - -
-
+test('with list of objects as iterable', () => { + const arr = [ { a : 1 }, { b : 2 }, { c : 3 } ] -TypeScript test + expect(includes({ c : 3 }, arr)).toBeTrue() + expect(includesRamda({ c : 3 }, arr)).toBeTrue() +}) -```typescript -import {concat, mergeWith} from 'rambda' +test('with NaN', () => { + const result = includes(NaN, [ NaN ]) + const ramdaResult = includesRamda(NaN, [ NaN ]) + expect(result).toBeTrue() + expect(ramdaResult).toBeTrue() +}) -interface Output { - a: boolean, - b: boolean, - values: number[], -} -const A = { - a: true, - values: [10, 20], -} -const B = { - b: true, - values: [15, 35], -} +test('with wrong input that does not throw', () => { + const result = includes(1, /foo/g) + const ramdaResult = includesRamda(1, /foo/g) + expect(result).toBeFalse() + expect(ramdaResult).toBeFalse() +}) -describe('R.mergeWith', () => { - test('no curry | without explicit types', () => { - const result = mergeWith(concat, A, B) - result // $ExpectType Record - }) - test('no curry | with explicit types', () => { - const result = mergeWith(concat, A, B) - result // $ExpectType Output - }) - test('curry 1 | without explicit types', () => { - const result = mergeWith(concat, A)(B) - result // $ExpectType Record - }) - test('curry 1 | with explicit types', () => { - const result = mergeWith(concat, A)(B) - result // $ExpectType Output - }) - test('curry 2 | without explicit types', () => { - const result = mergeWith(concat)(A, B) - result // $ExpectType Record - }) - test('curry 2 | with explicit types', () => { - const result = mergeWith(concat)(A, B) - result // $ExpectType Output - }) +test('throws on wrong input - match ramda behaviour', () => { + expect(() => includes(2, null)).toThrowWithMessage(TypeError, + 'Cannot read property \'indexOf\' of null') + expect(() => includesRamda(2, null)).toThrowWithMessage(TypeError, + 'Cannot read properties of null (reading \'indexOf\')') + expect(() => includes(2, undefined)).toThrowWithMessage(TypeError, + 'Cannot read property \'indexOf\' of undefined') + expect(() => includesRamda(2, undefined)).toThrowWithMessage(TypeError, + 'Cannot read properties of undefined (reading \'indexOf\')') }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeWith) +
-### min +TypeScript test ```typescript +import {includes} from 'rambda' + +const list = [{a: {b: '1'}}, {a: {c: '2'}}, {a: {b: '3'}}] + +describe('R.includes', () => { + it('happy', () => { + const result = includes({a: {b: '1'}}, list) + result // $ExpectType boolean + const result2 = includes('oo', ['f', 'oo']) + result2 // $ExpectType boolean + }) + it('with string', () => { + const str = 'foo' as 'foo' | 'bar' + const result = includes('oo', str) + const curriedResult = includes('oo')(str) -min(x: T, y: T): T + result // $ExpectType boolean + curriedResult // $ExpectType boolean + }) +}) ``` -It returns the lesser value between `x` and `y`. +
-```javascript -const result = [ - R.min(5, 7), - R.min('bar', 'foo'), -] -// => [5, 'bar'] +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#includes) + +### indexBy + +It generates object with properties provided by `condition` and values provided by `list` array. + +If `condition` is a function, then all list members are passed through it. + +If `condition` is a string, then all list members are passed through `R.path(condition)`. + +Try this R.indexBy example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#indexBy) + +### indexOf + +It returns the index of the first element of `list` equals to `valueToFind`. + +If there is no such element, it returns `-1`. + +Try this R.indexOf example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#indexOf) + +### init + +```typescript + +init(input: T): T extends readonly [...infer U, any] ? U : [...T] ``` -Try this R.min example in Rambda REPL +It returns all but the last element of list or string `input`. + +Try this R.init example in Rambda REPL
All TypeScript definitions ```typescript -min(x: T, y: T): T; -min(x: T): (y: T) => T; +init(input: T): T extends readonly [...infer U, any] ? U : [...T]; +init(input: string): string; ```
-R.min source +R.init source ```javascript -export function min(x, y){ - if (arguments.length === 1) return _y => min(x, _y) +import baseSlice from './_internals/baseSlice.js' + +export function init(listOrString){ + if (typeof listOrString === 'string') return listOrString.slice(0, -1) - return y < x ? y : x + return listOrString.length ? + baseSlice( + listOrString, 0, -1 + ) : + [] } ``` @@ -16763,11 +6443,21 @@ export function min(x, y){ Tests ```javascript -import { min } from './min.js' +import { init } from './init.js' -test('happy', () => { - expect(min(2, 1)).toBe(1) - expect(min(1)(2)).toBe(1) +test('with array', () => { + expect(init([ 1, 2, 3 ])).toEqual([ 1, 2 ]) + expect(init([ 1, 2 ])).toEqual([ 1 ]) + expect(init([ 1 ])).toEqual([]) + expect(init([])).toEqual([]) + expect(init([])).toEqual([]) + expect(init([ 1 ])).toEqual([]) +}) + +test('with string', () => { + expect(init('foo')).toBe('fo') + expect(init('f')).toBe('') + expect(init('')).toBe('') }) ``` @@ -16778,183 +6468,119 @@ test('happy', () => { TypeScript test ```typescript -import {min} from 'rambda' +import {init} from 'rambda' -const first = 1 -const second = 2 +describe('R.init', () => { + it('with string', () => { + const result = init('foo') -describe('R.min', () => { - it('happy', () => { - const result = min(first, second) - result // $ExpectType 1 | 2 - }) - it('curried', () => { - const result = min(first, second) - result // $ExpectType 1 | 2 + result // $ExpectType string }) - it('curried - cann pass type', () => { - const result = min(first, second) - result // $ExpectType number + it('with list - one type', () => { + const result = init([1, 2, 3]) + + result // $ExpectType number[] }) - it('can pass type', () => { - const result = min(first, second) - result // $ExpectType number + it('with list - mixed types', () => { + const result = init([1, 2, 3, 'foo', 'bar']) + + result // $ExpectType (string | number)[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#min) - -### 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. - -```javascript -const compareFn = Math.abs - -R.minBy(compareFn, -5, 2) // => -5 -``` - -Try this R.minBy example in Rambda REPL - -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#init) -All TypeScript definitions +### innerJoin -```typescript -minBy(compareFn: (input: T) => Ord, x: T, y: T): T; -minBy(compareFn: (input: T) => Ord, x: T): (y: T) => T; -minBy(compareFn: (input: T) => Ord): (x: T) => (y: T) => T; -``` +It returns a new list by applying a `predicate` function to all elements of `list1` and `list2` and keeping only these elements where `predicate` returns `true`. -
+Try this R.innerJoin example in Rambda REPL -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#innerJoin) -R.minBy source +### insert -```javascript -import { curry } from './curry.js' +Try this R.insert example in Rambda REPL -export function minByFn( - compareFn, x, y -){ - return compareFn(y) < compareFn(x) ? y : x -} +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#insert) -export const minBy = curry(minByFn) -``` +### insertAll -
+Try this R.insertAll example in Rambda REPL -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#insertAll) -Tests +### intersection -```javascript -import { minBy } from './minBy.js' +It loops through `listA` and `listB` and returns the intersection of the two according to `R.equals`. -test('happy', () => { - expect(minBy( - Math.abs, -5, 2 - )).toBe(2) -}) +Try this R.intersection example in Rambda REPL -test('curried', () => { - expect(minBy(Math.abs)(2, -5)).toBe(2) - expect(minBy(Math.abs)(2)(-5)).toBe(2) -}) -``` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#intersection) -
+### intersperse -
+It adds a `separator` between members of `list`. -TypeScript test +Try this R.intersperse example in Rambda REPL -```typescript -import {minBy} from 'rambda' +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#intersperse) -const compareFn = (x: number) => x % 2 === 0 ? 1 : -1 -const first = 1 -const second = 2 +### is -describe('R.minBy', () => { - it('happy', () => { - const result = minBy(compareFn, first, second) - result // $ExpectType 1 | 2 - }) -}) -``` +It returns `true` if `x` is instance of `targetPrototype`. -
+Try this R.is example in Rambda REPL -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#minBy) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#is) -### modify +### isEmpty ```typescript -modify(prop: K, fn: (value: T) => T): >(object: U) => U +isEmpty(x: T): boolean ``` -```javascript -const result = R.modify() -// => -``` +It returns `true` if `x` is `empty`. -Try this R.modify example in Rambda REPL +Try this R.isEmpty example in Rambda REPL
All TypeScript definitions ```typescript -modify(prop: K, fn: (value: T) => T): >(object: U) => U; -modify(prop: K, fn: (value: U[K]) => U[K], object: U): U; -modify(prop: K): { - (fn: (value: T) => T): >(object: U) => U; - >(fn: (value: T) => T, object: U): U; -}; +isEmpty(x: T): boolean; ```
-R.modify source +R.isEmpty source ```javascript -import { isArray } from './_internals/isArray.js' -import { isIterable } from './_internals/isIterable.js' -import { curry } from './curry.js' -import { updateFn } from './update.js' +import { type } from './type.js' -function modifyFn( - property, fn, iterable -){ - if (!isIterable(iterable)) return iterable - if (iterable[ property ] === undefined) return iterable - if (isArray(iterable)){ - return updateFn( - property, fn(iterable[ property ]), iterable - ) +export function isEmpty(input){ + const inputType = type(input) + if ([ 'Undefined', 'NaN', 'Number', 'Null' ].includes(inputType)) + return false + if (!input) return true + + if (inputType === 'Object'){ + return Object.keys(input).length === 0 } - return { - ...iterable, - [ property ] : fn(iterable[ property ]), + if (inputType === 'Array'){ + return input.length === 0 } -} -export const modify = curry(modifyFn) + return false +} ```
@@ -16964,223 +6590,157 @@ export const modify = curry(modifyFn) Tests ```javascript -import { modify as modifyRamda } from 'ramda' - -import { compareCombinations, FALSY_VALUES } from './_internals/testUtils.js' -import { add } from './add.js' -import { compose } from './compose.js' -import { modify } from './modify.js' - -const person = { - name : 'foo', - age : 20, -} +import { isEmpty } from './isEmpty.js' test('happy', () => { - expect(modify( - 'age', x => x + 1, person - )).toEqual({ - name : 'foo', - age : 21, - }) + expect(isEmpty(undefined)).toBeFalse() + expect(isEmpty('')).toBeTrue() + expect(isEmpty(null)).toBeFalse() + expect(isEmpty(' ')).toBeFalse() + expect(isEmpty(new RegExp(''))).toBeFalse() + expect(isEmpty([])).toBeTrue() + expect(isEmpty([ [] ])).toBeFalse() + expect(isEmpty({})).toBeTrue() + expect(isEmpty({ x : 0 })).toBeFalse() + expect(isEmpty(0)).toBeFalse() + expect(isEmpty(NaN)).toBeFalse() + expect(isEmpty([ '' ])).toBeFalse() }) +``` -test('property is missing', () => { - expect(modify( - 'foo', x => x + 1, person - )).toEqual(person) -}) +
-test('adjust if `array` at the given key with the `transformation` function', () => { - expect(modify( - 1, add(1), [ 100, 1400 ] - )).toEqual([ 100, 1401 ]) -}) +
-describe('ignores transformations if the input value is not Array and Object', () => { - ;[ 42, undefined, null, '' ].forEach(value => { - it(`${ value }`, () => { - expect(modify( - 'a', add(1), value - )).toEqual(value) - }) - }) -}) +TypeScript test -const possibleProperties = [ ...FALSY_VALUES, 'foo', 0 ] -const possibleTransformers = [ - ...FALSY_VALUES, - add(1), - add('foo'), - compose, - String, -] -const possibleObjects = [ - ...FALSY_VALUES, - {}, - [ 1, 2, 3 ], - { - a : 1, - foo : 2, - }, - { - a : 1, - foo : [ 1 ], - }, - { - a : 1, - foo : 'bar', - }, -] +```typescript +import {isEmpty} from 'rambda' -describe('brute force', () => { - compareCombinations({ - fn : modify, - fnRamda : modifyRamda, - firstInput : possibleProperties, - secondInput : possibleTransformers, - thirdInput : possibleObjects, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 0, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 0, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 630, - } - `) - }, +describe('R.isEmpty', () => { + it('happy', () => { + const result = isEmpty('foo') + result // $ExpectType boolean }) }) ```
-
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#isEmpty) -TypeScript test +### isNil ```typescript -import { add, identity, map, modify, pipe, toUpper } from 'rambda'; -type Obj = { - foo: string; - bar: number; -}; +isNil(x: any): x is null | undefined +``` -describe('R.modify', () => { - it('ramda tests', () => { - const result1 = modify('foo', toUpper, {} as Obj); - result1; // $ExpectType Obj +It returns `true` if `x` is either `null` or `undefined`. - const result2 = modify('bar', add(1), {} as Obj); - result2; // $ExpectType Obj +Try this R.isNil example in Rambda REPL - const result3 = modify('foo', toUpper)({} as Obj); - result3; // $ExpectType Obj +
- const result4 = modify('bar', add(1))({} as Obj); - result4; // $ExpectType Obj +All TypeScript definitions - const result5 = modify('foo')(toUpper)({} as Obj); - result5; // $ExpectType Obj +```typescript +isNil(x: any): x is null | undefined; +``` - const result6 = modify('bar')(add(1))({} as Obj); - result6; // $ExpectType Obj +
- const result7 = modify('foo')(toUpper, {} as Obj); - result7; // $ExpectType Obj +
- const result8 = modify('bar')(add(1), {} as Obj); - result8; // $ExpectType Obj +R.isNil source - const result9 = modify('foo', identity, {} as Obj); - result9; // $ExpectType Obj +```javascript +export function isNil(x){ + return x === undefined || x === null +} +``` - // @ts-expect-error - modify('foo', add(1), {} as Obj); - // @ts-expect-error - modify('bar', toUpper, {} as Obj); +
- const f = pipe(map(modify('foo', toUpper))); +
- f([] as Obj[]); // $ExpectType Obj[] - }); -}); +Tests + +```javascript +import { isNil } from './isNil.js' + +test('happy', () => { + expect(isNil(null)).toBeTrue() + + expect(isNil(undefined)).toBeTrue() + + expect(isNil([])).toBeFalse() +}) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#modify) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#isNil) -### modifyPath +### isNotEmpty ```typescript -modifyPath>(path: Path, fn: (x: any) => unknown, object: Record): T +isNotEmpty(value: T[]): value is NonEmptyArray ``` -It changes a property of object on the base of provided path and transformer function. +
-```javascript -const result = R.modifyPath('a.b.c', x=> x+1, {a:{b: {c:1}}}) -// => {a:{b: {c:2}}} +All TypeScript definitions + +```typescript +isNotEmpty(value: T[]): value is NonEmptyArray; +isNotEmpty(value: readonly T[]): value is ReadonlyNonEmptyArray; +isNotEmpty(value: any): boolean; ``` -Try this R.modifyPath example in Rambda REPL +
+ +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#isNotEmpty) + +### isNotNil + +Try this R.isNotNil example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#isNotNil) + +### join + +```typescript + +join(glue: string, list: T[]): string +``` + +It returns a string of all `list` instances joined with a `glue`. + +Try this R.join example in Rambda REPL
All TypeScript definitions ```typescript -modifyPath>(path: Path, fn: (x: any) => unknown, object: Record): T; -modifyPath>(path: Path, fn: (x: any) => unknown): (object: Record) => T; -modifyPath>(path: Path): (fn: (x: any) => unknown) => (object: Record) => T; +join(glue: string, list: T[]): string; +join(glue: string): (list: T[]) => string; ```
-R.modifyPath source +R.join source ```javascript -import { createPath } from './_internals/createPath.js' -import { isArray } from './_internals/isArray.js' -import { assoc } from './assoc.js' -import { curry } from './curry.js' -import { path as pathModule } from './path.js' - -export function modifyPathFn( - pathInput, fn, object -){ - const path = createPath(pathInput) - if (path.length === 1){ - return { - ...object, - [ path[ 0 ] ] : fn(object[ path[ 0 ] ]), - } - } - if (pathModule(path, object) === undefined) return object - - const val = modifyPath( - Array.prototype.slice.call(path, 1), - fn, - object[ path[ 0 ] ] - ) - if (val === object[ path[ 0 ] ]){ - return object - } +export function join(glue, list){ + if (arguments.length === 1) return _list => join(glue, _list) - return assoc( - path[ 0 ], val, object - ) + return list.join(glue) } - -export const modifyPath = curry(modifyPathFn) ```
@@ -17190,21 +6750,16 @@ export const modifyPath = curry(modifyPathFn) Tests ```javascript -import { modifyPath } from './modifyPath.js' +import { join } from './join.js' -test('happy', () => { - const result = modifyPath( - 'a.b.c', x => x + 1, { a : { b : { c : 1 } } } - ) - expect(result).toEqual({ a : { b : { c : 2 } } }) -}) +test('curry', () => { + expect(join('|')([ 'foo', 'bar', 'baz' ])).toBe('foo|bar|baz') -test('with array', () => { - const input = { foo : [ { bar : '123' } ] } - const result = modifyPath( - 'foo.0.bar', x => x + 'foo', input - ) - expect(result).toEqual({ foo : { 0 : { bar : '123foo' } } }) + expect(join('|', [ 1, 2, 3 ])).toBe('1|2|3') + + const spacer = join(' ') + + expect(spacer([ 'a', 2, 3.4 ])).toBe('a 2 3.4') }) ``` @@ -17215,64 +6770,53 @@ test('with array', () => { TypeScript test ```typescript -import {modifyPath} from 'rambda' - -const obj = {a: {b: {c: 1}}} +import {join} from 'rambda' -describe('R.modifyPath', () => { +describe('R.join', () => { it('happy', () => { - const result = modifyPath('a.b.c', (x: number) => x + 1, obj) - result // $ExpectType Record - }) - it('explicit return type', () => { - interface Foo extends Record { - a: 1, - } - const result = modifyPath('a.b.c', (x: number) => x + 1, obj) - result // $ExpectType Foo + const result = join('|', [1, 2, 3]) + result // $ExpectType string }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#modifyPath) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#join) -### modulo +### juxt ```typescript -modulo(x: number, y: number): number +juxt(fns: [(...a: A) => R1]): (...a: A) => [R1] ``` -Curried version of `x%y`. - -```javascript -R.modulo(17, 3) // => 2 -``` +It applies list of function to a list of inputs. -Try this R.modulo example in Rambda REPL +Try this R.juxt example in Rambda REPL
All TypeScript definitions ```typescript -modulo(x: number, y: number): number; -modulo(x: number): (y: number) => number; +juxt(fns: [(...a: A) => R1]): (...a: A) => [R1]; +juxt(fns: [(...a: A) => R1, (...a: A) => R2]): (...a: A) => [R1, R2]; +juxt(fns: [(...a: A) => R1, (...a: A) => R2, (...a: A) => R3]): (...a: A) => [R1, R2, R3]; +juxt(fns: [(...a: A) => R1, (...a: A) => R2, (...a: A) => R3, (...a: A) => R4]): (...a: A) => [R1, R2, R3, R4]; +juxt(fns: [(...a: A) => R1, (...a: A) => R2, (...a: A) => R3, (...a: A) => R4, (...a: A) => R5]): (...a: A) => [R1, R2, R3, R4, R5]; +juxt(fns: Array<(...args: A) => U>): (...args: A) => U[]; ```
-R.modulo source +R.juxt source ```javascript -export function modulo(x, y){ - if (arguments.length === 1) return _y => modulo(x, _y) - - return x % y +export function juxt(listOfFunctions){ + return (...args) => listOfFunctions.map(fn => fn(...args)) } ``` @@ -17283,11 +6827,14 @@ export function modulo(x, y){ Tests ```javascript -import { modulo } from './modulo.js' +import { juxt } from './juxt.js' test('happy', () => { - expect(modulo(17, 3)).toBe(2) - expect(modulo(15)(6)).toBe(3) + const fn = juxt([ Math.min, Math.max, Math.min ]) + const result = fn( + 3, 4, 9, -3 + ) + expect(result).toEqual([ -3, 9, -3 ]) }) ``` @@ -17298,84 +6845,51 @@ test('happy', () => { TypeScript test ```typescript -import {modulo} from 'rambda' +import {juxt} from 'rambda' -describe('R.modulo', () => { +describe('R.juxt', () => { it('happy', () => { - const result = modulo(4, 1) - - result // $ExpectType number - }) - it('curried', () => { - const result = modulo(4)(1) - - result // $ExpectType number + const fn = juxt([Math.min, Math.max]) + const result = fn(3, 4, 9, -3) + result // $ExpectType [number, number] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#modulo) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#juxt) -### move +### keys ```typescript -move(fromIndex: number, toIndex: number, list: T[]): T[] +keys(x: T): (keyof T & string)[] ``` -It returns a copy of `list` with exchanged `fromIndex` and `toIndex` elements. - -> :boom: Rambda.move doesn't support negative indexes - it throws an error. - -```javascript -const list = [1, 2, 3] -const result = R.move(0, 1, list) -// => [2, 1, 3] -``` +It applies `Object.keys` over `x` and returns its keys. -
Try this R.move example in Rambda REPL +Try this R.keys example in Rambda REPL
All TypeScript definitions ```typescript -move(fromIndex: number, toIndex: number, list: T[]): T[]; -move(fromIndex: number, toIndex: number): (list: T[]) => T[]; -move(fromIndex: number): { - (toIndex: number, list: T[]): T[]; - (toIndex: number): (list: T[]) => T[]; -}; +keys(x: T): (keyof T & string)[]; +keys(x: T): string[]; ```
-R.move source +R.keys source ```javascript -import { cloneList } from './_internals/cloneList.js' -import { curry } from './curry.js' - -function moveFn( - fromIndex, toIndex, list -){ - if (fromIndex < 0 || toIndex < 0){ - throw new Error('Rambda.move does not support negative indexes') - } - if (fromIndex > list.length - 1 || toIndex > list.length - 1) return list - - const clone = cloneList(list) - clone[ fromIndex ] = list[ toIndex ] - clone[ toIndex ] = list[ fromIndex ] - - return clone +export function keys(x){ + return Object.keys(x) } - -export const move = curry(moveFn) ```
@@ -17385,109 +6899,55 @@ export const move = curry(moveFn) Tests ```javascript -import { move } from './move.js' -const list = [ 1, 2, 3, 4 ] +import { keys } from './keys.js' test('happy', () => { - const result = move( - 0, 1, list - ) - - expect(result).toEqual([ 2, 1, 3, 4 ]) -}) - -test('with negative index', () => { - const errorMessage = 'Rambda.move does not support negative indexes' - expect(() => move( - 0, -1, list - )).toThrowErrorMatchingInlineSnapshot('"Rambda.move does not support negative indexes"') - expect(() => move( - -1, 0, list - )).toThrowErrorMatchingInlineSnapshot('"Rambda.move does not support negative indexes"') -}) - -test('when indexes are outside the list outbounds', () => { - const result1 = move( - 10, 1, list - ) - const result2 = move( - 1, 10, list - ) - - expect(result1).toEqual(list) - expect(result2).toEqual(list) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {move} from 'rambda' - -const list = [1, 2, 3] - -describe('R.move', () => { - it('happy', () => { - const result = move(0, 1, list) - - result // $ExpectType number[] - }) - it('curried 1', () => { - const result = move(0, 1)(list) - - result // $ExpectType number[] - }) - it('curried 2', () => { - const result = move(0)(1)(list) - - result // $ExpectType number[] - }) + expect(keys({ a : 1 })).toEqual([ 'a' ]) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#move) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#keys) -### multiply +### last ```typescript -multiply(x: number, y: number): number +last(str: ''): undefined ``` -Curried version of `x*y`. - -```javascript -R.multiply(2, 4) // => 8 -``` +It returns the last element of `input`, as the `input` can be either a string or an array. It returns `undefined` if array has length of 0. -Try this R.multiply example in Rambda REPL +Try this R.last example in Rambda REPL
All TypeScript definitions ```typescript -multiply(x: number, y: number): number; -multiply(x: number): (y: number) => number; +last(str: ''): undefined; +last(str: string): string; +last(list: readonly[]): undefined; +last(list: never[]): undefined; +last(array: T): LastArrayElement; +last(array: T): LastArrayElement; +last(str: string): string | undefined; ```
-R.multiply source +R.last source ```javascript -export function multiply(x, y){ - if (arguments.length === 1) return _y => multiply(x, _y) +export function last(listOrString){ + if (typeof listOrString === 'string'){ + return listOrString[ listOrString.length - 1 ] || '' + } - return x * y + return listOrString[ listOrString.length - 1 ] } ``` @@ -17498,71 +6958,62 @@ export function multiply(x, y){ Tests ```javascript -import { multiply } from './multiply.js' +import { last } from './last.js' -test('happy', () => { - expect(multiply(2, 4)).toBe(8) - expect(multiply(2)(4)).toBe(8) +test('with list', () => { + expect(last([ 1, 2, 3 ])).toBe(3) + expect(last([])).toBeUndefined() }) -``` - -
- -
- -TypeScript test - -```typescript -import {multiply} from 'rambda' - -describe('R.multiply', () => { - it('happy', () => { - const result = multiply(4, 1) - - result // $ExpectType number - }) - it('curried', () => { - const result = multiply(4)(1) - result // $ExpectType number - }) +test('with string', () => { + expect(last('abc')).toBe('c') + expect(last('')).toBe('') }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#multiply) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#last) -### negate +### lastIndexOf ```typescript -negate(x: number): number +lastIndexOf(target: T, list: T[]): number ``` -```javascript -R.negate(420)// => -420 -``` +It returns the last index of `target` in `list` array. -Try this R.negate example in Rambda REPL +`R.equals` is used to determine equality between `target` and members of `list`. + +If there is no such index, then `-1` is returned. + +Try this R.lastIndexOf example in Rambda REPL
All TypeScript definitions ```typescript -negate(x: number): number; +lastIndexOf(target: T, list: T[]): number; +lastIndexOf(target: T): (list: T[]) => number; ```
-R.negate source +R.lastIndexOf source ```javascript -export function negate(x){ - return -x +import { _lastIndexOf } from './equals.js' + +export function lastIndexOf(valueToFind, list){ + if (arguments.length === 1){ + return _list => _lastIndexOf(valueToFind, _list) + } + + return _lastIndexOf(valueToFind, list) } ``` @@ -17573,61 +7024,127 @@ export function negate(x){ Tests ```javascript -import { negate } from './negate.js' +import { lastIndexOf as lastIndexOfRamda } from 'ramda' + +import { compareCombinations } from './_internals/testUtils.js' +import { possibleIterables, possibleTargets } from './indexOf.spec.js' +import { lastIndexOf } from './lastIndexOf.js' + +test('with NaN', () => { + expect(lastIndexOf(NaN, [ NaN ])).toBe(0) +}) + +test('will throw with bad input', () => { + expect(lastIndexOfRamda([], true)).toBe(-1) + expect(() => indexOf([], true)).toThrowErrorMatchingInlineSnapshot('"indexOf is not defined"') +}) + +test('without list of objects - no R.equals', () => { + expect(lastIndexOf(3, [ 1, 2, 3, 4 ])).toBe(2) + expect(lastIndexOf(10)([ 1, 2, 3, 4 ])).toBe(-1) +}) + +test('list of objects uses R.equals', () => { + const listOfObjects = [ { a : 1 }, { b : 2 }, { c : 3 } ] + expect(lastIndexOf({ c : 4 }, listOfObjects)).toBe(-1) + expect(lastIndexOf({ c : 3 }, listOfObjects)).toBe(2) +}) + +test('list of arrays uses R.equals', () => { + const listOfLists = [ [ 1 ], [ 2, 3 ], [ 2, 3, 4 ], [ 2, 3 ], [ 1 ], [] ] + expect(lastIndexOf([], listOfLists)).toBe(5) + expect(lastIndexOf([ 1 ], listOfLists)).toBe(4) + expect(lastIndexOf([ 2, 3, 4 ], listOfLists)).toBe(2) + expect(lastIndexOf([ 2, 3, 5 ], listOfLists)).toBe(-1) +}) + +test('with string as iterable', () => { + expect(() => lastIndexOf('a', 'abc')).toThrowErrorMatchingInlineSnapshot('"Cannot read property \'indexOf\' of abc"') + expect(lastIndexOfRamda('a', 'abc')).toBe(0) +}) -test('negate', () => { - expect(negate(420)).toBe(-420) - expect(negate(-13)).toBe(13) +describe('brute force', () => { + compareCombinations({ + fn : lastIndexOf, + fnRamda : lastIndexOfRamda, + firstInput : possibleTargets, + secondInput : possibleIterables, + callback : errorsCounters => { + expect(errorsCounters).toMatchInlineSnapshot(` + { + "ERRORS_MESSAGE_MISMATCH": 0, + "ERRORS_TYPE_MISMATCH": 34, + "RESULTS_MISMATCH": 0, + "SHOULD_NOT_THROW": 51, + "SHOULD_THROW": 0, + "TOTAL_TESTS": 170, + } + `) + }, + }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#negate) +
-### none +TypeScript test ```typescript +import {lastIndexOf} from 'rambda' -none(predicate: (x: T) => boolean, list: T[]): boolean +const list = [1, 2, 3] + +describe('R.lastIndexOf', () => { + it('happy', () => { + const result = lastIndexOf(2, list) + result // $ExpectType number + }) + it('curried', () => { + const result = lastIndexOf(2)(list) + result // $ExpectType number + }) +}) ``` -It returns `true`, if all members of array `list` returns `false`, when applied as argument to `predicate` function. +
-```javascript -const list = [ 0, 1, 2, 3, 4 ] -const predicate = x => x > 6 +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lastIndexOf) + +### length -const result = R.none(predicate, arr) -// => true +```typescript + +length(input: T[]): number ``` -Try this R.none example in Rambda REPL +It returns the `length` property of list or string `input`. + +Try this R.length example in Rambda REPL
All TypeScript definitions ```typescript -none(predicate: (x: T) => boolean, list: T[]): boolean; -none(predicate: (x: T) => boolean): (list: T[]) => boolean; +length(input: T[]): number; ```
-R.none source +R.length source ```javascript -export function none(predicate, list){ - if (arguments.length === 1) return _list => none(predicate, _list) +import { isArray } from './_internals/isArray.js' - for (let i = 0; i < list.length; i++){ - if (predicate(list[ i ])) return false - } +export function length(x){ + if (isArray(x)) return x.length + if (typeof x === 'string') return x.length - return true + return NaN } ``` @@ -17638,85 +7155,81 @@ export function none(predicate, list){ Tests ```javascript -import { none } from './none.js' +import { length as lengthRamda } from 'ramda' -const isEven = n => n % 2 === 0 +import { length } from './length.js' -test('when true', () => { - expect(none(isEven, [ 1, 3, 5, 7 ])).toBeTrue() +test('happy', () => { + expect(length('foo')).toBe(3) + expect(length([ 1, 2, 3 ])).toBe(3) + expect(length([])).toBe(0) }) -test('when false curried', () => { - expect(none(input => input > 1, [ 1, 2, 3 ])).toBeFalse() +test('with empty string', () => { + expect(length('')).toBe(0) }) -``` - -
-
- -TypeScript test +test('with bad input returns NaN', () => { + expect(length(0)).toBeNaN() + expect(length({})).toBeNaN() + expect(length(null)).toBeNaN() + expect(length(undefined)).toBeNaN() +}) -```typescript -import {none} from 'rambda' +test('with length as property', () => { + const input1 = { length : '123' } + const input2 = { length : null } + const input3 = { length : '' } -describe('R.none', () => { - it('happy', () => { - const result = none( - x => { - x // $ExpectType number - return x > 0 - }, - [1, 2, 3] - ) - result // $ExpectType boolean - }) - it('curried needs a type', () => { - const result = none(x => { - x // $ExpectType number - return x > 0 - })([1, 2, 3]) - result // $ExpectType boolean - }) + expect(length(input1)).toBeNaN() + expect(lengthRamda(input1)).toBeNaN() + expect(length(input2)).toBeNaN() + expect(lengthRamda(input2)).toBeNaN() + expect(length(input3)).toBeNaN() + expect(lengthRamda(input3)).toBeNaN() }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#none) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#length) -### not +### lens ```typescript -not(input: any): boolean +lens(getter: (s: S) => A, setter: (a: A, s: S) => S): Lens ``` -It returns a boolean negated version of `input`. +It returns a `lens` for the given `getter` and `setter` functions. -```javascript -R.not(false) // true -``` +The `getter` **gets** the value of the focus; the `setter` **sets** the value of the focus. -Try this R.not example in Rambda REPL +The setter should not mutate the data structure. + +Try this R.lens example in Rambda REPL
All TypeScript definitions ```typescript -not(input: any): boolean; +lens(getter: (s: S) => A, setter: (a: A, s: S) => S): Lens; ```
-R.not source +R.lens source ```javascript -export function not(input){ - return !input +export function lens(getter, setter){ + return function (functor){ + return function (target){ + return functor(getter(target)).map(focus => setter(focus, target)) + } + } } ``` @@ -17724,92 +7237,104 @@ export function not(input){
-Tests - -```javascript -import { not } from './not.js' +TypeScript test -test('not', () => { - expect(not(false)).toBeTrue() - expect(not(true)).toBeFalse() - expect(not(0)).toBeTrue() - expect(not(1)).toBeFalse() -}) -``` +```typescript +import {lens, assoc, lensProp, view, lensIndex, over, lensPath} from 'rambda' -
+interface Input { + foo: string, +} +const testObject: Input = { + foo: 'Jazz', +} -
+describe('R.lens', () => { + it('happy', () => { + const fn = lens((x: Input) => { + x.foo // $ExpectType string + return x.foo + }, assoc('name')) + fn // $ExpectType Lens + }) +}) -TypeScript test +describe('R.lensProp', () => { + it('happy', () => { + const result = view(lensProp('foo'), testObject) + result // $ExpectType string + }) + it('issue 740', () => { + // @ts-expect-error + over(lensProp('x'), (n) => String(n), {x: 1}) + }) +}) -```typescript -import {not} from 'rambda' +describe('R.lensIndex', () => { + const testList: Input[] = [{foo: 'bar'}, {foo: 'baz'}] + it('happy', () => { + const result = view(lensIndex(0), testList) + result // $ExpectType Input + result.foo // $ExpectType string + }) +}) -describe('R.not', () => { +describe('R.lensPath', () => { + const path = lensPath(['bar', 'a']) it('happy', () => { - const result = not(4) + const result = view(path, testObject) + result // $ExpectType string + }) +}) - result // $ExpectType boolean +describe('R.view', () => { + const fooLens = lens((x: Input) => { + return x.foo + }, assoc('foo')) + it('happt', () => { + const result = view(fooLens, testObject) + result // $ExpectType string }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#not) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lens) -### nth +### lensIndex ```typescript -nth(index: number, input: string): string +lensIndex(n: number): Lens ``` -Curried version of `input[index]`. - -```javascript -const list = [1, 2, 3] -const str = 'foo' - -const result = [ - R.nth(2, list), - R.nth(6, list), - R.nth(0, str), -] -// => [3, undefined, 'f'] -``` +It returns a lens that focuses on specified `index`. -Try this R.nth example in Rambda REPL +Try this R.lensIndex example in Rambda REPL
All TypeScript definitions ```typescript -nth(index: number, input: string): string; -nth(index: number, input: T[]): T | undefined; -nth(n: number): { - (input: T[]): T | undefined; - (input: string): string; -}; +lensIndex(n: number): Lens; +lensIndex(n: N): Lens; ```
-R.nth source +R.lensIndex source ```javascript -export function nth(index, input){ - if (arguments.length === 1) return _input => nth(index, _input) - - const idx = index < 0 ? input.length + index : index +import { lens } from './lens.js' +import { nth } from './nth.js' +import { update } from './update.js' - return Object.prototype.toString.call(input) === '[object String]' ? - input.charAt(idx) : - input[ idx ] +export function lensIndex(index){ + return lens(nth(index), update(index)) } ``` @@ -17820,110 +7345,108 @@ export function nth(index, input){ Tests ```javascript -import { nth } from './nth.js' +import { compose } from './compose.js' +import { keys } from './keys.js' +import { lensIndex } from './lensIndex.js' +import { over } from './over.js' +import { set } from './set.js' +import { view } from './view.js' -test('happy', () => { - expect(nth(2, [ 1, 2, 3, 4 ])).toBe(3) -}) +const testList = [ { a : 1 }, { b : 2 }, { c : 3 } ] -test('with curry', () => { - expect(nth(2)([ 1, 2, 3, 4 ])).toBe(3) +test('focuses list element at the specified index', () => { + expect(view(lensIndex(0), testList)).toEqual({ a : 1 }) }) -test('with string and correct index', () => { - expect(nth(2)('foo')).toBe('o') +test('returns undefined if the specified index does not exist', () => { + expect(view(lensIndex(10), testList)).toBeUndefined() }) -test('with string and invalid index', () => { - expect(nth(20)('foo')).toBe('') +test('sets the list value at the specified index', () => { + expect(set( + lensIndex(0), 0, testList + )).toEqual([ 0, { b : 2 }, { c : 3 } ]) }) -test('with negative index', () => { - expect(nth(-3)([ 1, 2, 3, 4 ])).toBe(2) +test('applies function to the value at the specified list index', () => { + expect(over( + lensIndex(2), keys, testList + )).toEqual([ { a : 1 }, { b : 2 }, [ 'c' ] ]) }) -``` - -
- -
- -TypeScript test - -```typescript -import {nth} from 'rambda' - -const list = [1, 2, 3] -describe('R.nth', () => { - it('happy', () => { - const result = nth(4, list) - - result // $ExpectType number | undefined - }) - it('curried', () => { - const result = nth(1)(list) +test('can be composed', () => { + const nestedList = [ 0, [ 10, 11, 12 ], 1, 2 ] + const composedLens = compose(lensIndex(1), lensIndex(0)) - result // $ExpectType number | undefined - }) + expect(view(composedLens, nestedList)).toBe(10) }) -describe('R.nth - string', () => { - const str = 'abc' - it('happy', () => { - const result = nth(4, str) +test('set s (get s) === s', () => { + expect(set( + lensIndex(0), view(lensIndex(0), testList), testList + )).toEqual(testList) +}) - result // $ExpectType string - }) - it('curried', () => { - const result = nth(1)(str) +test('get (set s v) === v', () => { + expect(view(lensIndex(0), set( + lensIndex(0), 0, testList + ))).toBe(0) +}) - result // $ExpectType string - }) +test('get (set(set s v1) v2) === v2', () => { + expect(view(lensIndex(0), + set( + lensIndex(0), 11, set( + lensIndex(0), 10, testList + ) + ))).toBe(11) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#nth) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lensIndex) -### objOf +### lensPath -```typescript +It returns a lens that focuses on specified `path`. -objOf(key: K, value: T): Record -``` +
Try this R.lensPath example in Rambda REPL -It creates an object with a single key-value pair. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lensPath) -```javascript -const result = R.objOf('foo', 'bar') -// => {foo: 'bar'} +### lensProp + +```typescript + +lensProp(prop: K): Lens ``` -Try this R.objOf example in Rambda REPL +It returns a lens that focuses on specified property `prop`. + +Try this R.lensProp example in Rambda REPL
All TypeScript definitions ```typescript -objOf(key: K, value: T): Record; -objOf(key: K): (value: T) => Record; +lensProp(prop: K): Lens; ```
-R.objOf source +R.lensProp source ```javascript -export function objOf(key, value){ - if (arguments.length === 1){ - return _value => objOf(key, _value) - } +import { assoc } from './assoc.js' +import { lens } from './lens.js' +import { prop } from './prop.js' - return { [ key ] : value } +export function lensProp(key){ + return lens(prop(key), assoc(key)) } ``` @@ -17934,221 +7457,201 @@ export function objOf(key, value){ Tests ```javascript -import { objOf as objOfRamda } from 'ramda' +import { compose } from './compose.js' +import { identity } from './identity.js' +import { inc } from './inc.js' +import { lensProp } from './lensProp.js' +import { over } from './over.js' +import { set } from './set.js' +import { view } from './view.js' -import { compareCombinations } from './_internals/testUtils.js' -import { objOf } from './objOf.js' +const testObj = { + a : 1, + b : 2, + c : 3, +} -test('happy', () => { - expect(objOf('foo', 42)).toEqual({ foo : 42 }) +test('focuses object the specified object property', () => { + expect(view(lensProp('a'), testObj)).toBe(1) }) -test('with bad inputs', () => { - expect(objOf(null, undefined)).toEqual({ null : undefined }) +test('returns undefined if the specified property does not exist', () => { + expect(view(lensProp('X'), testObj)).toBeUndefined() }) -test('curried', () => { - expect(objOf('foo')(42)).toEqual({ foo : 42 }) +test('sets the value of the object property specified', () => { + expect(set( + lensProp('a'), 0, testObj + )).toEqual({ + a : 0, + b : 2, + c : 3, + }) }) -describe('brute force', () => { - const possibleInputs = [ 0, 1, null, undefined, [], {} ] - - compareCombinations({ - firstInput : possibleInputs, - secondInput : possibleInputs, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 0, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 0, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 36, - } - `) - }, - fn : objOf, - fnRamda : objOfRamda, +test('adds the property to the object if it doesn\'t exist', () => { + expect(set( + lensProp('d'), 4, testObj + )).toEqual({ + a : 1, + b : 2, + c : 3, + d : 4, }) }) -``` - -
- -
-TypeScript test +test('applies function to the value of the specified object property', () => { + expect(over( + lensProp('a'), inc, testObj + )).toEqual({ + a : 2, + b : 2, + c : 3, + }) +}) -```typescript -import {objOf} from 'rambda' +test('applies function to undefined and adds the property if it doesn\'t exist', () => { + expect(over( + lensProp('X'), identity, testObj + )).toEqual({ + a : 1, + b : 2, + c : 3, + X : undefined, + }) +}) -const key = 'foo' -const value = 42 +test('can be composed', () => { + const nestedObj = { + a : { b : 1 }, + c : 2, + } + const composedLens = compose(lensProp('a'), lensProp('b')) -describe('R.objOf', () => { - it('happy', () => { - const result = objOf(key, value) + expect(view(composedLens, nestedObj)).toBe(1) +}) - result.foo // $ExpectType number +test('set s (get s) === s', () => { + expect(set( + lensProp('a'), view(lensProp('a'), testObj), testObj + )).toEqual(testObj) +}) - // @ts-expect-error - result.bar - }) - it('curried', () => { - const result = objOf(key)(value) +test('get (set s v) === v', () => { + expect(view(lensProp('a'), set( + lensProp('a'), 0, testObj + ))).toBe(0) +}) - result.foo // $ExpectType number - }) +test('get (set(set s v1) v2) === v2', () => { + expect(view(lensProp('a'), + set( + lensProp('a'), 11, set( + lensProp('a'), 10, testObj + ) + ))).toBe(11) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#objOf) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lensProp) -### of +### lt -```typescript +Try this R.lt example in Rambda REPL -of(x: T): T[] -``` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lt) -```javascript -R.of(null); // => [null] -R.of([42]); // => [[42]] -``` +### lte -Try this R.of example in Rambda REPL +Try this R.lte example in Rambda REPL -
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#lte) -All TypeScript definitions +### map ```typescript -of(x: T): T[]; -``` - -
-
- -R.of source - -```javascript -export function of(value){ - return [ value ] -} +map(fn: ObjectIterator, iterable: Dictionary): Dictionary ``` -
- -
- -Tests - -```javascript -import { of } from './of.js' - -test('happy', () => { - expect(of(3)).toEqual([ 3 ]) +It returns the result of looping through `iterable` with `fn`. - expect(of(null)).toEqual([ null ]) -}) -``` +It works with both array and object. -
+Try this R.map example in Rambda REPL
-TypeScript test +All TypeScript definitions ```typescript -import {of} from 'rambda' - -const list = [1, 2, 3] - -describe('R.of', () => { - it('happy', () => { - const result = of(4) - - result // $ExpectType number[] - }) - it('curried', () => { - const result = of(list) - - result // $ExpectType number[][] - }) -}) +map(fn: ObjectIterator, iterable: Dictionary): Dictionary; +map(fn: Iterator, iterable: T[]): U[]; +map(fn: Iterator): (iterable: T[]) => U[]; +map(fn: ObjectIterator): (iterable: Dictionary) => Dictionary; +map(fn: Iterator): (iterable: T[]) => T[]; +map(fn: Iterator, iterable: T[]): T[]; ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#of) - -### omit - -```typescript - -omit(propsToOmit: K[], obj: T): Omit -``` - -It returns a partial copy of an `obj` without `propsToOmit` properties. +
-> :boom: When using this method with `TypeScript`, it is much easier to pass `propsToOmit` as an array. If passing a string, you will need to explicitly declare the output type. +R.map source ```javascript -const obj = {a: 1, b: 2, c: 3} -const propsToOmit = 'a,c,d' -const propsToOmitList = ['a', 'c', 'd'] - -const result = [ - R.omit(propsToOmit, Record), - R.omit(propsToOmitList, Record) -] -// => [{b: 2}, {b: 2}] -``` - -Try this R.omit example in Rambda REPL - -
- -All TypeScript definitions +import { INCORRECT_ITERABLE_INPUT } from './_internals/constants.js' +import { isArray } from './_internals/isArray.js' +import { keys } from './_internals/keys.js' -```typescript -omit(propsToOmit: K[], obj: T): Omit; -omit(propsToOmit: K[]): (obj: T) => Omit; -omit(propsToOmit: string, obj: T): U; -omit(propsToOmit: string): (obj: T) => U; -omit(propsToOmit: string, obj: object): T; -omit(propsToOmit: string): (obj: object) => T; -``` +export function mapArray( + fn, list, isIndexed = false +){ + let index = 0 + const willReturn = Array(list.length) -
+ while (index < list.length){ + willReturn[ index ] = isIndexed ? fn(list[ index ], index) : fn(list[ index ]) -
+ index++ + } -R.omit source + return willReturn +} -```javascript -import { createPath } from './_internals/createPath.js' -import { includes } from './_internals/includes.js' +export function mapObject(fn, obj){ + if (arguments.length === 1){ + return _obj => mapObject(fn, _obj) + } + let index = 0 + const objKeys = keys(obj) + const len = objKeys.length + const willReturn = {} -export function omit(propsToOmit, obj){ - if (arguments.length === 1) return _obj => omit(propsToOmit, _obj) + while (index < len){ + const key = objKeys[ index ] + willReturn[ key ] = fn( + obj[ key ], key, obj + ) + index++ + } - if (obj === null || obj === undefined) - return undefined + return willReturn +} - const propsToOmitValue = createPath(propsToOmit, ',') - const willReturn = {} +export const mapObjIndexed = mapObject - for (const key in obj) - if (!includes(key, propsToOmitValue)) - willReturn[ key ] = obj[ key ] +export function map(fn, iterable){ + if (arguments.length === 1) return _iterable => map(fn, _iterable) + if (!iterable){ + throw new Error(INCORRECT_ITERABLE_INPUT) + } - return willReturn + if (isArray(iterable)) return mapArray(fn, iterable) + + return mapObject(fn, iterable) } ``` @@ -18159,41 +7662,62 @@ export function omit(propsToOmit, obj){ Tests ```javascript -import { omit } from './omit.js' +import { map as mapRamda } from 'ramda' -test('with string as condition', () => { - const obj = { - a : 1, - b : 2, - c : 3, - } - const result = omit('a,c', obj) - const resultCurry = omit('a,c')(obj) - const expectedResult = { b : 2 } +import { map } from './map.js' - expect(result).toEqual(expectedResult) - expect(resultCurry).toEqual(expectedResult) +const double = x => x * 2 + +describe('with array', () => { + it('happy', () => { + expect(map(double, [ 1, 2, 3 ])).toEqual([ 2, 4, 6 ]) + }) + + it('curried', () => { + expect(map(double)([ 1, 2, 3 ])).toEqual([ 2, 4, 6 ]) + }) }) -test.only('with number as property to omit', () => { +describe('with object', () => { const obj = { - 1 : 1, + a : 1, b : 2, } - const result = omit([ 1 ], obj) - expect(result).toEqual({ b : 2 }) -}) -test('with null', () => { - expect(omit('a,b', null)).toBeUndefined() + it('happy', () => { + expect(map(double, obj)).toEqual({ + a : 2, + b : 4, + }) + }) + + it('property as second and input object as third argument', () => { + const obj = { + a : 1, + b : 2, + } + const iterator = ( + val, prop, inputObject + ) => { + expect(prop).toBeString() + expect(inputObject).toEqual(obj) + + return val * 2 + } + + expect(map(iterator)(obj)).toEqual({ + a : 2, + b : 4, + }) + }) }) -test('happy', () => { - expect(omit([ 'a', 'c' ])({ - a : 'foo', - b : 'bar', - c : 'baz', - })).toEqual({ b : 'bar' }) +test('bad inputs difference between Ramda and Rambda', () => { + expect(() => map(double, null)).toThrowErrorMatchingInlineSnapshot('"Incorrect iterable input"') + expect(() => map(double)(undefined)).toThrowErrorMatchingInlineSnapshot('"Incorrect iterable input"') + expect(() => mapRamda(double, null)).toThrowErrorMatchingInlineSnapshot('"Cannot read properties of null (reading \'fantasy-land/map\')"') + expect(() => + mapRamda(double, undefined)).toThrowErrorMatchingInlineSnapshot('"Cannot read properties of undefined (reading \'fantasy-land/map\')"') }) ``` @@ -18204,190 +7728,111 @@ test('happy', () => { TypeScript test ```typescript -import {omit} from 'rambda' - -describe('R.omit with array as props input', () => { - it('allow Typescript to infer object type', () => { - const input = {a: 'foo', b: 2, c: 3, d: 4} - const result = omit(['b,c'], input) - - result.a // $ExpectType string - result.d // $ExpectType number - - const curriedResult = omit(['a,c'], input) +import {map} from 'rambda' - curriedResult.a // $ExpectType string - curriedResult.d // $ExpectType number +describe('R.map with arrays', () => { + it('iterable returns the same type as the input', () => { + const result = map( + (x: number) => { + x // $ExpectType number + return x + 2 + }, + [1, 2, 3] + ) + result // $ExpectType number[] }) - - it('declare type of input object', () => { - interface Input { - a: string, - b: number, - c: number, - d: number, - } - const input: Input = {a: 'foo', b: 2, c: 3, d: 4} - const result = omit(['b,c'], input) - result // $ExpectType Omit - - result.a // $ExpectType string - result.d // $ExpectType number - - const curriedResult = omit(['a,c'], input) - - curriedResult.a // $ExpectType string - curriedResult.d // $ExpectType number + it('iterable returns the same type as the input - curried', () => { + const result = map((x: number) => { + x // $ExpectType number + return x + 2 + })([1, 2, 3]) + result // $ExpectType number[] + }) + it('iterable returns different type as the input', () => { + const result = map( + (x: number) => { + x // $ExpectType number + return String(x) + }, + [1, 2, 3] + ) + result // $ExpectType string[] }) }) -describe('R.omit with string as props input', () => { - interface Output { - b: number, - d: number, - } - - it('explicitly declare output', () => { - const result = omit('a,c', {a: 1, b: 2, c: 3, d: 4}) - result // $ExpectType Output - result.b // $ExpectType number - - const curriedResult = omit('a,c')({a: 1, b: 2, c: 3, d: 4}) - - curriedResult.b // $ExpectType number +describe('R.map with objects', () => { + it('iterable with all three arguments - curried', () => { + // It requires dummy third typing argument + // in order to identify compared to curry typings for arrays + // ============================================ + const result = map((a, b, c) => { + a // $ExpectType number + b // $ExpectType string + c // $ExpectType Dictionary + return `${a}` + })({a: 1, b: 2}) + result // $ExpectType Dictionary }) - - it('explicitly declare input and output', () => { - interface Input { - a: number, - b: number, - c: number, - d: number, - } - const result = omit('a,c', {a: 1, b: 2, c: 3, d: 4}) - result // $ExpectType Output - result.b // $ExpectType number - - const curriedResult = omit('a,c')({ - a: 1, - b: 2, - c: 3, - d: 4, - }) - - curriedResult.b // $ExpectType number + it('iterable with all three arguments', () => { + const result = map( + (a, b, c) => { + a // $ExpectType number + b // $ExpectType string + c // $ExpectType Dictionary + return `${a}` + }, + {a: 1, b: 2} + ) + result // $ExpectType Dictionary }) - - it('without passing type', () => { - const result = omit('a,c', {a: 1, b: 2, c: 3, d: 4}) - result // $ExpectType unknown + it('iterable with property argument', () => { + const result = map( + (a, b) => { + a // $ExpectType number + b // $ExpectType string + return `${a}` + }, + {a: 1, b: 2} + ) + result // $ExpectType Dictionary + }) + it('iterable with no property argument', () => { + const result = map( + a => { + a // $ExpectType number + return `${a}` + }, + {a: 1, b: 2} + ) + result // $ExpectType Dictionary }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#omit) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#map) -### on +### mapObjIndexed ```typescript -on(binaryFn: (a: U, b: U) => R, unaryFn: (value: T) => U, a: T, b: T): R +mapObjIndexed(fn: ObjectIterator, iterable: Dictionary): Dictionary ``` -It passes the two inputs through `unaryFn` and then the results are passed as inputs the the `binaryFn` to receive the final result(`binaryFn(unaryFn(FIRST_INPUT), unaryFn(SECOND_INPUT))`). - -This method is also known as P combinator. - -```javascript -const result = R.on((a, b) => a + b, R.prop('a'), {b:0, a:1}, {a:2}) -// => 3 -``` +It works the same way as `R.map` does for objects. It is added as Ramda also has this method. -Try this R.on example in Rambda REPL +Try this R.mapObjIndexed example in Rambda REPL
All TypeScript definitions ```typescript -on(binaryFn: (a: U, b: U) => R, unaryFn: (value: T) => U, a: T, b: T): R; -on(binaryFn: (a: U, b: U) => R, unaryFn: (value: T) => U, a: T): (b: T) => R; -on(binaryFn: (a: U, b: U) => R, unaryFn: (value: T) => U): { - (a: T, b: T): R; - (a: T): (b: T) => R; -}; -``` - -
- -
- -R.on source - -```javascript -export function on( - binaryFn, unaryFn, a, b -){ - if (arguments.length === 3){ - return _b => on( - binaryFn, unaryFn, a, _b - ) - } - if (arguments.length === 2){ - return (_a, _b) => on( - binaryFn, unaryFn, _a, _b - ) - } - - return binaryFn(unaryFn(a), unaryFn(b)) -} -``` - -
- -
- -Tests - -```javascript -import { on } from './on.js' - -const binaryFn = (a, b) => a === b -const unaryFn = x => x.a -const a = { - b : 0, - a : 1, -} -const b = { a : 1 } - -test('happy', () => { - expect(on( - binaryFn, unaryFn, a, b - )).toBeTrue() -}) - -test('return type is not limited to boolean', () => { - expect(on( - binaryFn, unaryFn, a, b - )).toBeTrue() -}) - -test('curried - last input', () => { - expect(on( - binaryFn, unaryFn, a - )(b)).toBeTrue() -}) - -test('curried - last two inputs', () => { - expect(on(binaryFn, unaryFn)(a, b)).toBeTrue() -}) - -test('not supported curried case', () => { - expect(() => - on(binaryFn, unaryFn)(a)(b)).toThrowErrorMatchingInlineSnapshot('"Cannot read properties of undefined (reading \'a\')"') -}) +mapObjIndexed(fn: ObjectIterator, iterable: Dictionary): Dictionary; +mapObjIndexed(fn: ObjectIterator, iterable: Dictionary): Dictionary; +mapObjIndexed(fn: ObjectIterator): (iterable: Dictionary) => Dictionary; +mapObjIndexed(fn: ObjectIterator): (iterable: Dictionary) => Dictionary; ```
@@ -18397,92 +7842,87 @@ test('not supported curried case', () => { TypeScript test ```typescript -import {on} from 'rambda' - -const binaryFn = (a: number, b: number) => a === b -const unaryFn = (x: {a: number}) => x.a -const a = { - b: 0, - a: 1, -} -const b = {a: 1} +import {mapObjIndexed} from 'rambda' -describe('R.on', () => { - it('with boolean as result', () => { - const result = on(binaryFn, unaryFn, a, b) +const obj = {a: 1, b: 2, c: 3} - result // $ExpectType boolean +describe('R.mapObjIndexed', () => { + it('without type transform', () => { + const result = mapObjIndexed((x, prop, obj) => { + x // $ExpectType number + prop // $ExpectType string + obj // $ExpectType Dictionary + return x + 2 + }, obj) + result // $ExpectType Dictionary }) - it('with number as result', () => { - const result = on((a: number, b: number) => a + b, unaryFn, a, b) - - result // $ExpectType number + it('without type transform - curried', () => { + const result = mapObjIndexed((x, prop, obj) => { + x // $ExpectType number + prop // $ExpectType string + obj // $ExpectType Dictionary + return x + 2 + })(obj) + result // $ExpectType Dictionary + }) + it('change of type', () => { + const result = mapObjIndexed((x, prop, obj) => { + x // $ExpectType number + prop // $ExpectType string + obj // $ExpectType Dictionary + return String(x + 2) + }, obj) + result // $ExpectType Dictionary + }) + it('change of type - curried', () => { + const result = mapObjIndexed((x, prop, obj) => { + x // $ExpectType number + prop // $ExpectType string + obj // $ExpectType Dictionary + return String(x + 2) + })(obj) + result // $ExpectType Dictionary }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#on) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mapObjIndexed) -### once +### match ```typescript -once(fn: T, context?: C): T +match(regExpression: RegExp, str: string): string[] ``` -It returns a function, which invokes only once `fn` function. - -```javascript -let result = 0 -const addOnce = R.once((x) => result = result + x) - -addOnce(1) -addOnce(1) -// => 1 -``` +Curried version of `String.prototype.match` which returns empty array, when there is no match. -Try this R.once example in Rambda REPL +Try this R.match example in Rambda REPL
All TypeScript definitions ```typescript -once(fn: T, context?: C): T; +match(regExpression: RegExp, str: string): string[]; +match(regExpression: RegExp): (str: string) => string[]; ```
-R.once source +R.match source ```javascript -import { curry } from './curry.js' - -function onceFn(fn, context){ - let result - - return function (){ - if (fn){ - result = fn.apply(context || this, arguments) - fn = null - } - - return result - } -} - -export function once(fn, context){ - if (arguments.length === 1){ - const wrap = onceFn(fn, context) +export function match(pattern, input){ + if (arguments.length === 1) return _input => match(pattern, _input) - return curry(wrap) - } + const willReturn = input.match(pattern) - return onceFn(fn, context) + return willReturn === null ? [] : willReturn } ``` @@ -18493,42 +7933,26 @@ export function once(fn, context){ Tests ```javascript -import { once } from './once.js' - -test('with counter', () => { - let counter = 0 - const runOnce = once(x => { - counter++ +import { equals } from './equals.js' +import { match } from './match.js' - return x + 2 - }) - expect(runOnce(1)).toBe(3) - runOnce(1) - runOnce(1) - runOnce(1) - expect(counter).toBe(1) +test('happy', () => { + expect(match(/a./g)('foo bar baz')).toEqual([ 'ar', 'az' ]) }) -test('happy path', () => { - const addOneOnce = once(( - a, b, c - ) => a + b + c, 1) - - expect(addOneOnce( - 10, 20, 30 - )).toBe(60) - expect(addOneOnce(40)).toBe(60) +test('fallback', () => { + expect(match(/a./g)('foo')).toEqual([]) }) -test('with context', () => { - const context = { name: 'fris' } - const getNameOnce = once(function (){ - return this.name - }, context) +test('with string', () => { + expect(match('a', 'foo')).toEqual([]) + expect(equals(match('o', 'foo'), [ 'o' ])).toBeTrue() +}) - expect(getNameOnce()).toBe('fris') - expect(getNameOnce()).toBe('fris') - expect(getNameOnce()).toBe('fris') +test('throwing', () => { + expect(() => { + match(/a./g, null) + }).toThrowErrorMatchingInlineSnapshot('"Cannot read properties of null (reading \'match\')"') }) ``` @@ -18539,69 +7963,80 @@ test('with context', () => { TypeScript test ```typescript -import {once} from 'rambda' +import {match} from 'rambda' -describe('R.once', () => { - it('happy', () => { - const runOnce = once((x: number) => { - return x + 2 - }) +const str = 'foo bar' - const result = runOnce(1) - result // $ExpectType number +describe('R.match', () => { + it('happy', () => { + const result = match(/foo/, str) + result // $ExpectType string[] }) - it('with context', () => { - const runOnce = once(function (this: any, x: number) { - return x + 2 - }) - - const result = runOnce.call({}, 1) - result // $ExpectType number + it('curried', () => { + const result = match(/foo/)(str) + result // $ExpectType string[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#once) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#match) -### or +### mathMod -```typescript +`R.mathMod` behaves like the modulo operator should mathematically, unlike the `%` operator (and by extension, `R.modulo`). So while `-17 % 5` is `-2`, `mathMod(-17, 5)` is `3`. -or(a: T, b: U): T | U -``` +Try this R.mathMod example in Rambda REPL -Logical OR +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mathMod) -```javascript -R.or(false, true); // => true -R.or(false, false); // => false -R.or(false, 'foo'); // => 'foo' +### max + +It returns the greater value between `x` and `y`. + +Try this R.max example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#max) + +### maxBy + +It returns the greater value between `x` and `y` according to `compareFn` function. + +Try this R.maxBy example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#maxBy) + +### mean + +```typescript + +mean(list: number[]): number ``` -Try this R.or example in Rambda REPL +It returns the mean value of `list` input. + +Try this R.mean example in Rambda REPL
All TypeScript definitions ```typescript -or(a: T, b: U): T | U; -or(a: T): (b: U) => T | U; +mean(list: number[]): number; ```
-R.or source +R.mean source ```javascript -export function or(a, b){ - if (arguments.length === 1) return _b => or(a, _b) +import { sum } from './sum.js' - return a || b +export function mean(list){ + return sum(list) / list.length } ``` @@ -18612,13 +8047,14 @@ export function or(a, b){ Tests ```javascript -import { or } from './or.js' +import { mean } from './mean.js' test('happy', () => { - expect(or(0, 'foo')).toBe('foo') - expect(or(true, true)).toBeTrue() - expect(or(false)(true)).toBeTrue() - expect(or(false, false)).toBeFalse() + expect(mean([ 2, 7 ])).toBe(4.5) +}) + +test('with NaN', () => { + expect(mean([])).toBeNaN() }) ``` @@ -18629,80 +8065,64 @@ test('happy', () => { TypeScript test ```typescript -import {or} from 'rambda' +import {mean} from 'rambda' -describe('R.or', () => { +describe('R.mean', () => { it('happy', () => { - const result = or(true, false) - result // $ExpectType boolean - }) - it('with falsy value as first input', () => { - const result = or('', false) - result // $ExpectType false | "" - }) - it('curried', () => { - const result = or(1)('foo') - result // $ExpectType number | "foo" + const result = mean([1, 2, 3]) + + result // $ExpectType number }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#or) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mean) -### over +### median ```typescript -over(lens: Lens): { - (fn: (a: A) => A): (value: S) => S +median(list: number[]): number ``` -It returns a copied **Object** or **Array** with modified value received by applying function `fn` to `lens` focus. - -```javascript -const headLens = R.lensIndex(0) - -R.over(headLens, R.toUpper, ['foo', 'bar', 'baz']) // => ['FOO', 'bar', 'baz'] -``` +It returns the median value of `list` input. -Try this R.over example in Rambda REPL +Try this R.median example in Rambda REPL
All TypeScript definitions ```typescript -over(lens: Lens): { - (fn: (a: A) => A): (value: S) => S; - (fn: (a: A) => A, value: S): S; -}; -over(lens: Lens, fn: (a: A) => A): (value: S) => S; -over(lens: Lens, fn: (a: A) => A, value: S): S; +median(list: number[]): number; ```
-R.over source +R.median source ```javascript -import { curry } from './curry.js' +import { mean } from './mean.js' -const Identity = x => ({ - x, - map : fn => Identity(fn(x)), -}) +export function median(list){ + const len = list.length + if (len === 0) return NaN + const width = 2 - len % 2 + const idx = (len - width) / 2 -function overFn( - lens, fn, object -){ - return lens(x => Identity(fn(x)))(object).x -} + return mean(Array.prototype.slice + .call(list, 0) + .sort((a, b) => { + if (a === b) return 0 -export const over = curry(overFn) + return a < b ? -1 : 1 + }) + .slice(idx, idx + width)) +} ```
@@ -18712,135 +8132,83 @@ export const over = curry(overFn) Tests ```javascript -import { assoc } from './assoc.js' -import { lens } from './lens.js' -import { lensIndex } from './lensIndex.js' -import { lensPath } from './lensPath.js' -import { over } from './over.js' -import { prop } from './prop.js' -import { toUpper } from './toUpper.js' - -const testObject = { - foo : 'bar', - baz : { - a : 'x', - b : 'y', - }, -} - -test('assoc lens', () => { - const assocLens = lens(prop('foo'), assoc('foo')) - const result = over( - assocLens, toUpper, testObject - ) - const expected = { - ...testObject, - foo : 'BAR', - } - expect(result).toEqual(expected) -}) +import { median } from './median.js' -test('path lens', () => { - const pathLens = lensPath('baz.a') - const result = over( - pathLens, toUpper, testObject - ) - const expected = { - ...testObject, - baz : { - a : 'X', - b : 'y', - }, - } - expect(result).toEqual(expected) +test('happy', () => { + expect(median([ 2 ])).toBe(2) + expect(median([ 7, 2, 10, 2, 9 ])).toBe(7) }) -test('index lens', () => { - const indexLens = lensIndex(0) - const result = over(indexLens, toUpper)([ 'foo', 'bar' ]) - expect(result).toEqual([ 'FOO', 'bar' ]) +test('with empty array', () => { + expect(median([])).toBeNaN() }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#over) +
-### partial +TypeScript test ```typescript +import {median} from 'rambda' -partial(fn: (x0: V0, x1: V1) => T, args: [V0]): (x1: V1) => T +describe('R.median', () => { + it('happy', () => { + const result = median([1, 2, 3]) + + result // $ExpectType number + }) +}) ``` -It is very similar to `R.curry`, but you can pass initial arguments when you create the curried function. +
-`R.partial` will keep returning a function until all the arguments that the function `fn` expects are passed. -The name comes from the fact that you partially inject the inputs. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#median) -> :boom: Rambda's partial doesn't need the input arguments to be wrapped as array. +### merge -```javascript -const fn = (title, firstName, lastName) => { - return title + ' ' + firstName + ' ' + lastName + '!' -} +Same as `R.mergeRight`. + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#merge) -const canPassAnyNumberOfArguments = R.partial(fn, 'Hello') -const ramdaStyle = R.partial(fn, ['Hello']) +### mergeAll -const finalFn = canPassAnyNumberOfArguments('Foo') +```typescript -finalFn('Bar') // => 'Hello, Foo Bar!' +mergeAll(list: object[]): T ``` -Try this R.partial example in Rambda REPL +It merges all objects of `list` array sequentially and returns the result. + +Try this R.mergeAll example in Rambda REPL
All TypeScript definitions ```typescript -partial(fn: (x0: V0, x1: V1) => T, args: [V0]): (x1: V1) => T; -partial(fn: (x0: V0, x1: V1, x2: V2) => T, args: [V0, V1]): (x2: V2) => T; -partial(fn: (x0: V0, x1: V1, x2: V2) => T, args: [V0]): (x1: V1, x2: V2) => T; -partial( - fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, - args: [V0, V1, V2], -): (x2: V3) => T; -partial( - fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, - args: [V0, V1], -): (x2: V2, x3: V3) => T; -partial( - fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, - args: [V0], -): (x1: V1, x2: V2, x3: V3) => T; -partial(fn: (...a: any[]) => T, args: any[]): (...a: any[]) => T; +mergeAll(list: object[]): T; +mergeAll(list: object[]): object; ```
-R.partial source +R.mergeAll source ```javascript -import { isArray } from './_internals/isArray.js' - -export function partial(fn, ...args){ - const len = fn.length - - // If a single array argument is given, those are the args (a la Ramda). - // Otherwise, the variadic arguments are the args. - const argList = args.length === 1 && isArray(args[0]) ? args[0] : args +import { map } from './map.js' +import { mergeRight } from './mergeRight.js' - return (...rest) => { - if (argList.length + rest.length >= len){ - return fn(...argList, ...rest) - } +export function mergeAll(arr){ + let willReturn = {} + map(val => { + willReturn = mergeRight(willReturn, val) + }, arr) - return partial(fn, ...[ ...argList, ...rest ]) - } + return willReturn } ``` @@ -18851,67 +8219,47 @@ export function partial(fn, ...args){ Tests ```javascript -import { partial } from './partial.js' -import { type } from './type.js' - -const greet = ( - salutation, title, firstName, lastName -) => - [salutation, title, firstName, lastName] - -test('happy', () => { - const canPassAnyNumberOfArguments = partial( - greet, 'Hello', 'Ms.' - ) - const fn = canPassAnyNumberOfArguments('foo') - const sayHello = partial(greet, [ 'Hello' ]) - const sayHelloRamda = partial(sayHello, [ 'Ms.' ]) - - expect(type(fn)).toBe('Function') - - expect(fn('bar')).toStrictEqual(['Hello', 'Ms.', 'foo', 'bar']) - expect(sayHelloRamda('foo', 'bar')).toStrictEqual(['Hello', 'Ms.', 'foo', 'bar']) -}) - -test('extra arguments are ignored', () => { - const canPassAnyNumberOfArguments = partial( - greet, 'Hello', 'Ms.' - ) - const fn = canPassAnyNumberOfArguments('foo') - - expect(type(fn)).toBe('Function') +import { mergeAll } from './mergeAll.js' - expect(fn( - 'bar', 1, 2 - )).toStrictEqual(['Hello', 'Ms.', 'foo', 'bar']) +test('case 1', () => { + const arr = [ { a : 1 }, { b : 2 }, { c : 3 } ] + const expectedResult = { + a : 1, + b : 2, + c : 3, + } + expect(mergeAll(arr)).toEqual(expectedResult) }) -test('when array is input', () => { - const fooFn = ( - a, b, c, d - ) => ({ - a, - b, - c, - d, +test('case 2', () => { + expect(mergeAll([ { foo : 1 }, { bar : 2 }, { baz : 3 } ])).toEqual({ + foo : 1, + bar : 2, + baz : 3, }) - const barFn = partial( - fooFn, [ 1, 2 ], [] - ) +}) - expect(barFn(1, 2)).toEqual({ - a : [ 1, 2 ], - b : [], - c : 1, - d : 2, +describe('acts as if nil values are simply empty objects', () => { + it('if the first object is nil', () => { + expect(mergeAll([ null, { foo : 1 }, { foo : 2 }, { bar : 2 } ])).toEqual({ + foo : 2, + bar : 2, + }) }) -}) -test('ramda spec', () => { - const sayHello = partial(greet, 'Hello') - const sayHelloToMs = partial(sayHello, 'Ms.') + it('if the last object is nil', () => { + expect(mergeAll([ { foo : 1 }, { foo : 2 }, { bar : 2 }, undefined ])).toEqual({ + foo : 2, + bar : 2, + }) + }) - expect(sayHelloToMs('Jane', 'Jones')).toStrictEqual(['Hello', 'Ms.', 'Jane', 'Jones']) + it('if an intermediate object is nil', () => { + expect(mergeAll([ { foo : 1 }, { foo : 2 }, null, { bar : 2 } ])).toEqual({ + foo : 2, + bar : 2, + }) + }) }) ``` @@ -18922,86 +8270,87 @@ test('ramda spec', () => { TypeScript test ```typescript -import {partial} from 'rambda' +import {mergeAll} from 'rambda' -describe('R.partial', () => { - it('happy', () => { - function fn( - aString: string, - aNumber: number, - aBoolean: boolean, - aNull: null - ) { - return {aString, aNumber, aBoolean, aNull} +describe('R.mergeAll', () => { + it('with passing type', () => { + interface Output { + foo: number, + bar: number, } + const result = mergeAll([{foo: 1}, {bar: 2}]) + result.foo // $ExpectType number + result.bar // $ExpectType number + }) - // @ts-expect-error - partial(fn, 1) - - const fn1 = partial(fn, ['a']) - partial(fn1, ['b']) - - const fn2 = partial(fn1, [2]) - const result = fn2(true, null) - result // $ExpectType { aString: string; aNumber: number; aBoolean: boolean; aNull: null; } + it('without passing type', () => { + const result = mergeAll([{foo: 1}, {bar: 2}]) + result // $ExpectType unknown }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#partial) - -### partialObject +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeAll) -```typescript +### mergeDeepLeft -partialObject( - fn: (input: Input) => Output, - partialInput: PartialInput, -): (input: Pick>) => Output -``` +Try this R.mergeDeepLeft example in Rambda REPL -`R.partialObject` is a curry helper designed specifically for functions accepting object as a single argument. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeDeepLeft) -Initially the function knows only a part from the whole input object and then `R.partialObject` helps in preparing the function for the second part, when it receives the rest of the input. +### mergeDeepRight -> :boom: Function input can be asynchronous +```typescript -```javascript -const fn = ({ a, b, c }) => a + b + c -const curried = R.partialObject(fn, { a : 1 }) -const result = curried({ - b : 2, - c : 3, -}) -// => 6 +mergeDeepRight(target: object, newProps: object): Output ``` -Try this R.partialObject example in Rambda REPL +Creates a new object with the own properties of the first object merged with the own properties of the second object. If a key exists in both objects: + + - and both values are objects, the two values will be recursively merged + - otherwise the value from the second object will be used.
All TypeScript definitions ```typescript -partialObject( - fn: (input: Input) => Output, - partialInput: PartialInput, -): (input: Pick>) => Output; +mergeDeepRight(target: object, newProps: object): Output; +mergeDeepRight(target: object): (newProps: object) => Output; ```
-R.partialObject source +R.mergeDeepRight source ```javascript -import { mergeDeepRight } from './mergeDeepRight.js' +import { clone } from './clone.js' +import { type } from './type.js' -export function partialObject(fn, input){ - return nextInput => fn(mergeDeepRight(nextInput, input)) +export function mergeDeepRight(target, source){ + if (arguments.length === 1){ + return sourceHolder => mergeDeepRight(target, sourceHolder) + } + + const willReturn = clone(target) + + Object.keys(source).forEach(key => { + if (type(source[ key ]) === 'Object'){ + if (type(target[ key ]) === 'Object'){ + willReturn[ key ] = mergeDeepRight(target[ key ], source[ key ]) + } else { + willReturn[ key ] = source[ key ] + } + } else { + willReturn[ key ] = source[ key ] + } + }) + + return willReturn } ``` @@ -19012,69 +8361,132 @@ export function partialObject(fn, input){ Tests ```javascript -import { delay } from './delay.js' -import { partialObject } from './partialObject.js' -import { type } from './type.js' +import { mergeDeepRight } from './mergeDeepRight.js' -test('with plain function', () => { - const fn = ({ a, b, c }) => a + b + c - const curried = partialObject(fn, { a : 1 }) +const student = { + name : 'foo', + age : 10, + contact : { + a : 1, + email : 'foo@example.com', + }, +} +const teacher = { + age : 40, + contact : { email : 'baz@example.com' }, + songs : { title : 'Remains the same' }, +} - expect(type(curried)).toBe('Function') - expect(curried({ - b : 2, - c : 3, - })).toBe(6) +test('when merging object with lists inside them', () => { + const a = { + a : [ 1, 2, 3 ], + b : [ 4, 5, 6 ], + } + const b = { + a : [ 7, 8, 9 ], + b : [ 10, 11, 12 ], + } + const result = mergeDeepRight(a, b) + const expected = { + a : [ 7, 8, 9 ], + b : [ 10, 11, 12 ], + } + expect(result).toEqual(expected) }) -test('with function that throws an error', () => { - const fn = ({ a, b, c }) => { - throw new Error('foo') +test('happy', () => { + const result = mergeDeepRight(student, teacher) + const curryResult = mergeDeepRight(student)(teacher) + const expected = { + age : 40, + name : 'foo', + contact : { + a : 1, + email : 'baz@example.com', + }, + songs : { title : 'Remains the same' }, } - const curried = partialObject(fn, { a : 1 }) - expect(type(curried)).toBe('Function') - expect(() => - curried({ - b : 2, - c : 3, - })).toThrowErrorMatchingInlineSnapshot('"foo"') + expect(result).toEqual(expected) + expect(curryResult).toEqual(expected) }) -test('with async', async () => { - const fn = async ({ a, b, c }) => { - await delay(100) +test('issue 650', () => { + expect(Object.keys(mergeDeepRight({ a : () => {} }, { b : () => {} }))).toEqual([ + 'a', + 'b', + ]) +}) - return a + b + c +test('ramda compatible test 1', () => { + const a = { + w : 1, + x : 2, + y : { z : 3 }, + } + const b = { + a : 4, + b : 5, + c : { d : 6 }, + } + const result = mergeDeepRight(a, b) + const expected = { + w : 1, + x : 2, + y : { z : 3 }, + a : 4, + b : 5, + c : { d : 6 }, } - const curried = partialObject(fn, { a : 1 }) - - const result = await curried({ - b : 2, - c : 3, - }) - - expect(result).toBe(6) + expect(result).toEqual(expected) }) -test('async function throwing an error', async () => { - const fn = async ({ a, b, c }) => { - await delay(100) - throw new Error('foo') +test('ramda compatible test 2', () => { + const a = { + a : { + b : 1, + c : 2, + }, + y : 0, + } + const b = { + a : { + b : 3, + d : 4, + }, + z : 0, + } + const result = mergeDeepRight(a, b) + const expected = { + a : { + b : 3, + c : 2, + d : 4, + }, + y : 0, + z : 0, } - const curried = partialObject(fn, { a : 1 }) + expect(result).toEqual(expected) +}) - try { - await curried({ - b : 2, - c : 3, - }) - expect(true).toBeFalsy() - } catch (e){ - expect(e.message).toBe('foo') +test('ramda compatible test 3', () => { + const a = { + w : 1, + x : { y : 2 }, + } + const result = mergeDeepRight(a, { x : { y : 3 } }) + const expected = { + w : 1, + x : { y : 3 }, } + expect(result).toEqual(expected) +}) + +test('functions are not discarded', () => { + const obj = { foo : () => {} } + expect(typeof mergeDeepRight(obj, {}).foo).toBe('function') }) ``` @@ -19085,148 +8497,57 @@ test('async function throwing an error', async () => { TypeScript test ```typescript -import {partialObject, delay} from 'rambda' +import {mergeDeepRight} from 'rambda' -describe('R.partialObject', () => { - it('happy', () => { - interface Input { - a: number, - b: number, - c: string, - } - const fn = ({a, b, c}: Input) => a + b + c - const curried = partialObject(fn, {a: 1}) - const result = curried({ - b: 2, - c: 'foo', - }) - result // $ExpectType string - }) - it('asynchronous', async() => { - interface Input { - a: number, - b: number, - c: string, - } - const fn = async({a, b, c}: Input) => { - await delay(100) - return a + b + c - } - const curried = partialObject(fn, {a: 1}) - const result = await curried({ - b: 2, - c: 'foo', - }) - result // $ExpectType string - }) +interface Output { + foo: { + bar: number, + }, +} + +describe('R.mergeDeepRight', () => { + const result = mergeDeepRight({foo: {bar: 1}}, {foo: {bar: 2}}) + result.foo.bar // $ExpectType number }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#partialObject) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeDeepRight) -### partition +### mergeLeft ```typescript -partition( - predicate: Predicate, - input: T[] -): [T[], T[]] +mergeLeft(newProps: object, target: object): Output ``` -It will return array of two objects/arrays according to `predicate` function. The first member holds all instances of `input` that pass the `predicate` function, while the second member - those who doesn't. - -```javascript -const list = [1, 2, 3] -const obj = {a: 1, b: 2, c: 3} -const predicate = x => x > 2 - -const result = [ - R.partition(predicate, list), - R.partition(predicate, Record) -] -const expected = [ - [[3], [1, 2]], - [{c: 3}, {a: 1, b: 2}], -] -// `result` is equal to `expected` -``` +Same as `R.merge`, but in opposite direction. -Try this R.partition example in Rambda REPL +Try this R.mergeLeft example in Rambda REPL
All TypeScript definitions ```typescript -partition( - predicate: Predicate, - input: T[] -): [T[], T[]]; -partition( - predicate: Predicate -): (input: T[]) => [T[], T[]]; -partition( - predicate: (x: T, prop?: string) => boolean, - input: { [key: string]: T} -): [{ [key: string]: T}, { [key: string]: T}]; -partition( - predicate: (x: T, prop?: string) => boolean -): (input: { [key: string]: T}) => [{ [key: string]: T}, { [key: string]: T}]; +mergeLeft(newProps: object, target: object): Output; +mergeLeft(newProps: object): (target: object) => Output; ```
-R.partition source +R.mergeLeft source ```javascript -import { isArray } from './_internals/isArray.js' - -export function partitionObject(predicate, iterable){ - const yes = {} - const no = {} - Object.entries(iterable).forEach(([ prop, value ]) => { - if (predicate(value, prop)){ - yes[ prop ] = value - } else { - no[ prop ] = value - } - }) - - return [ yes, no ] -} - -export function partitionArray( - predicate, list, indexed = false -){ - const yes = [] - const no = [] - let counter = -1 - - while (counter++ < list.length - 1){ - if ( - indexed ? predicate(list[ counter ], counter) : predicate(list[ counter ]) - ){ - yes.push(list[ counter ]) - } else { - no.push(list[ counter ]) - } - } - - return [ yes, no ] -} +import { mergeRight } from './mergeRight.js' -export function partition(predicate, iterable){ - if (arguments.length === 1){ - return listHolder => partition(predicate, listHolder) - } - if (!isArray(iterable)) return partitionObject(predicate, iterable) +export function mergeLeft(x, y){ + if (arguments.length === 1) return _y => mergeLeft(x, _y) - return partitionArray(predicate, iterable) + return mergeRight(y, x) } ``` @@ -19237,70 +8558,33 @@ export function partition(predicate, iterable){ Tests ```javascript -import { partition } from './partition.js' - -test('with array', () => { - const predicate = x => x > 2 - const list = [ 1, 2, 3, 4 ] +import { mergeLeft } from './mergeLeft.js' - const result = partition(predicate, list) - const expectedResult = [ - [ 3, 4 ], - [ 1, 2 ], - ] +const obj = { + foo : 1, + bar : 2, +} - expect(result).toEqual(expectedResult) +test('happy', () => { + expect(mergeLeft({ bar : 20 }, obj)).toEqual({ + foo : 1, + bar : 20, + }) }) -test('with object', () => { - const predicate = (value, prop) => { - expect(typeof prop).toBe('string') - - return value > 2 - } - const hash = { - a : 1, - b : 2, - c : 3, - d : 4, - } - - const result = partition(predicate)(hash) - const expectedResult = [ - { - c : 3, - d : 4, - }, - { - a : 1, - b : 2, - }, - ] - - expect(result).toEqual(expectedResult) +test('curry', () => { + expect(mergeLeft({ baz : 3 })(obj)).toEqual({ + foo : 1, + bar : 2, + baz : 3, + }) }) -test('readme example', () => { - const list = [ 1, 2, 3 ] - const obj = { - a : 1, - b : 2, - c : 3, - } - const predicate = x => x > 2 - - const result = [ partition(predicate, list), partition(predicate, obj) ] - const expected = [ - [ [ 3 ], [ 1, 2 ] ], - [ - { c : 3 }, - { - a : 1, - b : 2, - }, - ], - ] - expect(result).toEqual(expected) +test('when undefined or null instead of object', () => { + expect(mergeLeft(null, undefined)).toEqual({}) + expect(mergeLeft(obj, null)).toEqual(obj) + expect(mergeLeft(obj, undefined)).toEqual(obj) + expect(mergeLeft(undefined, obj)).toEqual(obj) }) ``` @@ -19310,160 +8594,92 @@ test('readme example', () => { TypeScript test -```typescript -import {partition} from 'rambda' - -describe('R.partition', () => { - it('with array', () => { - const predicate = (x: number) => { - return x > 2 - } - const list = [1, 2, 3, 4] - - const result = partition(predicate, list) - const curriedResult = partition(predicate)(list) - result // $ExpectType [number[], number[]] - curriedResult // $ExpectType [number[], number[]] - }) - - /* - revert to old version of `dtslint` and `R.partition` typing - as there is diff between VSCode types(correct) and dtslint(incorrect) - - it('with object', () => { - const predicate = (value: number, prop?: string) => { - return value > 2 - } - const hash = { - a: 1, - b: 2, - c: 3, - d: 4, - } - - const result = partition(predicate, hash) - const curriedResult = partition(predicate)(hash) - result[0] // $xExpectType { [key: string]: number; } - result[1] // $xExpectType { [key: string]: number; } - curriedResult[0] // $xExpectType { [key: string]: number; } - curriedResult[1] // $xExpectType { [key: string]: number; } - }) - */ +```typescript +import {mergeLeft} from 'rambda' + +interface Output { + foo: number, + bar: number, +} + +describe('R.mergeLeft', () => { + const result = mergeLeft({foo: 1}, {bar: 2}) + const curriedResult = mergeLeft({foo: 1})({bar: 2}) + + result.foo // $ExpectType number + result.bar // $ExpectType number + curriedResult.bar // $ExpectType number }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#partition) - -### path +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeLeft) -```typescript +### mergeRight -path(path: [K0], obj: S): S[K0] -``` +It creates a copy of `target` object with overwritten `newProps` properties. Previously known as `R.merge` but renamed after Ramda did the same. -If `pathToSearch` is `'a.b'` then it will return `1` if `obj` is `{a:{b:1}}`. +Try this R.mergeRight example in Rambda REPL -It will return `undefined`, if such path is not found. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeRight) -> :boom: String annotation of `pathToSearch` is one of the differences between `Rambda` and `Ramda`. +### mergeWith -```javascript -const obj = {a: {b: 1}} -const pathToSearch = 'a.b' -const pathToSearchList = ['a', 'b'] +```typescript -const result = [ - R.path(pathToSearch, obj), - R.path(pathToSearchList, obj), - R.path('a.b.c.d', obj) -] -// => [1, 1, undefined] +mergeWith(fn: (x: any, z: any) => any, a: Record, b: Record): Record ``` -Try this R.path example in Rambda REPL +It takes two objects and a function, which will be used when there is an overlap between the keys. + +Try this R.mergeWith example in Rambda REPL
All TypeScript definitions ```typescript -path(path: [K0], obj: S): S[K0]; -path(path: [K0, K1], obj: S): S[K0][K1]; -path< - S, - K0 extends keyof S = keyof S, - K1 extends keyof S[K0] = keyof S[K0], - K2 extends keyof S[K0][K1] = keyof S[K0][K1] ->(path: [K0, K1, K2], obj: S): S[K0][K1][K2]; -path< - S, - K0 extends keyof S = keyof S, - K1 extends keyof S[K0] = keyof S[K0], - K2 extends keyof S[K0][K1] = keyof S[K0][K1], - K3 extends keyof S[K0][K1][K2] = keyof S[K0][K1][K2], ->(path: [K0, K1, K2, K3], obj: S): S[K0][K1][K2][K3]; -path< - S, - K0 extends keyof S = keyof S, - K1 extends keyof S[K0] = keyof S[K0], - K2 extends keyof S[K0][K1] = keyof S[K0][K1], - K3 extends keyof S[K0][K1][K2] = keyof S[K0][K1][K2], - K4 extends keyof S[K0][K1][K2][K3] = keyof S[K0][K1][K2][K3], ->(path: [K0, K1, K2, K3, K4], obj: S): S[K0][K1][K2][K3][K4]; -path< - S, - K0 extends keyof S = keyof S, - K1 extends keyof S[K0] = keyof S[K0], - K2 extends keyof S[K0][K1] = keyof S[K0][K1], - K3 extends keyof S[K0][K1][K2] = keyof S[K0][K1][K2], - K4 extends keyof S[K0][K1][K2][K3] = keyof S[K0][K1][K2][K3], - K5 extends keyof S[K0][K1][K2][K3][K4] = keyof S[K0][K1][K2][K3][K4], ->(path: [K0, K1, K2, K3, K4, K5], obj: S): S[K0][K1][K2][K3][K4][K5]; -path(pathToSearch: string, obj: any): T | undefined; -path(pathToSearch: string): (obj: any) => T | undefined; -path(pathToSearch: RamdaPath): (obj: any) => T | undefined; -path(pathToSearch: RamdaPath, obj: any): T | undefined; +mergeWith(fn: (x: any, z: any) => any, a: Record, b: Record): Record; +mergeWith(fn: (x: any, z: any) => any, a: Record, b: Record): Output; +mergeWith(fn: (x: any, z: any) => any, a: Record): (b: Record) => Record; +mergeWith(fn: (x: any, z: any) => any, a: Record): (b: Record) => Output; +mergeWith(fn: (x: any, z: any) => any): (a: U, b: V) => Record; +mergeWith(fn: (x: any, z: any) => any): (a: U, b: V) => Output; ```
-R.path source +R.mergeWith source ```javascript -import { createPath } from './_internals/createPath.js' +import { curry } from './curry.js' -export function pathFn(pathInput, obj){ - let willReturn = obj - let counter = 0 +export function mergeWithFn( + mergeFn, aInput, bInput +){ + const a = aInput ?? {} + const b = bInput ?? {} + const willReturn = {} - const pathArrValue = createPath(pathInput) + Object.keys(a).forEach(key => { + if (b[ key ] === undefined) willReturn[ key ] = a[ key ] + else willReturn[ key ] = mergeFn(a[ key ], b[ key ]) + }) - while (counter < pathArrValue.length){ - if (willReturn === null || willReturn === undefined){ - return undefined - } - if (willReturn[ pathArrValue[ counter ] ] === null) return undefined + Object.keys(b).forEach(key => { + if (willReturn[ key ] !== undefined) return - willReturn = willReturn[ pathArrValue[ counter ] ] - counter++ - } + if (a[ key ] === undefined) willReturn[ key ] = b[ key ] + else willReturn[ key ] = mergeFn(a[ key ], b[ key ]) + }) return willReturn } -export function path(pathInput, obj){ - if (arguments.length === 1) return _obj => path(pathInput, _obj) - - if (obj === null || obj === undefined){ - return undefined - } - - return pathFn(pathInput, obj) -} +export const mergeWith = curry(mergeWithFn) ```
@@ -19473,41 +8689,60 @@ export function path(pathInput, obj){ Tests ```javascript -import { path } from './path.js' - -test('with array inside object', () => { - const obj = { a : { b : [ 1, { c : 1 } ] } } +import { concat } from './concat.js' +import { mergeWithFn } from './mergeWith.js' - expect(path('a.b.1.c', obj)).toBe(1) +test('happy', () => { + const result = mergeWithFn( + concat, + { + a : true, + values : [ 10, 20 ], + }, + { + b : true, + values : [ 15, 35 ], + } + ) + const expected = { + a : true, + b : true, + values : [ 10, 20, 15, 35 ], + } + expect(result).toEqual(expected) }) -test('works with undefined', () => { - const obj = { a : { b : { c : 1 } } } - - expect(path('a.b.c.d.f', obj)).toBeUndefined() - expect(path('foo.babaz', undefined)).toBeUndefined() - expect(path('foo.babaz')(undefined)).toBeUndefined() -}) +// https://github.com/ramda/ramda/pull/3222/files#diff-d925d9188b478d2f1d4b26012c6dddac374f9e9d7a336604d654b9a113bfc857 +describe('acts as if nil values are simply empty objects', () => { + it('if the first object is nil and the second empty', () => { + expect(mergeWithFn( + concat, undefined, {} + )).toEqual({}) + }) -test('works with string instead of array', () => { - expect(path('foo.bar.baz')({ foo : { bar : { baz : 'yes' } } })).toBe('yes') -}) + it('if the first object is empty and the second nil', () => { + expect(mergeWithFn( + concat, {}, null + )).toEqual({}) + }) -test('path', () => { - expect(path([ 'foo', 'bar', 'baz' ])({ foo : { bar : { baz : 'yes' } } })).toBe('yes') - expect(path([ 'foo', 'bar', 'baz' ])(null)).toBeUndefined() - expect(path([ 'foo', 'bar', 'baz' ])({ foo : { bar : 'baz' } })).toBeUndefined() -}) + it('if both objects are nil', () => { + expect(mergeWithFn( + concat, undefined, null + )).toEqual({}) + }) -test('with number string in between', () => { - expect(path(['a','1','b'], {a: [{b: 1}, {b: 2}]})).toBe(2) -}) + it('if the first object is not empty and the second is nil', () => { + expect(mergeWithFn( + concat, { a : 'a' }, null + )).toEqual({ a : 'a' }) + }) -test('null is not a valid path', () => { - expect(path('audio_tracks', { - a : 1, - audio_tracks : null, - })).toBeUndefined() + it('if the first object is nil and the second is not empty', () => { + expect(mergeWithFn( + concat, undefined, { a : 'a' } + )).toEqual({ a : 'a' }) + }) }) ``` @@ -19518,122 +8753,122 @@ test('null is not a valid path', () => { TypeScript test ```typescript -import {path} from 'rambda' +import {concat, mergeWith} from 'rambda' -const input = {a: {b: {c: true}}} +interface Output { + a: boolean, + b: boolean, + values: number[], +} +const A = { + a: true, + values: [10, 20], +} +const B = { + b: true, + values: [15, 35], +} -describe('R.path with string as path', () => { - it('without specified output type', () => { - // $ExpectType unknown - path('a.b.c', input) - // $ExpectType unknown - path('a.b.c')(input) - }) - it('with specified output type', () => { - // $ExpectType boolean | undefined - path('a.b.c', input) - // $ExpectType boolean | undefined - path('a.b.c')(input) +describe('R.mergeWith', () => { + test('no curry | without explicit types', () => { + const result = mergeWith(concat, A, B) + result // $ExpectType Record }) -}) - -describe('R.path with list as path', () => { - it('with array as path', () => { - // $ExpectType boolean - path(['a', 'b', 'c'], input) - // $ExpectType unknown - path(['a', 'b', 'c'])(input) + test('no curry | with explicit types', () => { + const result = mergeWith(concat, A, B) + result // $ExpectType Output }) - test('shallow property', () => { - // $ExpectType number - path(['a'], {a: 1}) - - // $ExpectType unknown - path(['b'], {a: 1}) + test('curry 1 | without explicit types', () => { + const result = mergeWith(concat, A)(B) + result // $ExpectType Record }) - test('deep property', () => { - const testObject = {a: {b: {c: {d: {e: {f: 1}}}}}} - const result = path(['a', 'b', 'c', 'd', 'e', 'f'], testObject) - // $ExpectType number - result - const curriedResult = path(['a', 'b', 'c', 'd', 'e', 'f'])(testObject) - // $ExpectType unknown - curriedResult + test('curry 1 | with explicit types', () => { + const result = mergeWith(concat, A)(B) + result // $ExpectType Output }) - test('issue #668 - path is not correct', () => { - const object = { - is: { - a: 'path', - }, - } - const result = path(['is', 'not', 'a'], object) - // $ExpectType unknown - result - const curriedResult = path(['is', 'not', 'a'])(object) - // $ExpectType unknown - curriedResult + test('curry 2 | without explicit types', () => { + const result = mergeWith(concat)(A, B) + result // $ExpectType Record + }) + test('curry 2 | with explicit types', () => { + const result = mergeWith(concat)(A, B) + result // $ExpectType Output }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#path) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#mergeWith) -### pathEq +### min -```typescript +It returns the lesser value between `x` and `y`. -pathEq(pathToSearch: Path, target: any, input: any): boolean -``` +Try this R.min example in Rambda REPL -It returns `true` if `pathToSearch` of `input` object is equal to `target` value. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#min) -`pathToSearch` is passed to `R.path`, which means that it can be either a string or an array. Also equality between `target` and the found value is determined by `R.equals`. +### minBy -```javascript -const path = 'a.b' -const target = {c: 1} -const input = {a: {b: {c: 1}}} +It returns the lesser value between `x` and `y` according to `compareFn` function. -const result = R.pathEq( - path, - target, - input -) -// => true +Try this R.minBy example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#minBy) + +### modify + +```typescript + +modify(prop: K, fn: (value: T) => T): >(object: U) => U ``` -Try this R.pathEq example in Rambda REPL +Try this R.modify example in Rambda REPL
All TypeScript definitions ```typescript -pathEq(pathToSearch: Path, target: any, input: any): boolean; -pathEq(pathToSearch: Path, target: any): (input: any) => boolean; -pathEq(pathToSearch: Path): (target: any) => (input: any) => boolean; +modify(prop: K, fn: (value: T) => T): >(object: U) => U; +modify(prop: K, fn: (value: U[K]) => U[K], object: U): U; +modify(prop: K): { + (fn: (value: T) => T): >(object: U) => U; + >(fn: (value: T) => T, object: U): U; +}; ```
-R.pathEq source +R.modify source ```javascript +import { isArray } from './_internals/isArray.js' +import { isIterable } from './_internals/isIterable.js' import { curry } from './curry.js' -import { equals } from './equals.js' -import { path } from './path.js' +import { updateFn } from './update.js' -function pathEqFn( - pathToSearch, target, input +function modifyFn( + property, fn, iterable ){ - return equals(path(pathToSearch, input), target) + if (!isIterable(iterable)) return iterable + if (iterable[ property ] === undefined) return iterable + if (isArray(iterable)){ + return updateFn( + property, fn(iterable[ property ]), iterable + ) + } + + return { + ...iterable, + [ property ] : fn(iterable[ property ]), + } } -export const pathEq = curry(pathEqFn) +export const modify = curry(modifyFn) ```
@@ -19643,34 +8878,95 @@ export const pathEq = curry(pathEqFn) Tests ```javascript -import { pathEq } from './pathEq.js' +import { modify as modifyRamda } from 'ramda' -test('when true', () => { - const path = 'a.b' - const obj = { a : { b : { c : 1 } } } - const target = { c : 1 } +import { compareCombinations, FALSY_VALUES } from './_internals/testUtils.js' +import { add } from './add.js' +import { compose } from './compose.js' +import { modify } from './modify.js' - expect(pathEq( - path, target, obj - )).toBeTrue() +const person = { + name : 'foo', + age : 20, +} + +test('happy', () => { + expect(modify( + 'age', x => x + 1, person + )).toEqual({ + name : 'foo', + age : 21, + }) }) -test('when false', () => { - const path = 'a.b' - const obj = { a : { b : 1 } } - const target = 2 +test('property is missing', () => { + expect(modify( + 'foo', x => x + 1, person + )).toEqual(person) +}) - expect(pathEq(path, target)(obj)).toBeFalse() +test('adjust if `array` at the given key with the `transformation` function', () => { + expect(modify( + 1, add(1), [ 100, 1400 ] + )).toEqual([ 100, 1401 ]) }) -test('when wrong path', () => { - const path = 'foo.bar' - const obj = { a : { b : 1 } } - const target = 2 +describe('ignores transformations if the input value is not Array and Object', () => { + ;[ 42, undefined, null, '' ].forEach(value => { + it(`${ value }`, () => { + expect(modify( + 'a', add(1), value + )).toEqual(value) + }) + }) +}) - expect(pathEq( - path, target, obj - )).toBeFalse() +const possibleProperties = [ ...FALSY_VALUES, 'foo', 0 ] +const possibleTransformers = [ + ...FALSY_VALUES, + add(1), + add('foo'), + compose, + String, +] +const possibleObjects = [ + ...FALSY_VALUES, + {}, + [ 1, 2, 3 ], + { + a : 1, + foo : 2, + }, + { + a : 1, + foo : [ 1 ], + }, + { + a : 1, + foo : 'bar', + }, +] + +describe('brute force', () => { + compareCombinations({ + fn : modify, + fnRamda : modifyRamda, + firstInput : possibleProperties, + secondInput : possibleTransformers, + thirdInput : possibleObjects, + callback : errorsCounters => { + expect(errorsCounters).toMatchInlineSnapshot(` + { + "ERRORS_MESSAGE_MISMATCH": 0, + "ERRORS_TYPE_MISMATCH": 0, + "RESULTS_MISMATCH": 0, + "SHOULD_NOT_THROW": 0, + "SHOULD_THROW": 0, + "TOTAL_TESTS": 630, + } + `) + }, + }) }) ``` @@ -19681,112 +8977,132 @@ test('when wrong path', () => { TypeScript test ```typescript -import {pathEq} from 'rambda' +import { add, identity, map, modify, pipe, toUpper } from 'rambda'; -describe('R.pathEq', () => { - it('with string path', () => { - const pathToSearch = 'a.b.c' - const input = {a: {b: {c: 1}}} - const target = {c: 1} +type Obj = { + foo: string; + bar: number; +}; + +describe('R.modify', () => { + it('ramda tests', () => { + const result1 = modify('foo', toUpper, {} as Obj); + result1; // $ExpectType Obj + + const result2 = modify('bar', add(1), {} as Obj); + result2; // $ExpectType Obj + + const result3 = modify('foo', toUpper)({} as Obj); + result3; // $ExpectType Obj + + const result4 = modify('bar', add(1))({} as Obj); + result4; // $ExpectType Obj + + const result5 = modify('foo')(toUpper)({} as Obj); + result5; // $ExpectType Obj + + const result6 = modify('bar')(add(1))({} as Obj); + result6; // $ExpectType Obj + + const result7 = modify('foo')(toUpper, {} as Obj); + result7; // $ExpectType Obj + + const result8 = modify('bar')(add(1), {} as Obj); + result8; // $ExpectType Obj + + const result9 = modify('foo', identity, {} as Obj); + result9; // $ExpectType Obj + + // @ts-expect-error + modify('foo', add(1), {} as Obj); + // @ts-expect-error + modify('bar', toUpper, {} as Obj); + + const f = pipe(map(modify('foo', toUpper))); + + f([] as Obj[]); // $ExpectType Obj[] + }); +}); +``` + +
+ +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#modify) + +### modifyPath + +It changes a property of object on the base of provided path and transformer function. + +Try this R.modifyPath example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#modifyPath) + +### modulo + +Curried version of `x%y`. + +Try this R.modulo example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#modulo) - const result = pathEq(pathToSearch, input, target) - const curriedResult = pathEq(pathToSearch, input, target) - result // $ExpectType boolean - curriedResult // $ExpectType boolean - }) +### move - it('with array path', () => { - const pathToSearch = ['a', 'b', 'c'] - const input = {a: {b: {c: 1}}} - const target = {c: 1} +It returns a copy of `list` with exchanged `fromIndex` and `toIndex` elements. - const result = pathEq(pathToSearch, input, target) - const curriedResult = pathEq(pathToSearch, input, target) - result // $ExpectType boolean - curriedResult // $ExpectType boolean - }) -}) +Try this R.move example in Rambda REPL -describe('with ramda specs', () => { - const testPath = ['x', 0, 'y'] - const testObj = { - x: [ - {y: 2, z: 3}, - {y: 4, z: 5}, - ], - } +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#move) - const result1 = pathEq(testPath, 2, testObj) - const result2 = pathEq(testPath, 2)(testObj) - const result3 = pathEq(testPath)(2)(testObj) - result1 // $ExpectType boolean - result2 // $ExpectType boolean - result3 // $ExpectType boolean -}) -``` +### multiply -
+Curried version of `x*y`. -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pathEq) +Try this R.multiply example in Rambda REPL -### pathOr +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#multiply) -```typescript +### negate -pathOr(defaultValue: T, pathToSearch: Path, obj: any): T -``` +Try this R.negate example in Rambda REPL -It reads `obj` input and returns either `R.path(pathToSearch, Record)` result or `defaultValue` input. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#negate) -```javascript -const defaultValue = 'DEFAULT_VALUE' -const pathToSearch = 'a.b' -const pathToSearchList = ['a', 'b'] +### none -const obj = { - a : { - b : 1 - } -} +```typescript -const result = [ - R.pathOr(DEFAULT_VALUE, pathToSearch, Record), - R.pathOr(DEFAULT_VALUE, pathToSearchList, Record), - R.pathOr(DEFAULT_VALUE, 'a.b.c', Record) -] -// => [1, 1, 'DEFAULT_VALUE'] +none(predicate: (x: T) => boolean, list: T[]): boolean ``` -Try this R.pathOr example in Rambda REPL +It returns `true`, if all members of array `list` returns `false`, when applied as argument to `predicate` function. + +Try this R.none example in Rambda REPL
All TypeScript definitions ```typescript -pathOr(defaultValue: T, pathToSearch: Path, obj: any): T; -pathOr(defaultValue: T, pathToSearch: Path): (obj: any) => T; -pathOr(defaultValue: T): (pathToSearch: Path) => (obj: any) => T; +none(predicate: (x: T) => boolean, list: T[]): boolean; +none(predicate: (x: T) => boolean): (list: T[]) => boolean; ```
-R.pathOr source +R.none source ```javascript -import { curry } from './curry.js' -import { defaultTo } from './defaultTo.js' -import { path } from './path.js' +export function none(predicate, list){ + if (arguments.length === 1) return _list => none(predicate, _list) -function pathOrFn( - defaultValue, pathInput, obj -){ - return defaultTo(defaultValue, path(pathInput, obj)) -} + for (let i = 0; i < list.length; i++){ + if (predicate(list[ i ])) return false + } -export const pathOr = curry(pathOrFn) + return true +} ```
@@ -19796,48 +9112,16 @@ export const pathOr = curry(pathOrFn) Tests ```javascript -import { pathOr } from './pathOr.js' - -test('with undefined', () => { - const result = pathOr( - 'foo', 'x.y', { x : { y : 1 } } - ) - - expect(result).toBe(1) -}) - -test('with null', () => { - const result = pathOr( - 'foo', 'x.y', null - ) - - expect(result).toBe('foo') -}) - -test('with NaN', () => { - const result = pathOr( - 'foo', 'x.y', NaN - ) - - expect(result).toBe('foo') -}) - -test('curry case (x)(y)(z)', () => { - const result = pathOr('foo')('x.y.z')({ x : { y : { a : 1 } } }) - - expect(result).toBe('foo') -}) +import { none } from './none.js' -test('curry case (x)(y,z)', () => { - const result = pathOr('foo', 'x.y.z')({ x : { y : { a : 1 } } }) +const isEven = n => n % 2 === 0 - expect(result).toBe('foo') +test('when true', () => { + expect(none(isEven, [ 1, 3, 5, 7 ])).toBeTrue() }) -test('curry case (x,y)(z)', () => { - const result = pathOr('foo')('x.y.z', { x : { y : { a : 1 } } }) - - expect(result).toBe('foo') +test('when false curried', () => { + expect(none(input => input > 1, [ 1, 2, 3 ])).toBeFalse() }) ``` @@ -19848,89 +9132,61 @@ test('curry case (x,y)(z)', () => { TypeScript test ```typescript -import {pathOr} from 'rambda' +import {none} from 'rambda' -describe('R.pathOr', () => { - it('with string path', () => { - const x = pathOr('foo', 'x.y', {x: {y: 'bar'}}) - x // $ExpectType string - }) - it('with array path', () => { - const x = pathOr('foo', ['x', 'y'], {x: {y: 'bar'}}) - x // $ExpectType string - }) - it('without passing type looks bad', () => { - const x = pathOr('foo', 'x.y', {x: {y: 'bar'}}) - x // $ExpectType "foo" +describe('R.none', () => { + it('happy', () => { + const result = none( + x => { + x // $ExpectType number + return x > 0 + }, + [1, 2, 3] + ) + result // $ExpectType boolean }) - it('curried', () => { - const x = pathOr('foo', 'x.y')({x: {y: 'bar'}}) - x // $ExpectType string + it('curried needs a type', () => { + const result = none(x => { + x // $ExpectType number + return x > 0 + })([1, 2, 3]) + result // $ExpectType boolean }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pathOr) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#none) -### paths +### not ```typescript -paths(pathsToSearch: Path[], obj: Input): (T | undefined)[] +not(input: any): boolean ``` -It loops over members of `pathsToSearch` as `singlePath` and returns the array produced by `R.path(singlePath, Record)`. - -Because it calls `R.path`, then `singlePath` can be either string or a list. - -```javascript -const obj = { - a : { - b : { - c : 1, - d : 2 - } - } -} - -const result = R.paths([ - 'a.b.c', - 'a.b.d', - 'a.b.c.d.e', -], Record) -// => [1, 2, undefined] -``` +It returns a boolean negated version of `input`. -Try this R.paths example in Rambda REPL +Try this R.not example in Rambda REPL
All TypeScript definitions ```typescript -paths(pathsToSearch: Path[], obj: Input): (T | undefined)[]; -paths(pathsToSearch: Path[]): (obj: Input) => (T | undefined)[]; -paths(pathsToSearch: Path[], obj: any): (T | undefined)[]; -paths(pathsToSearch: Path[]): (obj: any) => (T | undefined)[]; +not(input: any): boolean; ```
-R.paths source +R.not source ```javascript -import { path } from './path.js' - -export function paths(pathsToSearch, obj){ - if (arguments.length === 1){ - return _obj => paths(pathsToSearch, _obj) - } - - return pathsToSearch.map(singlePath => path(singlePath, obj)) +export function not(input){ + return !input } ``` @@ -19941,63 +9197,13 @@ export function paths(pathsToSearch, obj){ Tests ```javascript -import { paths } from './paths.js' - -const obj = { - a : { - b : { - c : 1, - d : 2, - }, - }, - p : [ { q : 3 } ], - x : { - y : 'FOO', - z : [ [ {} ] ], - }, -} - -test('with string path + curry', () => { - const pathsInput = [ 'a.b.d', 'p.q' ] - const expected = [ 2, undefined ] - const result = paths(pathsInput, obj) - const curriedResult = paths(pathsInput)(obj) - - expect(result).toEqual(expected) - expect(curriedResult).toEqual(expected) -}) - -test('with array path', () => { - const result = paths([ - [ 'a', 'b', 'c' ], - [ 'x', 'y' ], - ], - obj) - - expect(result).toEqual([ 1, 'FOO' ]) -}) - -test('takes a paths that contains indices into arrays', () => { - expect(paths([ - [ 'p', 0, 'q' ], - [ 'x', 'z', 0, 0 ], - ], - obj)).toEqual([ 3, {} ]) - expect(paths([ - [ 'p', 0, 'q' ], - [ 'x', 'z', 2, 1 ], - ], - obj)).toEqual([ 3, undefined ]) -}) - -test('gets a deep property\'s value from objects', () => { - expect(paths([ [ 'a', 'b' ] ], obj)).toEqual([ obj.a.b ]) - expect(paths([ [ 'p', 0 ] ], obj)).toEqual([ obj.p[ 0 ] ]) -}) +import { not } from './not.js' -test('returns undefined for items not found', () => { - expect(paths([ [ 'a', 'x', 'y' ] ], obj)).toEqual([ undefined ]) - expect(paths([ [ 'p', 2 ] ], obj)).toEqual([ undefined ]) +test('not', () => { + expect(not(false)).toBeTrue() + expect(not(true)).toBeFalse() + expect(not(0)).toBeTrue() + expect(not(1)).toBeFalse() }) ``` @@ -20008,86 +9214,61 @@ test('returns undefined for items not found', () => { TypeScript test ```typescript -import {paths} from 'rambda' - -interface Input { - a: number, - b: number, - c: number, -} - -const input: Input = {a: 1, b: 2, c: 3} - -describe('R.paths', () => { - it('with dot notation', () => { - const result = paths(['a.b.c', 'foo.bar'], input) - result // $ExpectType (number | undefined)[] - }) - - it('without type', () => { - const result = paths(['a.b.c', 'foo.bar'], input) - result // $ExpectType unknown[] - }) +import {not} from 'rambda' - it('with array as path', () => { - const result = paths([['a', 'b', 'c'], ['foo.bar']], input) - result // $ExpectType (number | undefined)[] - }) +describe('R.not', () => { + it('happy', () => { + const result = not(4) - it('curried', () => { - const result = paths([['a', 'b', 'c'], ['foo.bar']])(input) - result // $ExpectType (number | undefined)[] + result // $ExpectType boolean }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#paths) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#not) -### pathSatisfies +### nth ```typescript -pathSatisfies(pred: (val: T) => boolean, path: Path): (obj: U) => boolean +nth(index: number, input: string): string ``` -```javascript -const result = R.pathSatisfies( - x => x > 0, - ['a', 'b', 'c'], - {a: {b: {c: 1}}} -) -// => true -``` +Curried version of `input[index]`. -Try this R.pathSatisfies example in Rambda REPL +Try this R.nth example in Rambda REPL
All TypeScript definitions ```typescript -pathSatisfies(pred: (val: T) => boolean, path: Path): (obj: U) => boolean; -pathSatisfies(pred: (val: T) => boolean, path: Path, obj: U): boolean; +nth(index: number, input: string): string; +nth(index: number, input: T[]): T | undefined; +nth(n: number): { + (input: T[]): T | undefined; + (input: string): string; +}; ```
-R.pathSatisfies source +R.nth source ```javascript -import { path } from "./path.js"; -import { curry } from "./curry.js"; +export function nth(index, input){ + if (arguments.length === 1) return _input => nth(index, _input) -export function pathSatisfiesFn(fn, pathInput, obj) { - if(pathInput.length === 0) throw new Error("R.pathSatisfies received an empty path") - return Boolean(fn(path(pathInput, obj))) -} + const idx = index < 0 ? input.length + index : index -export const pathSatisfies = curry(pathSatisfiesFn) + return Object.prototype.toString.call(input) === '[object String]' ? + input.charAt(idx) : + input[ idx ] +} ```
@@ -20097,124 +9278,106 @@ export const pathSatisfies = curry(pathSatisfiesFn) Tests ```javascript -import { pathSatisfies } from './pathSatisfies'; +import { nth } from './nth.js' -const isPositive = (n) => n > 0; +test('happy', () => { + expect(nth(2, [ 1, 2, 3, 4 ])).toBe(3) +}) -it('returns true if the specified object path satisfies the given predicate', () => { - expect( - pathSatisfies(isPositive, ['x', 1, 'y'], { x: [{ y: -1 }, { y: 1 }] }), - ).toBe(true); - expect( - pathSatisfies(isPositive, 'x.1.y', { x: [{ y: -1 }, { y: 1 }] }), - ).toBe(true); -}); +test('with curry', () => { + expect(nth(2)([ 1, 2, 3, 4 ])).toBe(3) +}) -it('returns false if the specified path does not exist', () => { - expect(pathSatisfies(isPositive, ['x', 'y'], { x: { z: 42 } })).toBe(false); - expect(pathSatisfies(isPositive, 'x.y', { x: { z: 42 } })).toBe(false); -}); +test('with string and correct index', () => { + expect(nth(2)('foo')).toBe('o') +}) -it('throws on empty paths', () => { - expect(() => pathSatisfies(Object.is, [], { x: { z: 42 } })).toThrow(); -}); +test('with string and invalid index', () => { + expect(nth(20)('foo')).toBe('') +}) -it('returns false otherwise', () => { - expect(pathSatisfies(isPositive, ['x', 'y'], { x: { y: 0 } })).toBe(false); -}); +test('with negative index', () => { + expect(nth(-3)([ 1, 2, 3, 4 ])).toBe(2) +}) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pathSatisfies) +
-### pick +TypeScript test ```typescript +import {nth} from 'rambda' -pick(propsToPick: K[], input: T): Pick>> +const list = [1, 2, 3] + +describe('R.nth', () => { + it('happy', () => { + const result = nth(4, list) + + result // $ExpectType number | undefined + }) + it('curried', () => { + const result = nth(1)(list) + + result // $ExpectType number | undefined + }) +}) + +describe('R.nth - string', () => { + const str = 'abc' + it('happy', () => { + const result = nth(4, str) + + result // $ExpectType string + }) + it('curried', () => { + const result = nth(1)(str) + + result // $ExpectType string + }) +}) ``` -It returns a partial copy of an `input` containing only `propsToPick` properties. +
-`input` can be either an object or an array. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#nth) -String annotation of `propsToPick` is one of the differences between `Rambda` and `Ramda`. +### objOf + +It creates an object with a single key-value pair. -> :boom: When using this method with `TypeScript`, it is much easier to pass `propsToPick` as an array. If passing a string, you will need to explicitly declare the output type. +Try this R.objOf example in Rambda REPL -```javascript -const obj = { - a : 1, - b : false, - foo: 'cherry' -} -const list = [1, 2, 3, 4] -const propsToPick = 'a,foo' -const propsToPickList = ['a', 'foo'] - -const result = [ - R.pick(propsToPick, Record), - R.pick(propsToPickList, Record), - R.pick('a,bar', Record), - R.pick('bar', Record), - R.pick([0, 3, 5], list), - R.pick('0,3,5', list), -] +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#objOf) -const expected = [ - {a:1, foo: 'cherry'}, - {a:1, foo: 'cherry'}, - {a:1}, - {}, - {0: 1, 3: 4}, - {0: 1, 3: 4}, -] -// => `result` is equal to `expected` +### of + +```typescript + +of(x: T): T[] ``` -Try this R.pick example in Rambda REPL +Try this R.of example in Rambda REPL
All TypeScript definitions ```typescript -pick(propsToPick: K[], input: T): Pick>>; -pick(propsToPick: K[]): (input: T) => Pick>>; -pick(propsToPick: string, input: T): U; -pick(propsToPick: string): (input: T) => U; -pick(propsToPick: string, input: object): T; -pick(propsToPick: string): (input: object) => T; +of(x: T): T[]; ```
-R.pick source +R.of source ```javascript -import { createPath } from './_internals/createPath.js' - -export function pick(propsToPick, input){ - if (arguments.length === 1) return _input => pick(propsToPick, _input) - - if (input === null || input === undefined){ - return undefined - } - const keys = createPath(propsToPick, ',') - const willReturn = {} - let counter = 0 - - while (counter < keys.length){ - if (keys[ counter ] in input){ - willReturn[ keys[ counter ] ] = input[ keys[ counter ] ] - } - counter++ - } - - return willReturn +export function of(value){ + return [ value ] } ``` @@ -20225,83 +9388,12 @@ export function pick(propsToPick, input){ Tests ```javascript -import { pick } from './pick.js' - -const obj = { - a : 1, - b : 2, - c : 3, -} - -test('props to pick is a string', () => { - const result = pick('a,c', obj) - const resultCurry = pick('a,c')(obj) - const expectedResult = { - a : 1, - c : 3, - } - - expect(result).toEqual(expectedResult) - expect(resultCurry).toEqual(expectedResult) -}) - -test('when prop is missing', () => { - const result = pick('a,d,f', obj) - expect(result).toEqual({ a : 1 }) -}) - -test('with list indexes as props', () => { - const list = [ 1, 2, 3 ] - const expected = { - 0 : 1, - 2 : 3, - } - expect(pick([ 0, 2, 3 ], list)).toEqual(expected) - expect(pick('0,2,3', list)).toEqual(expected) -}) - -test('props to pick is an array', () => { - expect(pick([ 'a', 'c' ])({ - a : 'foo', - b : 'bar', - c : 'baz', - })).toEqual({ - a : 'foo', - c : 'baz', - }) - - expect(pick([ 'a', 'd', 'e', 'f' ])({ - a : 'foo', - b : 'bar', - c : 'baz', - })).toEqual({ a : 'foo' }) - - expect(pick('a,d,e,f')(null)).toBeUndefined() -}) - -test('works with list as input and number as props - props to pick is an array', () => { - const result = pick([ 1, 2 ], [ 'a', 'b', 'c', 'd' ]) - expect(result).toEqual({ - 1 : 'b', - 2 : 'c', - }) -}) - -test('works with list as input and number as props - props to pick is a string', () => { - const result = pick('1,2', [ 'a', 'b', 'c', 'd' ]) - expect(result).toEqual({ - 1 : 'b', - 2 : 'c', - }) -}) +import { of } from './of.js' -test('with symbol', () => { - const symbolProp = Symbol('s') - expect(pick([ symbolProp ], { [ symbolProp ] : 'a' })).toMatchInlineSnapshot(` -{ - Symbol(s): "a", -} -`) +test('happy', () => { + expect(of(3)).toEqual([ 3 ]) + + expect(of(null)).toEqual([ null ]) }) ``` @@ -20312,137 +9404,74 @@ test('with symbol', () => { TypeScript test ```typescript -import {pick} from 'rambda' - -const input = {a: 'foo', b: 2, c: 3, d: 4} - -describe('R.pick with array as props input', () => { - it('without passing type', () => { - const result = pick(['a', 'c'], input) - result.a // $ExpectType string - result.c // $ExpectType number - }) -}) - -describe('R.pick with string as props input', () => { - interface Input { - a: string, - b: number, - c: number, - d: number, - } - interface Output { - a: string, - c: number, - } - it('explicitly declare output', () => { - const result = pick('a,c', input) - result // $ExpectType Output - result.a // $ExpectType string - result.c // $ExpectType number - - const curriedResult = pick('a,c')(input) - - curriedResult.a // $ExpectType string - }) +import {of} from 'rambda' - it('explicitly declare input and output', () => { - const result = pick('a,c', input) - result // $ExpectType Output - result.a // $ExpectType string +const list = [1, 2, 3] - const curriedResult = pick('a,c')(input) +describe('R.of', () => { + it('happy', () => { + const result = of(4) - curriedResult.a // $ExpectType string + result // $ExpectType number[] }) + it('curried', () => { + const result = of(list) - it('without passing type', () => { - const result = pick('a,c', input) - result // $ExpectType unknown + result // $ExpectType number[][] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pick) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#of) -### pickAll +### omit ```typescript -pickAll(propsToPicks: K[], input: T): Pick +omit(propsToOmit: K[], obj: T): Omit ``` -Same as `R.pick` but it won't skip the missing props, i.e. it will assign them to `undefined`. - -> :boom: When using this method with `TypeScript`, it is much easier to pass `propsToPick` as an array. If passing a string, you will need to explicitly declare the output type. - -```javascript -const obj = { - a : 1, - b : false, - foo: 'cherry' -} -const propsToPick = 'a,foo,bar' -const propsToPickList = ['a', 'foo', 'bar'] - -const result = [ - R.pickAll(propsToPick, Record), - R.pickAll(propsToPickList, Record), - R.pickAll('a,bar', Record), - R.pickAll('bar', Record), -] -const expected = [ - {a:1, foo: 'cherry', bar: undefined}, - {a:1, foo: 'cherry', bar: undefined}, - {a:1, bar: undefined}, - {bar: undefined} -] -// => `result` is equal to `expected` -``` +It returns a partial copy of an `obj` without `propsToOmit` properties. -Try this R.pickAll example in Rambda REPL +Try this R.omit example in Rambda REPL
All TypeScript definitions ```typescript -pickAll(propsToPicks: K[], input: T): Pick; -pickAll(propsToPicks: string[], input: T): U; -pickAll(propsToPicks: string[]): (input: T) => U; -pickAll(propsToPick: string, input: T): U; -pickAll(propsToPick: string): (input: T) => U; +omit(propsToOmit: K[], obj: T): Omit; +omit(propsToOmit: K[]): (obj: T) => Omit; +omit(propsToOmit: string, obj: T): U; +omit(propsToOmit: string): (obj: T) => U; +omit(propsToOmit: string, obj: object): T; +omit(propsToOmit: string): (obj: object) => T; ```
-R.pickAll source +R.omit source ```javascript import { createPath } from './_internals/createPath.js' +import { includes } from './_internals/includes.js' -export function pickAll(propsToPick, obj){ - if (arguments.length === 1) return _obj => pickAll(propsToPick, _obj) +export function omit(propsToOmit, obj){ + if (arguments.length === 1) return _obj => omit(propsToOmit, _obj) - if (obj === null || obj === undefined){ + if (obj === null || obj === undefined) return undefined - } - const keysValue = createPath(propsToPick, ',') + + const propsToOmitValue = createPath(propsToOmit, ',') const willReturn = {} - let counter = 0 - while (counter < keysValue.length){ - if (keysValue[ counter ] in obj){ - willReturn[ keysValue[ counter ] ] = obj[ keysValue[ counter ] ] - } else { - willReturn[ keysValue[ counter ] ] = undefined - } - counter++ - } + for (const key in obj) + if (!includes(key, propsToOmitValue)) + willReturn[ key ] = obj[ key ] return willReturn } @@ -20455,12 +9484,7 @@ export function pickAll(propsToPick, obj){ Tests ```javascript -import { pickAll } from './pickAll.js' - -test('when input is undefined or null', () => { - expect(pickAll('a', null)).toBeUndefined() - expect(pickAll('a', undefined)).toBeUndefined() -}) +import { omit } from './omit.js' test('with string as condition', () => { const obj = { @@ -20468,27 +9492,33 @@ test('with string as condition', () => { b : 2, c : 3, } - const result = pickAll('a,c', obj) - const resultCurry = pickAll('a,c')(obj) - const expectedResult = { - a : 1, - b : undefined, - c : 3, - } + const result = omit('a,c', obj) + const resultCurry = omit('a,c')(obj) + const expectedResult = { b : 2 } expect(result).toEqual(expectedResult) expect(resultCurry).toEqual(expectedResult) }) -test('with array as condition', () => { - expect(pickAll([ 'a', 'b', 'c' ], { - a : 'foo', - c : 'baz', - })).toEqual({ +test.only('with number as property to omit', () => { + const obj = { + 1 : 1, + b : 2, + } + const result = omit([ 1 ], obj) + expect(result).toEqual({ b : 2 }) +}) + +test('with null', () => { + expect(omit('a,b', null)).toBeUndefined() +}) + +test('happy', () => { + expect(omit([ 'a', 'c' ])({ a : 'foo', - b : undefined, + b : 'bar', c : 'baz', - }) + })).toEqual({ b : 'bar' }) }) ``` @@ -20499,106 +9529,150 @@ test('with array as condition', () => { TypeScript test ```typescript -import {pickAll} from 'rambda' +import {omit} from 'rambda' -interface Input { - a: string, - b: number, - c: number, - d: number, -} -interface Output { - a?: string, - c?: number, -} -const input = {a: 'foo', b: 2, c: 3, d: 4} +describe('R.omit with array as props input', () => { + it('allow Typescript to infer object type', () => { + const input = {a: 'foo', b: 2, c: 3, d: 4} + const result = omit(['b,c'], input) -describe('R.pickAll with array as props input', () => { - it('without passing type', () => { - const result = pickAll(['a', 'c'], input) result.a // $ExpectType string - result.c // $ExpectType number - }) - it('without passing type + curry', () => { - const result = pickAll(['a', 'c'])(input) - result // $ExpectType unknown + result.d // $ExpectType number + + const curriedResult = omit(['a,c'], input) + + curriedResult.a // $ExpectType string + curriedResult.d // $ExpectType number }) - it('explicitly passing types', () => { - const result = pickAll(['a', 'c'], input) - result.a // $ExpectType string | undefined - result.c // $ExpectType number | undefined + + it('declare type of input object', () => { + interface Input { + a: string, + b: number, + c: number, + d: number, + } + const input: Input = {a: 'foo', b: 2, c: 3, d: 4} + const result = omit(['b,c'], input) + result // $ExpectType Omit + + result.a // $ExpectType string + result.d // $ExpectType number + + const curriedResult = omit(['a,c'], input) + + curriedResult.a // $ExpectType string + curriedResult.d // $ExpectType number }) }) -describe('R.pickAll with string as props input', () => { - it('without passing type', () => { - const result = pickAll('a,c', input) - result // $ExpectType unknown - }) - it('without passing type + curry', () => { - const result = pickAll('a,c')(input) - result // $ExpectType unknown - }) - it('explicitly passing types', () => { - const result = pickAll('a,c', input) - result.a // $ExpectType string | undefined - result.c // $ExpectType number | undefined +describe('R.omit with string as props input', () => { + interface Output { + b: number, + d: number, + } + + it('explicitly declare output', () => { + const result = omit('a,c', {a: 1, b: 2, c: 3, d: 4}) + result // $ExpectType Output + result.b // $ExpectType number + + const curriedResult = omit('a,c')({a: 1, b: 2, c: 3, d: 4}) + + curriedResult.b // $ExpectType number }) - it('explicitly passing types + curry', () => { - const result = pickAll('a,c')(input) - result.a // $ExpectType string | undefined - result.c // $ExpectType number | undefined + + it('explicitly declare input and output', () => { + interface Input { + a: number, + b: number, + c: number, + d: number, + } + const result = omit('a,c', {a: 1, b: 2, c: 3, d: 4}) + result // $ExpectType Output + result.b // $ExpectType number + + const curriedResult = omit('a,c')({ + a: 1, + b: 2, + c: 3, + d: 4, + }) + + curriedResult.b // $ExpectType number + }) + + it('without passing type', () => { + const result = omit('a,c', {a: 1, b: 2, c: 3, d: 4}) + result // $ExpectType unknown }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pickAll) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#omit) -### pickBy +### on + +It passes the two inputs through `unaryFn` and then the results are passed as inputs the the `binaryFn` to receive the final result(`binaryFn(unaryFn(FIRST_INPUT), unaryFn(SECOND_INPUT))`). + +This method is also known as P combinator. + +Try this R.on example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#on) + +### once ```typescript -pickBy(pred: ObjPred): (obj: V) => U +once(fn: T, context?: C): T ``` -```javascript -const result = R.pickBy( - x => x > 1, - {a: 1, b: 2, c: 3} -) -// => {b: 2, c: 3} -``` +It returns a function, which invokes only once `fn` function. -Try this R.pickBy example in Rambda REPL +Try this R.once example in Rambda REPL
All TypeScript definitions ```typescript -pickBy(pred: ObjPred): (obj: V) => U; -pickBy(pred: ObjPred, obj: T): U; +once(fn: T, context?: C): T; ```
-R.pickBy source +R.once source ```javascript -export function pickBy(predicate, obj) { +import { curry } from './curry.js' + +function onceFn(fn, context){ + let result + + return function (){ + if (fn){ + result = fn.apply(context || this, arguments) + fn = null + } + + return result + } +} + +export function once(fn, context){ if (arguments.length === 1){ - return (_obj) => pickBy(predicate, _obj); + const wrap = onceFn(fn, context) + + return curry(wrap) } - return Object.keys(obj).reduce((accum, key) => { - if (predicate(obj[ key ], key, obj)){ - accum[ key ] = obj[ key ]; - } - return accum; - }, {}); + + return onceFn(fn, context) } ``` @@ -20609,374 +9683,255 @@ export function pickBy(predicate, obj) { Tests ```javascript -import { T, always } from 'rambdax' -import { pickBy } from './pickBy' +import { once } from './once.js' -var obj = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6} +test('with counter', () => { + let counter = 0 + const runOnce = once(x => { + counter++ -it('creates a copy of the object', () => { - expect(pickBy(always(true), obj)).not.toBe(obj) + return x + 2 + }) + expect(runOnce(1)).toBe(3) + runOnce(1) + runOnce(1) + runOnce(1) + expect(counter).toBe(1) }) -it('when returning truthy, keeps the key', () => { - expect(pickBy(T, obj)).toEqual(obj) - expect(pickBy(always({}), obj)).toEqual(obj) - expect(pickBy(always(1), obj)).toEqual(obj) -}) +test('happy path', () => { + const addOneOnce = once(( + a, b, c + ) => a + b + c, 1) -it('when returning falsy, keeps the key', () => { - expect(pickBy(always(false), obj)).toEqual({}) - expect(pickBy(always(0), obj)).toEqual({}) - expect(pickBy(always(null), obj)).toEqual({}) + expect(addOneOnce( + 10, 20, 30 + )).toBe(60) + expect(addOneOnce(40)).toBe(60) }) -it('is called with (val,key,obj)', () => { - expect( - pickBy((val, key, _obj) => { - expect(_obj).toBe(obj) - return key === 'd' && val === 4 - }, obj) - ).toEqual({d: 4}) +test('with context', () => { + const context = { name: 'fris' } + const getNameOnce = once(function (){ + return this.name + }, context) + + expect(getNameOnce()).toBe('fris') + expect(getNameOnce()).toBe('fris') + expect(getNameOnce()).toBe('fris') }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pickBy) +
-### pipe +TypeScript test ```typescript +import {once} from 'rambda' -pipe( - ...funcs: [ - f1: (...args: TArgs) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4, - f5: (a: R4) => R5, - f6: (a: R5) => R6, - f7: (a: R6) => R7, - ...func: Array<(a: any) => any>, - fnLast: (a: any) => TResult - ] -): (...args: TArgs) => TResult +describe('R.once', () => { + it('happy', () => { + const runOnce = once((x: number) => { + return x + 2 + }) + + const result = runOnce(1) + result // $ExpectType number + }) + it('with context', () => { + const runOnce = once(function (this: any, x: number) { + return x + 2 + }) + + const result = runOnce.call({}, 1) + result // $ExpectType number + }) +}) ``` -It performs left-to-right function composition. +
-```javascript -const result = R.pipe( - R.filter(val => val > 2), - R.map(a => a * 2) -)([1, 2, 3, 4]) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#once) -// => [6, 8] +### or + +Logical OR + +Try this R.or example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#or) + +### over + +```typescript + +over(lens: Lens): { + (fn: (a: A) => A): (value: S) => S ``` -Try this R.pipe example in Rambda REPL +It returns a copied **Object** or **Array** with modified value received by applying function `fn` to `lens` focus. + +Try this R.over example in Rambda REPL
All TypeScript definitions ```typescript -pipe( - ...funcs: [ - f1: (...args: TArgs) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4, - f5: (a: R4) => R5, - f6: (a: R5) => R6, - f7: (a: R6) => R7, - ...func: Array<(a: any) => any>, - fnLast: (a: any) => TResult - ] -): (...args: TArgs) => TResult; // fallback overload if number of piped functions greater than 7 -pipe( - f1: (...args: TArgs) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4, - f5: (a: R4) => R5, - f6: (a: R5) => R6, - f7: (a: R6) => R7 -): (...args: TArgs) => R7; -pipe( - f1: (...args: TArgs) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4, - f5: (a: R4) => R5, - f6: (a: R5) => R6 -): (...args: TArgs) => R6; -pipe( - f1: (...args: TArgs) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4, - f5: (a: R4) => R5 -): (...args: TArgs) => R5; -pipe( - f1: (...args: TArgs) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4 -): (...args: TArgs) => R4; -pipe( - f1: (...args: TArgs) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3 -): (...args: TArgs) => R3; -pipe( - f1: (...args: TArgs) => R1, - f2: (a: R1) => R2 -): (...args: TArgs) => R2; -pipe( - f1: (...args: TArgs) => R1 -): (...args: TArgs) => R1; -``` - -
- -
- -R.pipe source - -```javascript -import { _arity } from './_internals/_arity.js' -import { reduceFn } from './reduce.js' - -export function _pipe(f, g){ - return function (){ - return g.call(this, f.apply(this, arguments)) - } -} - -export function pipe(){ - if (arguments.length === 0){ - throw new Error('pipe requires at least one argument') - } - - return _arity(arguments[ 0 ].length, - reduceFn( - _pipe, - arguments[ 0 ], - Array.prototype.slice.call( - arguments, 1, Infinity - ) - )) -} +over(lens: Lens): { + (fn: (a: A) => A): (value: S) => S; + (fn: (a: A) => A, value: S): S; +}; +over(lens: Lens, fn: (a: A) => A): (value: S) => S; +over(lens: Lens, fn: (a: A) => A, value: S): S; ```
-Tests +R.over source ```javascript -import { add, last, length, map } from '../rambda.js' -import { __findHighestArity } from './applySpec.js' -import { pipe } from './pipe.js' - -test('happy', () => { - const list = [ 1, 2, 3 ] - const result = pipe( - map(add(1)), map(add(10)), last - )(list) +import { curry } from './curry.js' - expect(result).toBe(14) +const Identity = x => ({ + x, + map : fn => Identity(fn(x)), }) -test('issue #627', () => { - expect(__findHighestArity({ len : pipe(length) })).toBe(1) -}) +function overFn( + lens, fn, object +){ + return lens(x => Identity(fn(x)))(object).x +} -test('with bad input', () => { - expect(() => pipe()).toThrowErrorMatchingInlineSnapshot('"pipe requires at least one argument"') -}) +export const over = curry(overFn) ```
-TypeScript test +Tests -```typescript -import { - add, - subtract, - pipe, - map, - filter, - identity, - dissoc, - inc, - negate, -} from 'rambda' +```javascript +import { assoc } from './assoc.js' +import { lens } from './lens.js' +import { lensIndex } from './lensIndex.js' +import { lensPath } from './lensPath.js' +import { over } from './over.js' +import { prop } from './prop.js' +import { toUpper } from './toUpper.js' -interface Input { - a: string, - b: string, -} -interface Output { - c: string, +const testObject = { + foo : 'bar', + baz : { + a : 'x', + b : 'y', + }, } -describe('R.pipe with explicit types', () => { - it('with explicit types - complex', () => { - const obj = { - a: 'foo', - b: 'bar', - } - interface AfterInput { - a: number, - } - interface BeforeOutput { - b: string, - } - - const result = pipe( - x => ({a: x.a.length + x.b.length}), - x => ({b: x.a + 'foo'}), - x => ({c: x.b + 'bar'}) - )(obj) - - result // $ExpectType Output - }) - it('with explicit types - correct', () => { - const obj = { - a: 'foo', - b: 'bar', - } - - const result = pipe(input => { - input // $ExpectType Input - return input as unknown as Output - }, identity)(obj) - result // $ExpectType Output - }) - it('with explicit types - wrong', () => { - const obj: Input = { - a: 'foo', - b: 'bar', - } - - // @ts-expect-error - pipe(identity, dissoc('b'))(obj) - }) +test('assoc lens', () => { + const assocLens = lens(prop('foo'), assoc('foo')) + const result = over( + assocLens, toUpper, testObject + ) + const expected = { + ...testObject, + foo : 'BAR', + } + expect(result).toEqual(expected) }) -describe('R.pipe', () => { - it('happy', () => { - const result = pipe(subtract(11), add(1), add(1))(1) - result // $ExpectType number - }) - it('happy - more complex', () => { - const result = pipe( - (x: string) => x.length + 1, - (x: number) => x + 1 - )('foo') - result // $ExpectType number - }) - - it('with R.filter', () => { - const result = pipe( - filter(x => x > 2), - map(add(1)) - )([1, 2, 3]) - result // $ExpectType number[] - }) - - it('with native filter', () => { - const result = pipe( - (list: number[]) => list.filter(x => x > 2), - (list: number[]) => { - list // $ExpectType number[] - return list - }, - map(add(1)) - )([1, 2, 3]) - - result // $ExpectType number[] - }) - - it('with void', () => { - const result = pipe( - () => {}, - () => {} - )() - result // $ExpectType void - }) +test('path lens', () => { + const pathLens = lensPath('baz.a') + const result = over( + pathLens, toUpper, testObject + ) + const expected = { + ...testObject, + baz : { + a : 'X', + b : 'y', + }, + } + expect(result).toEqual(expected) }) -describe('R.pipe - @types/ramda tests', () => { - test('complex', () => { - const fn = pipe(Math.pow, negate, inc, inc, inc, inc, inc, inc, inc, inc) - const result = fn(3, 4) - result // $ExpectType number - }) +test('index lens', () => { + const indexLens = lensIndex(0) + const result = over(indexLens, toUpper)([ 'foo', 'bar' ]) + expect(result).toEqual([ 'FOO', 'bar' ]) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pipe) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#over) -### pluck +### partial ```typescript -pluck(property: K, list: T[]): T[K][] +partial(fn: (x0: V0, x1: V1) => T, args: [V0]): (x1: V1) => T ``` -It returns list of the values of `property` taken from the all objects inside `list`. - -```javascript -const list = [{a: 1}, {a: 2}, {b: 3}] -const property = 'a' +It is very similar to `R.curry`, but you can pass initial arguments when you create the curried function. -const result = R.pluck(property, list) -// => [1, 2] -``` +`R.partial` will keep returning a function until all the arguments that the function `fn` expects are passed. +The name comes from the fact that you partially inject the inputs. -Try this R.pluck example in Rambda REPL +Try this R.partial example in Rambda REPL
All TypeScript definitions ```typescript -pluck(property: K, list: T[]): T[K][]; -pluck(property: number, list: { [k: number]: T }[]): T[]; -pluck

(property: P): (list: Record[]) => T[]; -pluck(property: number): (list: { [k: number]: T }[]) => T[]; +partial(fn: (x0: V0, x1: V1) => T, args: [V0]): (x1: V1) => T; +partial(fn: (x0: V0, x1: V1, x2: V2) => T, args: [V0, V1]): (x2: V2) => T; +partial(fn: (x0: V0, x1: V1, x2: V2) => T, args: [V0]): (x1: V1, x2: V2) => T; +partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: [V0, V1, V2], +): (x2: V3) => T; +partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: [V0, V1], +): (x2: V2, x3: V3) => T; +partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: [V0], +): (x1: V1, x2: V2, x3: V3) => T; +partial(fn: (...a: any[]) => T, args: any[]): (...a: any[]) => T; ```

-R.pluck source +R.partial source ```javascript -import { map } from './map.js' +import { isArray } from './_internals/isArray.js' -export function pluck(property, list){ - if (arguments.length === 1) return _list => pluck(property, _list) +export function partial(fn, ...args){ + const len = fn.length - const willReturn = [] + // If a single array argument is given, those are the args (a la Ramda). + // Otherwise, the variadic arguments are the args. + const argList = args.length === 1 && isArray(args[0]) ? args[0] : args - map(x => { - if (x[ property ] !== undefined){ - willReturn.push(x[ property ]) + return (...rest) => { + if (argList.length + rest.length >= len){ + return fn(...argList, ...rest) } - }, list) - return willReturn + return partial(fn, ...[ ...argList, ...rest ]) + } } ``` @@ -20987,23 +9942,67 @@ export function pluck(property, list){ Tests ```javascript -import { pluck } from './pluck.js' +import { partial } from './partial.js' +import { type } from './type.js' + +const greet = ( + salutation, title, firstName, lastName +) => + [salutation, title, firstName, lastName] test('happy', () => { - expect(pluck('a')([ { a : 1 }, { a : 2 }, { b : 1 } ])).toEqual([ 1, 2 ]) + const canPassAnyNumberOfArguments = partial( + greet, 'Hello', 'Ms.' + ) + const fn = canPassAnyNumberOfArguments('foo') + const sayHello = partial(greet, [ 'Hello' ]) + const sayHelloRamda = partial(sayHello, [ 'Ms.' ]) + + expect(type(fn)).toBe('Function') + + expect(fn('bar')).toStrictEqual(['Hello', 'Ms.', 'foo', 'bar']) + expect(sayHelloRamda('foo', 'bar')).toStrictEqual(['Hello', 'Ms.', 'foo', 'bar']) }) -test('with undefined', () => { - expect(pluck(undefined)([ { a : 1 }, { a : 2 }, { b : 1 } ])).toEqual([ ]) +test('extra arguments are ignored', () => { + const canPassAnyNumberOfArguments = partial( + greet, 'Hello', 'Ms.' + ) + const fn = canPassAnyNumberOfArguments('foo') + + expect(type(fn)).toBe('Function') + + expect(fn( + 'bar', 1, 2 + )).toStrictEqual(['Hello', 'Ms.', 'foo', 'bar']) }) -test('with number', () => { - const input = [ - [ 1, 2 ], - [ 3, 4 ], - ] +test('when array is input', () => { + const fooFn = ( + a, b, c, d + ) => ({ + a, + b, + c, + d, + }) + const barFn = partial( + fooFn, [ 1, 2 ], [] + ) - expect(pluck(0, input)).toEqual([ 1, 3 ]) + expect(barFn(1, 2)).toEqual({ + a : [ 1, 2 ], + b : [], + c : 1, + d : 2, + }) +}) + +test('ramda spec', () => { + const sayHello = partial(greet, 'Hello') + const sayHelloToMs = partial(sayHello, 'Ms.') + + expect(sayHelloToMs('Jane', 'Jones')).toStrictEqual(['Hello', 'Ms.', 'Jane', 'Jones']) }) ``` @@ -21014,82 +10013,74 @@ test('with number', () => { TypeScript test ```typescript -import {pluck} from 'rambda' +import {partial} from 'rambda' -describe('R.pluck', () => { - it('with object', () => { - interface ListMember { - a: number, - b: string, +describe('R.partial', () => { + it('happy', () => { + function fn( + aString: string, + aNumber: number, + aBoolean: boolean, + aNull: null + ) { + return {aString, aNumber, aBoolean, aNull} } - const input: ListMember[] = [ - {a: 1, b: 'foo'}, - {a: 2, b: 'bar'}, - ] - const resultA = pluck('a', input) - const resultB = pluck('b')(input) - resultA // $ExpectType number[] - resultB // $ExpectType string[] - }) - it('with array', () => { - const input = [ - [1, 2], - [3, 4], - [5, 6], - ] - const result = pluck(0, input) - const resultCurry = pluck(0)(input) - result // $ExpectType number[] - resultCurry // $ExpectType number[] + // @ts-expect-error + partial(fn, 1) + + const fn1 = partial(fn, ['a']) + partial(fn1, ['b']) + + const fn2 = partial(fn1, [2]) + const result = fn2(true, null) + result // $ExpectType { aString: string; aNumber: number; aBoolean: boolean; aNull: null; } }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pluck) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#partial) -### prepend +### partialObject ```typescript -prepend(xToPrepend: T, iterable: T[]): T[] +partialObject( + fn: (input: Input) => Output, + partialInput: PartialInput, +): (input: Pick>) => Output ``` -It adds element `x` at the beginning of `list`. +`R.partialObject` is a curry helper designed specifically for functions accepting object as a single argument. -```javascript -const result = R.prepend('foo', ['bar', 'baz']) -// => ['foo', 'bar', 'baz'] -``` +Initially the function knows only a part from the whole input object and then `R.partialObject` helps in preparing the function for the second part, when it receives the rest of the input. -Try this R.prepend example in Rambda REPL +Try this R.partialObject example in Rambda REPL
All TypeScript definitions ```typescript -prepend(xToPrepend: T, iterable: T[]): T[]; -prepend(xToPrepend: T, iterable: IsFirstSubtypeOfSecond[]) : U[]; -prepend(xToPrepend: T): (iterable: IsFirstSubtypeOfSecond[]) => U[]; -prepend(xToPrepend: T): (iterable: T[]) => T[]; +partialObject( + fn: (input: Input) => Output, + partialInput: PartialInput, +): (input: Pick>) => Output; ```
-R.prepend source +R.partialObject source ```javascript -export function prepend(x, input){ - if (arguments.length === 1) return _input => prepend(x, _input) - - if (typeof input === 'string') return [ x ].concat(input.split('')) +import { mergeDeepRight } from './mergeDeepRight.js' - return [ x ].concat(input) +export function partialObject(fn, input){ + return nextInput => fn(mergeDeepRight(nextInput, input)) } ``` @@ -21100,81 +10091,69 @@ export function prepend(x, input){ Tests ```javascript -import { prepend } from './prepend.js' - -test('happy', () => { - expect(prepend('yes', [ 'foo', 'bar', 'baz' ])).toEqual([ - 'yes', - 'foo', - 'bar', - 'baz', - ]) -}) - -test('with empty list', () => { - expect(prepend('foo')([])).toEqual([ 'foo' ]) -}) - -test('with string instead of array', () => { - expect(prepend('foo')('bar')).toEqual([ 'foo', 'b', 'a', 'r' ]) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#prepend) - -### product - -```typescript - -product(list: number[]): number -``` - -```javascript -R.product([ 2, 3, 4 ]) -// => 24) -``` - -Try this R.product example in Rambda REPL - -
- -All TypeScript definitions +import { delay } from './delay.js' +import { partialObject } from './partialObject.js' +import { type } from './type.js' -```typescript -product(list: number[]): number; -``` +test('with plain function', () => { + const fn = ({ a, b, c }) => a + b + c + const curried = partialObject(fn, { a : 1 }) -
+ expect(type(curried)).toBe('Function') + expect(curried({ + b : 2, + c : 3, + })).toBe(6) +}) -
+test('with function that throws an error', () => { + const fn = ({ a, b, c }) => { + throw new Error('foo') + } + const curried = partialObject(fn, { a : 1 }) -R.product source + expect(type(curried)).toBe('Function') + expect(() => + curried({ + b : 2, + c : 3, + })).toThrowErrorMatchingInlineSnapshot('"foo"') +}) -```javascript -import { multiply } from './multiply.js' -import { reduce } from './reduce.js' +test('with async', async () => { + const fn = async ({ a, b, c }) => { + await delay(100) -export const product = reduce(multiply, 1) -``` + return a + b + c + } -
+ const curried = partialObject(fn, { a : 1 }) -
+ const result = await curried({ + b : 2, + c : 3, + }) -Tests + expect(result).toBe(6) +}) -```javascript -import { product } from './product.js' +test('async function throwing an error', async () => { + const fn = async ({ a, b, c }) => { + await delay(100) + throw new Error('foo') + } -test('happy', () => { - expect(product([ 2, 3, 4 ])).toBe(24) -}) + const curried = partialObject(fn, { a : 1 }) -test('bad input', () => { - expect(product([ null ])).toBe(0) - expect(product([])).toBe(1) + try { + await curried({ + b : 2, + c : 3, + }) + expect(true).toBeFalsy() + } catch (e){ + expect(e.message).toBe('foo') + } }) ``` @@ -21185,70 +10164,132 @@ test('bad input', () => { TypeScript test ```typescript -import {product} from 'rambda' +import {partialObject, delay} from 'rambda' -describe('R.product', () => { +describe('R.partialObject', () => { it('happy', () => { - const result = product([1, 2, 3]) - - result // $ExpectType number + interface Input { + a: number, + b: number, + c: string, + } + const fn = ({a, b, c}: Input) => a + b + c + const curried = partialObject(fn, {a: 1}) + const result = curried({ + b: 2, + c: 'foo', + }) + result // $ExpectType string + }) + it('asynchronous', async() => { + interface Input { + a: number, + b: number, + c: string, + } + const fn = async({a, b, c}: Input) => { + await delay(100) + return a + b + c + } + const curried = partialObject(fn, {a: 1}) + const result = await curried({ + b: 2, + c: 'foo', + }) + result // $ExpectType string }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#product) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#partialObject) -### prop +### partition ```typescript -prop<_, P extends keyof never, T>(p: P, value: T): Prop +partition( + predicate: Predicate, + input: T[] +): [T[], T[]] ``` -It returns the value of property `propToFind` in `obj`. - -If there is no such property, it returns `undefined`. - -```javascript -const result = [ - R.prop('x', {x: 100}), - R.prop('x', {a: 1}) -] -// => [100, undefined] -``` +It will return array of two objects/arrays according to `predicate` function. The first member holds all instances of `input` that pass the `predicate` function, while the second member - those who doesn't. -Try this R.prop example in Rambda REPL +Try this R.partition example in Rambda REPL
All TypeScript definitions ```typescript -prop<_, P extends keyof never, T>(p: P, value: T): Prop; -prop(p: keyof never, value: unknown): V; -prop<_, P extends keyof never>(p: P): (value: T) => Prop; -prop(p: keyof never): (value: unknown) => V; +partition( + predicate: Predicate, + input: T[] +): [T[], T[]]; +partition( + predicate: Predicate +): (input: T[]) => [T[], T[]]; +partition( + predicate: (x: T, prop?: string) => boolean, + input: { [key: string]: T} +): [{ [key: string]: T}, { [key: string]: T}]; +partition( + predicate: (x: T, prop?: string) => boolean +): (input: { [key: string]: T}) => [{ [key: string]: T}, { [key: string]: T}]; ```
-R.prop source +R.partition source ```javascript -export function propFn(searchProperty, obj){ - if (!obj) return undefined +import { isArray } from './_internals/isArray.js' - return obj[ searchProperty ] +export function partitionObject(predicate, iterable){ + const yes = {} + const no = {} + Object.entries(iterable).forEach(([ prop, value ]) => { + if (predicate(value, prop)){ + yes[ prop ] = value + } else { + no[ prop ] = value + } + }) + + return [ yes, no ] } -export function prop(searchProperty, obj){ - if (arguments.length === 1) return _obj => prop(searchProperty, _obj) +export function partitionArray( + predicate, list, indexed = false +){ + const yes = [] + const no = [] + let counter = -1 - return propFn(searchProperty, obj) + while (counter++ < list.length - 1){ + if ( + indexed ? predicate(list[ counter ], counter) : predicate(list[ counter ]) + ){ + yes.push(list[ counter ]) + } else { + no.push(list[ counter ]) + } + } + + return [ yes, no ] +} + +export function partition(predicate, iterable){ + if (arguments.length === 1){ + return listHolder => partition(predicate, listHolder) + } + if (!isArray(iterable)) return partitionObject(predicate, iterable) + + return partitionArray(predicate, iterable) } ``` @@ -21259,129 +10300,218 @@ export function prop(searchProperty, obj){ Tests ```javascript -import { prop } from './prop.js' +import { partition } from './partition.js' -test('prop', () => { - expect(prop('foo')({ foo : 'baz' })).toBe('baz') +test('with array', () => { + const predicate = x => x > 2 + const list = [ 1, 2, 3, 4 ] - expect(prop('bar')({ foo : 'baz' })).toBeUndefined() + const result = partition(predicate, list) + const expectedResult = [ + [ 3, 4 ], + [ 1, 2 ], + ] - expect(prop('bar')(null)).toBeUndefined() + expect(result).toEqual(expectedResult) }) -``` -
+test('with object', () => { + const predicate = (value, prop) => { + expect(typeof prop).toBe('string') -
+ return value > 2 + } + const hash = { + a : 1, + b : 2, + c : 3, + d : 4, + } -TypeScript test + const result = partition(predicate)(hash) + const expectedResult = [ + { + c : 3, + d : 4, + }, + { + a : 1, + b : 2, + }, + ] -```typescript -import {prop} from 'rambda' + expect(result).toEqual(expectedResult) +}) -describe('R.prop', () => { - interface Foo { - a: number, - b: string, - c?: number, +test('readme example', () => { + const list = [ 1, 2, 3 ] + const obj = { + a : 1, + b : 2, + c : 3, } - const obj: Foo = {a: 1, b: 'foo'} - - it('issue #553', () => { - const result = { - a: prop('a', obj), - b: prop('b', obj), - c: prop('c', obj), - } - const curriedResult = { - a: prop('a')(obj), - b: prop('b')(obj), - c: prop('c')(obj), - } + const predicate = x => x > 2 - result // $ExpectType { a: number; b: string; c: number | undefined; } - curriedResult // $ExpectType { a: number; b: string; c: number | undefined; } - }) + const result = [ partition(predicate, list), partition(predicate, obj) ] + const expected = [ + [ [ 3 ], [ 1, 2 ] ], + [ + { c : 3 }, + { + a : 1, + b : 2, + }, + ], + ] + expect(result).toEqual(expected) }) +``` -describe('with number as prop', () => { - const list = [1, 2, 3] - const index = 1 - it('happy', () => { - const result = prop(index, list) +
- result // $ExpectType number - }) - it('curried require explicit type', () => { - const result = prop(index)(list) +
- result // $ExpectType number +TypeScript test + +```typescript +import {partition} from 'rambda' + +describe('R.partition', () => { + it('with array', () => { + const predicate = (x: number) => { + return x > 2 + } + const list = [1, 2, 3, 4] + + const result = partition(predicate, list) + const curriedResult = partition(predicate)(list) + result // $ExpectType [number[], number[]] + curriedResult // $ExpectType [number[], number[]] }) + + /* + revert to old version of `dtslint` and `R.partition` typing + as there is diff between VSCode types(correct) and dtslint(incorrect) + + it('with object', () => { + const predicate = (value: number, prop?: string) => { + return value > 2 + } + const hash = { + a: 1, + b: 2, + c: 3, + d: 4, + } + + const result = partition(predicate, hash) + const curriedResult = partition(predicate)(hash) + result[0] // $xExpectType { [key: string]: number; } + result[1] // $xExpectType { [key: string]: number; } + curriedResult[0] // $xExpectType { [key: string]: number; } + curriedResult[1] // $xExpectType { [key: string]: number; } + }) + */ }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#prop) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#partition) -### propEq +### path ```typescript -propEq(valueToMatch: any, propToFind: K, obj: Record): boolean +path(path: [K0], obj: S): S[K0] ``` -It returns true if `obj` has property `propToFind` and its value is equal to `valueToMatch`. - -```javascript -const obj = { foo: 'bar' } -const secondObj = { foo: 1 } - -const propToFind = 'foo' -const valueToMatch = 'bar' +If `pathToSearch` is `'a.b'` then it will return `1` if `obj` is `{a:{b:1}}`. -const result = [ - R.propEq(propToFind, valueToMatch, Record), - R.propEq(propToFind, valueToMatch, secondRecord) -] -// => [true, false] -``` +It will return `undefined`, if such path is not found. -Try this R.propEq example in Rambda REPL +Try this R.path example in Rambda REPL
All TypeScript definitions ```typescript -propEq(valueToMatch: any, propToFind: K, obj: Record): boolean; -propEq(valueToMatch: any, propToFind: K): (obj: Record) => boolean; -propEq(valueToMatch: any): { - (propToFind: K, obj: Record): boolean; - (propToFind: K): (obj: Record) => boolean; -}; +path(path: [K0], obj: S): S[K0]; +path(path: [K0, K1], obj: S): S[K0][K1]; +path< + S, + K0 extends keyof S = keyof S, + K1 extends keyof S[K0] = keyof S[K0], + K2 extends keyof S[K0][K1] = keyof S[K0][K1] +>(path: [K0, K1, K2], obj: S): S[K0][K1][K2]; +path< + S, + K0 extends keyof S = keyof S, + K1 extends keyof S[K0] = keyof S[K0], + K2 extends keyof S[K0][K1] = keyof S[K0][K1], + K3 extends keyof S[K0][K1][K2] = keyof S[K0][K1][K2], +>(path: [K0, K1, K2, K3], obj: S): S[K0][K1][K2][K3]; +path< + S, + K0 extends keyof S = keyof S, + K1 extends keyof S[K0] = keyof S[K0], + K2 extends keyof S[K0][K1] = keyof S[K0][K1], + K3 extends keyof S[K0][K1][K2] = keyof S[K0][K1][K2], + K4 extends keyof S[K0][K1][K2][K3] = keyof S[K0][K1][K2][K3], +>(path: [K0, K1, K2, K3, K4], obj: S): S[K0][K1][K2][K3][K4]; +path< + S, + K0 extends keyof S = keyof S, + K1 extends keyof S[K0] = keyof S[K0], + K2 extends keyof S[K0][K1] = keyof S[K0][K1], + K3 extends keyof S[K0][K1][K2] = keyof S[K0][K1][K2], + K4 extends keyof S[K0][K1][K2][K3] = keyof S[K0][K1][K2][K3], + K5 extends keyof S[K0][K1][K2][K3][K4] = keyof S[K0][K1][K2][K3][K4], +>(path: [K0, K1, K2, K3, K4, K5], obj: S): S[K0][K1][K2][K3][K4][K5]; +path(pathToSearch: string, obj: any): T | undefined; +path(pathToSearch: string): (obj: any) => T | undefined; +path(pathToSearch: RamdaPath): (obj: any) => T | undefined; +path(pathToSearch: RamdaPath, obj: any): T | undefined; ```
-R.propEq source +R.path source ```javascript -import { curry } from './curry.js' -import { equals } from './equals.js' -import { prop } from './prop.js' +import { createPath } from './_internals/createPath.js' -function propEqFn( - valueToMatch, propToFind, obj -){ - if (!obj) return false +export function pathFn(pathInput, obj){ + let willReturn = obj + let counter = 0 - return equals(valueToMatch, prop(propToFind, obj)) + const pathArrValue = createPath(pathInput) + + while (counter < pathArrValue.length){ + if (willReturn === null || willReturn === undefined){ + return undefined + } + if (willReturn[ pathArrValue[ counter ] ] === null) return undefined + + willReturn = willReturn[ pathArrValue[ counter ] ] + counter++ + } + + return willReturn } -export const propEq = curry(propEqFn) +export function path(pathInput, obj){ + if (arguments.length === 1) return _obj => path(pathInput, _obj) + + if (obj === null || obj === undefined){ + return undefined + } + + return pathFn(pathInput, obj) +} ```
@@ -21391,26 +10521,41 @@ export const propEq = curry(propEqFn) Tests ```javascript -import { BAR, FOO } from './_internals/testUtils.js' -import { propEq } from './propEq.js' +import { path } from './path.js' -test('happy', () => { - const obj = { [ FOO ] : BAR } - expect(propEq(BAR, FOO)(obj)).toBeTrue() - expect(propEq(1, FOO)(obj)).toBeFalse() - expect(propEq(1)(FOO)(obj)).toBeFalse() - expect(propEq( - 1, 1, null - )).toBeFalse() +test('with array inside object', () => { + const obj = { a : { b : [ 1, { c : 1 } ] } } + + expect(path('a.b.1.c', obj)).toBe(1) }) -test('returns false if called with a null or undefined object', () => { - expect(propEq( - 'name', 'Abby', null - )).toBeFalse() - expect(propEq( - 'name', 'Abby', undefined - )).toBeFalse() +test('works with undefined', () => { + const obj = { a : { b : { c : 1 } } } + + expect(path('a.b.c.d.f', obj)).toBeUndefined() + expect(path('foo.babaz', undefined)).toBeUndefined() + expect(path('foo.babaz')(undefined)).toBeUndefined() +}) + +test('works with string instead of array', () => { + expect(path('foo.bar.baz')({ foo : { bar : { baz : 'yes' } } })).toBe('yes') +}) + +test('path', () => { + expect(path([ 'foo', 'bar', 'baz' ])({ foo : { bar : { baz : 'yes' } } })).toBe('yes') + expect(path([ 'foo', 'bar', 'baz' ])(null)).toBeUndefined() + expect(path([ 'foo', 'bar', 'baz' ])({ foo : { bar : 'baz' } })).toBeUndefined() +}) + +test('with number string in between', () => { + expect(path(['a','1','b'], {a: [{b: 1}, {b: 2}]})).toBe(2) +}) + +test('null is not a valid path', () => { + expect(path('audio_tracks', { + a : 1, + audio_tracks : null, + })).toBeUndefined() }) ``` @@ -21421,111 +10566,109 @@ test('returns false if called with a null or undefined object', () => { TypeScript test ```typescript -import {propEq} from 'rambda' +import {path} from 'rambda' -const property = 'foo' -const numberProperty = 1 -const value = 'bar' -const obj = {[property]: value} -const objWithNumberIndex = {[numberProperty]: value} +const input = {a: {b: {c: true}}} -describe('R.propEq', () => { - it('happy', () => { - const result = propEq(value, property, obj) - result // $ExpectType boolean +describe('R.path with string as path', () => { + it('without specified output type', () => { + // $ExpectType unknown + path('a.b.c', input) + // $ExpectType unknown + path('a.b.c')(input) }) - - it('number is property', () => { - const result = propEq(value, 1, objWithNumberIndex) - result // $ExpectType boolean + it('with specified output type', () => { + // $ExpectType boolean | undefined + path('a.b.c', input) + // $ExpectType boolean | undefined + path('a.b.c')(input) }) +}) - it('with optional property', () => { - interface MyType { - optional?: string | number, - } +describe('R.path with list as path', () => { + it('with array as path', () => { + // $ExpectType boolean + path(['a', 'b', 'c'], input) + // $ExpectType unknown + path(['a', 'b', 'c'])(input) + }) + test('shallow property', () => { + // $ExpectType number + path(['a'], {a: 1}) - const myObject: MyType = {} - const valueToFind = '1111' - // @ts-expect-error - propEq(valueToFind, 'optional', myObject) + // $ExpectType unknown + path(['b'], {a: 1}) + }) + test('deep property', () => { + const testObject = {a: {b: {c: {d: {e: {f: 1}}}}}} + const result = path(['a', 'b', 'c', 'd', 'e', 'f'], testObject) + // $ExpectType number + result + const curriedResult = path(['a', 'b', 'c', 'd', 'e', 'f'])(testObject) + // $ExpectType unknown + curriedResult }) - - it('imported from @types/ramda', () => { - interface A { - foo: string | null, - } - const obj: A = { - foo: 'bar', + test('issue #668 - path is not correct', () => { + const object = { + is: { + a: 'path', + }, } - const value = '' - const result = propEq(value, 'foo')(obj) - result // $ExpectType boolean - - // @ts-expect-error - propEq(value, 'bar')(obj) + const result = path(['is', 'not', 'a'], object) + // $ExpectType unknown + result + const curriedResult = path(['is', 'not', 'a'])(object) + // $ExpectType unknown + curriedResult }) }) ``` -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#propEq) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#path) -### propIs +### pathEq ```typescript -propIs(type: C, name: K, obj: any): obj is Record> +pathEq(pathToSearch: Path, target: any, input: any): boolean ``` -It returns `true` if `property` of `obj` is from `target` type. - -```javascript -const obj = {a:1, b: 'foo'} +It returns `true` if `pathToSearch` of `input` object is equal to `target` value. -const result = [ - R.propIs(Number, 'a', Record), - R.propIs(String, 'b', Record), - R.propIs(Number, 'b', Record), -] -// => [true, true, false] -``` +`pathToSearch` is passed to `R.path`, which means that it can be either a string or an array. Also equality between `target` and the found value is determined by `R.equals`. -Try this R.propIs example in Rambda REPL +Try this R.pathEq example in Rambda REPL
All TypeScript definitions ```typescript -propIs(type: C, name: K, obj: any): obj is Record>; -propIs(type: C, name: K, obj: any): obj is Record>; -propIs(type: C, name: K): (obj: any) => obj is Record>; -propIs(type: C, name: K): (obj: any) => obj is Record>; -propIs(type: C): { - (name: K, obj: any): obj is Record>; - (name: K): (obj: any) => obj is Record>; -}; +pathEq(pathToSearch: Path, target: any, input: any): boolean; +pathEq(pathToSearch: Path, target: any): (input: any) => boolean; +pathEq(pathToSearch: Path): (target: any) => (input: any) => boolean; ```
-R.propIs source +R.pathEq source ```javascript import { curry } from './curry.js' -import { is } from './is.js' +import { equals } from './equals.js' +import { path } from './path.js' -function propIsFn( - targetPrototype, property, obj +function pathEqFn( + pathToSearch, target, input ){ - return is(targetPrototype, obj[ property ]) + return equals(path(pathToSearch, input), target) } -export const propIs = curry(propIsFn) +export const pathEq = curry(pathEqFn) ```
@@ -21535,28 +10678,33 @@ export const propIs = curry(propIsFn) Tests ```javascript -import { propIs } from './propIs.js' - -const obj = { - a : 1, - b : 'foo', -} +import { pathEq } from './pathEq.js' test('when true', () => { - expect(propIs( - Number, 'a', obj - )).toBeTrue() - expect(propIs( - String, 'b', obj + const path = 'a.b' + const obj = { a : { b : { c : 1 } } } + const target = { c : 1 } + + expect(pathEq( + path, target, obj )).toBeTrue() }) test('when false', () => { - expect(propIs( - String, 'a', obj - )).toBeFalse() - expect(propIs( - Number, 'b', obj + const path = 'a.b' + const obj = { a : { b : 1 } } + const target = 2 + + expect(pathEq(path, target)(obj)).toBeFalse() +}) + +test('when wrong path', () => { + const path = 'foo.bar' + const obj = { a : { b : 1 } } + const target = 2 + + expect(pathEq( + path, target, obj )).toBeFalse() }) ``` @@ -21568,83 +10716,93 @@ test('when false', () => { TypeScript test ```typescript -import {propIs} from 'rambda' +import {pathEq} from 'rambda' -const property = 'a' -const obj = {a: 1} +describe('R.pathEq', () => { + it('with string path', () => { + const pathToSearch = 'a.b.c' + const input = {a: {b: {c: 1}}} + const target = {c: 1} -describe('R.propIs', () => { - it('happy', () => { - const result = propIs(Number, property, obj) + const result = pathEq(pathToSearch, input, target) + const curriedResult = pathEq(pathToSearch, input, target) result // $ExpectType boolean + curriedResult // $ExpectType boolean }) - it('curried', () => { - const result = propIs(Number, property)(obj) + it('with array path', () => { + const pathToSearch = ['a', 'b', 'c'] + const input = {a: {b: {c: 1}}} + const target = {c: 1} + + const result = pathEq(pathToSearch, input, target) + const curriedResult = pathEq(pathToSearch, input, target) result // $ExpectType boolean + curriedResult // $ExpectType boolean }) }) + +describe('with ramda specs', () => { + const testPath = ['x', 0, 'y'] + const testObj = { + x: [ + {y: 2, z: 3}, + {y: 4, z: 5}, + ], + } + + const result1 = pathEq(testPath, 2, testObj) + const result2 = pathEq(testPath, 2)(testObj) + const result3 = pathEq(testPath)(2)(testObj) + result1 // $ExpectType boolean + result2 // $ExpectType boolean + result3 // $ExpectType boolean +}) ``` -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#propIs) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pathEq) -### propOr +### pathOr ```typescript -propOr(defaultValue: T, property: P, obj: Partial> | undefined): T +pathOr(defaultValue: T, pathToSearch: Path, obj: any): T ``` -It returns either `defaultValue` or the value of `property` in `obj`. - -```javascript -const obj = {a: 1} -const defaultValue = 'DEFAULT_VALUE' -const property = 'a' - -const result = [ - R.propOr(defaultValue, property, Record), - R.propOr(defaultValue, 'foo', Record) -] -// => [1, 'DEFAULT_VALUE'] -``` +It reads `obj` input and returns either `R.path(pathToSearch, Record)` result or `defaultValue` input. -Try this R.propOr example in Rambda REPL +Try this R.pathOr example in Rambda REPL
All TypeScript definitions ```typescript -propOr(defaultValue: T, property: P, obj: Partial> | undefined): T; -propOr(defaultValue: T, property: P): (obj: Partial> | undefined) => T; -propOr(defaultValue: T): { -

(property: P, obj: Partial> | undefined): T; -

(property: P): (obj: Partial> | undefined) => T; -} +pathOr(defaultValue: T, pathToSearch: Path, obj: any): T; +pathOr(defaultValue: T, pathToSearch: Path): (obj: any) => T; +pathOr(defaultValue: T): (pathToSearch: Path) => (obj: any) => T; ```

-R.propOr source +R.pathOr source ```javascript import { curry } from './curry.js' import { defaultTo } from './defaultTo.js' +import { path } from './path.js' -function propOrFn( - defaultValue, property, obj +function pathOrFn( + defaultValue, pathInput, obj ){ - if (!obj) return defaultValue - - return defaultTo(defaultValue, obj[ property ]) + return defaultTo(defaultValue, path(pathInput, obj)) } -export const propOr = curry(propOrFn) +export const pathOr = curry(pathOrFn) ```
@@ -21654,27 +10812,48 @@ export const propOr = curry(propOrFn) Tests ```javascript -import { propOr } from './propOr.js' +import { pathOr } from './pathOr.js' -test('propOr (result)', () => { - const obj = { a : 1 } - expect(propOr( - 'default', 'a', obj - )).toBe(1) - expect(propOr( - 'default', 'notExist', obj - )).toBe('default') - expect(propOr( - 'default', 'notExist', null - )).toBe('default') +test('with undefined', () => { + const result = pathOr( + 'foo', 'x.y', { x : { y : 1 } } + ) + + expect(result).toBe(1) }) -test('propOr (currying)', () => { - const obj = { a : 1 } - expect(propOr('default')('a', obj)).toBe(1) - expect(propOr('default', 'a')(obj)).toBe(1) - expect(propOr('default')('notExist', obj)).toBe('default') - expect(propOr('default', 'notExist')(obj)).toBe('default') +test('with null', () => { + const result = pathOr( + 'foo', 'x.y', null + ) + + expect(result).toBe('foo') +}) + +test('with NaN', () => { + const result = pathOr( + 'foo', 'x.y', NaN + ) + + expect(result).toBe('foo') +}) + +test('curry case (x)(y)(z)', () => { + const result = pathOr('foo')('x.y.z')({ x : { y : { a : 1 } } }) + + expect(result).toBe('foo') +}) + +test('curry case (x)(y,z)', () => { + const result = pathOr('foo', 'x.y.z')({ x : { y : { a : 1 } } }) + + expect(result).toBe('foo') +}) + +test('curry case (x,y)(z)', () => { + const result = pathOr('foo')('x.y.z', { x : { y : { a : 1 } } }) + + expect(result).toBe('foo') }) ``` @@ -21685,84 +10864,71 @@ test('propOr (currying)', () => { TypeScript test ```typescript -import {propOr} from 'rambda' - -const obj = {foo: 'bar'} -const property = 'foo' -const fallback = 'fallback' +import {pathOr} from 'rambda' -describe('R.propOr', () => { - it('happy', () => { - const result = propOr(fallback, property, obj) - result // $ExpectType string +describe('R.pathOr', () => { + it('with string path', () => { + const x = pathOr('foo', 'x.y', {x: {y: 'bar'}}) + x // $ExpectType string }) - it('curry 1', () => { - const result = propOr(fallback)(property, obj) - result // $ExpectType string + it('with array path', () => { + const x = pathOr('foo', ['x', 'y'], {x: {y: 'bar'}}) + x // $ExpectType string }) - it('curry 2', () => { - const result = propOr(fallback, property)(obj) - result // $ExpectType string + it('without passing type looks bad', () => { + const x = pathOr('foo', 'x.y', {x: {y: 'bar'}}) + x // $ExpectType "foo" }) - it('curry 3', () => { - const result = propOr(fallback)(property)(obj) - result // $ExpectType string + it('curried', () => { + const x = pathOr('foo', 'x.y')({x: {y: 'bar'}}) + x // $ExpectType string }) }) ``` -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#propOr) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pathOr) -### props +### paths ```typescript -props

(propsToPick: P[], obj: Record): T[] +paths(pathsToSearch: Path[], obj: Input): (T | undefined)[] ``` -It takes list with properties `propsToPick` and returns a list with property values in `obj`. +It loops over members of `pathsToSearch` as `singlePath` and returns the array produced by `R.path(singlePath, Record)`. -```javascript -const result = R.props( - ['a', 'b'], - {a:1, c:3} -) -// => [1, undefined] -``` +Because it calls `R.path`, then `singlePath` can be either string or a list. -Try this R.props example in Rambda REPL +Try this R.paths example in Rambda REPL

All TypeScript definitions ```typescript -props

(propsToPick: P[], obj: Record): T[]; -props

(propsToPick: P[]): (obj: Record) => T[]; -props

(propsToPick: P[]): (obj: Record) => T[]; +paths(pathsToSearch: Path[], obj: Input): (T | undefined)[]; +paths(pathsToSearch: Path[]): (obj: Input) => (T | undefined)[]; +paths(pathsToSearch: Path[], obj: any): (T | undefined)[]; +paths(pathsToSearch: Path[]): (obj: any) => (T | undefined)[]; ```

-R.props source +R.paths source ```javascript -import { isArray } from './_internals/isArray.js' -import { mapArray } from './map.js' +import { path } from './path.js' -export function props(propsToPick, obj){ +export function paths(pathsToSearch, obj){ if (arguments.length === 1){ - return _obj => props(propsToPick, _obj) - } - if (!isArray(propsToPick)){ - throw new Error('propsToPick is not a list') + return _obj => paths(pathsToSearch, _obj) } - return mapArray(prop => obj[ prop ], propsToPick) + return pathsToSearch.map(singlePath => path(singlePath, obj)) } ``` @@ -21773,26 +10939,63 @@ export function props(propsToPick, obj){ Tests ```javascript -import { props } from './props.js' +import { paths } from './paths.js' const obj = { - a : 1, - b : 2, + a : { + b : { + c : 1, + d : 2, + }, + }, + p : [ { q : 3 } ], + x : { + y : 'FOO', + z : [ [ {} ] ], + }, } -const propsToPick = [ 'a', 'c' ] -test('happy', () => { - const result = props(propsToPick, obj) - expect(result).toEqual([ 1, undefined ]) +test('with string path + curry', () => { + const pathsInput = [ 'a.b.d', 'p.q' ] + const expected = [ 2, undefined ] + const result = paths(pathsInput, obj) + const curriedResult = paths(pathsInput)(obj) + + expect(result).toEqual(expected) + expect(curriedResult).toEqual(expected) }) -test('curried', () => { - const result = props(propsToPick)(obj) - expect(result).toEqual([ 1, undefined ]) +test('with array path', () => { + const result = paths([ + [ 'a', 'b', 'c' ], + [ 'x', 'y' ], + ], + obj) + + expect(result).toEqual([ 1, 'FOO' ]) }) -test('wrong input', () => { - expect(() => props(null)(obj)).toThrowErrorMatchingInlineSnapshot('"propsToPick is not a list"') +test('takes a paths that contains indices into arrays', () => { + expect(paths([ + [ 'p', 0, 'q' ], + [ 'x', 'z', 0, 0 ], + ], + obj)).toEqual([ 3, {} ]) + expect(paths([ + [ 'p', 0, 'q' ], + [ 'x', 'z', 2, 1 ], + ], + obj)).toEqual([ 3, undefined ]) +}) + +test('gets a deep property\'s value from objects', () => { + expect(paths([ [ 'a', 'b' ] ], obj)).toEqual([ obj.a.b ]) + expect(paths([ [ 'p', 0 ] ], obj)).toEqual([ obj.p[ 0 ] ]) +}) + +test('returns undefined for items not found', () => { + expect(paths([ [ 'a', 'x', 'y' ] ], obj)).toEqual([ undefined ]) + expect(paths([ [ 'p', 2 ] ], obj)).toEqual([ undefined ]) }) ``` @@ -21803,173 +11006,305 @@ test('wrong input', () => { TypeScript test ```typescript -import {props} from 'rambda' +import {paths} from 'rambda' -const obj = {a: 1, b: 2} +interface Input { + a: number, + b: number, + c: number, +} -describe('R.props', () => { - it('happy', () => { - const result = props(['a', 'b'], obj) +const input: Input = {a: 1, b: 2, c: 3} - result // $ExpectType number[] +describe('R.paths', () => { + it('with dot notation', () => { + const result = paths(['a.b.c', 'foo.bar'], input) + result // $ExpectType (number | undefined)[] }) - it('curried', () => { - const result = props(['a', 'b'])(obj) - result // $ExpectType number[] + it('without type', () => { + const result = paths(['a.b.c', 'foo.bar'], input) + result // $ExpectType unknown[] + }) + + it('with array as path', () => { + const result = paths([['a', 'b', 'c'], ['foo.bar']], input) + result // $ExpectType (number | undefined)[] + }) + + it('curried', () => { + const result = paths([['a', 'b', 'c'], ['foo.bar']])(input) + result // $ExpectType (number | undefined)[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#props) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#paths) -### propSatisfies +### pathSatisfies + +Try this R.pathSatisfies example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pathSatisfies) + +### pick ```typescript -propSatisfies(predicate: Predicate, property: string, obj: Record): boolean +pick(propsToPick: K[], input: T): Pick>> ``` -It returns `true` if the object property satisfies a given predicate. +It returns a partial copy of an `input` containing only `propsToPick` properties. -```javascript -const obj = {a: {b:1}} -const property = 'a' -const predicate = x => x?.b === 1 +`input` can be either an object or an array. -const result = R.propSatisfies(predicate, property, Record) -// => true -``` +String annotation of `propsToPick` is one of the differences between `Rambda` and `Ramda`. -Try this R.propSatisfies example in Rambda REPL +Try this R.pick example in Rambda REPL
All TypeScript definitions ```typescript -propSatisfies(predicate: Predicate, property: string, obj: Record): boolean; -propSatisfies(predicate: Predicate, property: string): (obj: Record) => boolean; +pick(propsToPick: K[], input: T): Pick>>; +pick(propsToPick: K[]): (input: T) => Pick>>; +pick(propsToPick: string, input: T): U; +pick(propsToPick: string): (input: T) => U; +pick(propsToPick: string, input: object): T; +pick(propsToPick: string): (input: object) => T; +``` + +
+ +
+ +R.pick source + +```javascript +import { createPath } from './_internals/createPath.js' + +export function pick(propsToPick, input){ + if (arguments.length === 1) return _input => pick(propsToPick, _input) + + if (input === null || input === undefined){ + return undefined + } + const keys = createPath(propsToPick, ',') + const willReturn = {} + let counter = 0 + + while (counter < keys.length){ + if (keys[ counter ] in input){ + willReturn[ keys[ counter ] ] = input[ keys[ counter ] ] + } + counter++ + } + + return willReturn +} ``` -
+ + +
+ +Tests + +```javascript +import { pick } from './pick.js' + +const obj = { + a : 1, + b : 2, + c : 3, +} + +test('props to pick is a string', () => { + const result = pick('a,c', obj) + const resultCurry = pick('a,c')(obj) + const expectedResult = { + a : 1, + c : 3, + } + + expect(result).toEqual(expectedResult) + expect(resultCurry).toEqual(expectedResult) +}) + +test('when prop is missing', () => { + const result = pick('a,d,f', obj) + expect(result).toEqual({ a : 1 }) +}) + +test('with list indexes as props', () => { + const list = [ 1, 2, 3 ] + const expected = { + 0 : 1, + 2 : 3, + } + expect(pick([ 0, 2, 3 ], list)).toEqual(expected) + expect(pick('0,2,3', list)).toEqual(expected) +}) + +test('props to pick is an array', () => { + expect(pick([ 'a', 'c' ])({ + a : 'foo', + b : 'bar', + c : 'baz', + })).toEqual({ + a : 'foo', + c : 'baz', + }) + + expect(pick([ 'a', 'd', 'e', 'f' ])({ + a : 'foo', + b : 'bar', + c : 'baz', + })).toEqual({ a : 'foo' }) -
+ expect(pick('a,d,e,f')(null)).toBeUndefined() +}) -R.propSatisfies source +test('works with list as input and number as props - props to pick is an array', () => { + const result = pick([ 1, 2 ], [ 'a', 'b', 'c', 'd' ]) + expect(result).toEqual({ + 1 : 'b', + 2 : 'c', + }) +}) -```javascript -import { curry } from './curry.js' -import { prop } from './prop.js' +test('works with list as input and number as props - props to pick is a string', () => { + const result = pick('1,2', [ 'a', 'b', 'c', 'd' ]) + expect(result).toEqual({ + 1 : 'b', + 2 : 'c', + }) +}) -function propSatisfiesFn( - predicate, property, obj -){ - return predicate(prop(property, obj)) +test('with symbol', () => { + const symbolProp = Symbol('s') + expect(pick([ symbolProp ], { [ symbolProp ] : 'a' })).toMatchInlineSnapshot(` +{ + Symbol(s): "a", } - -export const propSatisfies = curry(propSatisfiesFn) +`) +}) ```
-Tests - -```javascript -import { propSatisfies } from './propSatisfies.js' +TypeScript test -const obj = { a : 1 } +```typescript +import {pick} from 'rambda' -test('when true', () => { - expect(propSatisfies( - x => x > 0, 'a', obj - )).toBeTrue() -}) +const input = {a: 'foo', b: 2, c: 3, d: 4} -test('when false', () => { - expect(propSatisfies(x => x < 0, 'a')(obj)).toBeFalse() +describe('R.pick with array as props input', () => { + it('without passing type', () => { + const result = pick(['a', 'c'], input) + result.a // $ExpectType string + result.c // $ExpectType number + }) }) -``` - -
-
+describe('R.pick with string as props input', () => { + interface Input { + a: string, + b: number, + c: number, + d: number, + } + interface Output { + a: string, + c: number, + } + it('explicitly declare output', () => { + const result = pick('a,c', input) + result // $ExpectType Output + result.a // $ExpectType string + result.c // $ExpectType number -TypeScript test + const curriedResult = pick('a,c')(input) -```typescript -import {propSatisfies} from 'rambda' + curriedResult.a // $ExpectType string + }) -const obj = {a: 1} + it('explicitly declare input and output', () => { + const result = pick('a,c', input) + result // $ExpectType Output + result.a // $ExpectType string -describe('R.propSatisfies', () => { - it('happy', () => { - const result = propSatisfies(x => x > 0, 'a', obj) + const curriedResult = pick('a,c')(input) - result // $ExpectType boolean + curriedResult.a // $ExpectType string }) - it('curried requires explicit type', () => { - const result = propSatisfies(x => x > 0, 'a')(obj) - result // $ExpectType boolean + it('without passing type', () => { + const result = pick('a,c', input) + result // $ExpectType unknown }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#propSatisfies) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pick) -### range +### pickAll ```typescript -range(startInclusive: number, endExclusive: number): number[] +pickAll(propsToPicks: K[], input: T): Pick ``` -It returns list of numbers between `startInclusive` to `endExclusive` markers. - -```javascript -R.range(0, 5) -// => [0, 1, 2, 3, 4] -``` +Same as `R.pick` but it won't skip the missing props, i.e. it will assign them to `undefined`. -Try this R.range example in Rambda REPL +Try this R.pickAll example in Rambda REPL
All TypeScript definitions ```typescript -range(startInclusive: number, endExclusive: number): number[]; -range(startInclusive: number): (endExclusive: number) => number[]; +pickAll(propsToPicks: K[], input: T): Pick; +pickAll(propsToPicks: string[], input: T): U; +pickAll(propsToPicks: string[]): (input: T) => U; +pickAll(propsToPick: string, input: T): U; +pickAll(propsToPick: string): (input: T) => U; ```
-R.range source +R.pickAll source ```javascript -export function range(start, end){ - if (arguments.length === 1) return _end => range(start, _end) - - if (Number.isNaN(Number(start)) || Number.isNaN(Number(end))){ - throw new TypeError('Both arguments to range must be numbers') - } +import { createPath } from './_internals/createPath.js' - if (end < start) return [] +export function pickAll(propsToPick, obj){ + if (arguments.length === 1) return _obj => pickAll(propsToPick, _obj) - const len = end - start - const willReturn = Array(len) + if (obj === null || obj === undefined){ + return undefined + } + const keysValue = createPath(propsToPick, ',') + const willReturn = {} + let counter = 0 - for (let i = 0; i < len; i++){ - willReturn[ i ] = start + i + while (counter < keysValue.length){ + if (keysValue[ counter ] in obj){ + willReturn[ keysValue[ counter ] ] = obj[ keysValue[ counter ] ] + } else { + willReturn[ keysValue[ counter ] ] = undefined + } + counter++ } return willReturn @@ -21983,129 +11318,165 @@ export function range(start, end){ Tests ```javascript -import { range } from './range.js' +import { pickAll } from './pickAll.js' -test('happy', () => { - expect(range(0, 10)).toEqual([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]) +test('when input is undefined or null', () => { + expect(pickAll('a', null)).toBeUndefined() + expect(pickAll('a', undefined)).toBeUndefined() }) -test('end range is bigger than start range', () => { - expect(range(7, 3)).toEqual([]) - expect(range(5, 5)).toEqual([]) +test('with string as condition', () => { + const obj = { + a : 1, + b : 2, + c : 3, + } + const result = pickAll('a,c', obj) + const resultCurry = pickAll('a,c')(obj) + const expectedResult = { + a : 1, + b : undefined, + c : 3, + } + + expect(result).toEqual(expectedResult) + expect(resultCurry).toEqual(expectedResult) }) -test('with bad input', () => { - const throwMessage = 'Both arguments to range must be numbers' - expect(() => range('a', 6)).toThrowWithMessage(Error, throwMessage) - expect(() => range(6, 'z')).toThrowWithMessage(Error, throwMessage) +test('with array as condition', () => { + expect(pickAll([ 'a', 'b', 'c' ], { + a : 'foo', + c : 'baz', + })).toEqual({ + a : 'foo', + b : undefined, + c : 'baz', + }) }) +``` -test('curry', () => { - expect(range(0)(10)).toEqual([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]) +
+ +
+ +TypeScript test + +```typescript +import {pickAll} from 'rambda' + +interface Input { + a: string, + b: number, + c: number, + d: number, +} +interface Output { + a?: string, + c?: number, +} +const input = {a: 'foo', b: 2, c: 3, d: 4} + +describe('R.pickAll with array as props input', () => { + it('without passing type', () => { + const result = pickAll(['a', 'c'], input) + result.a // $ExpectType string + result.c // $ExpectType number + }) + it('without passing type + curry', () => { + const result = pickAll(['a', 'c'])(input) + result // $ExpectType unknown + }) + it('explicitly passing types', () => { + const result = pickAll(['a', 'c'], input) + result.a // $ExpectType string | undefined + result.c // $ExpectType number | undefined + }) +}) + +describe('R.pickAll with string as props input', () => { + it('without passing type', () => { + const result = pickAll('a,c', input) + result // $ExpectType unknown + }) + it('without passing type + curry', () => { + const result = pickAll('a,c')(input) + result // $ExpectType unknown + }) + it('explicitly passing types', () => { + const result = pickAll('a,c', input) + result.a // $ExpectType string | undefined + result.c // $ExpectType number | undefined + }) + it('explicitly passing types + curry', () => { + const result = pickAll('a,c')(input) + result.a // $ExpectType string | undefined + result.c // $ExpectType number | undefined + }) }) ```
-
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pickAll) -TypeScript test +### pickBy -```typescript -import {range} from 'rambda' +Try this R.pickBy example in Rambda REPL -describe('R.range', () => { - it('happy', () => { - const result = range(1, 4) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pickBy) - result // $ExpectType number[] - }) - it('curried', () => { - const result = range(1)(4) +### pipe - result // $ExpectType number[] - }) -}) -``` +It performs left-to-right function composition. -
+Try this R.pipe example in Rambda REPL -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#range) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pipe) -### reduce +### pluck ```typescript -reduce(reducer: (prev: TResult, current: T, i: number) => TResult, initialValue: TResult, list: T[]): TResult +pluck(property: K, list: T[]): T[K][] ``` -> :boom: It passes index of the list as third argument to `reducer` function. - -```javascript -const list = [1, 2, 3] -const initialValue = 10 -const reducer = (prev, current) => prev * current - -const result = R.reduce(reducer, initialValue, list) -// => 60 -``` +It returns list of the values of `property` taken from the all objects inside `list`. -Try this R.reduce example in Rambda REPL +Try this R.pluck example in Rambda REPL
All TypeScript definitions ```typescript -reduce(reducer: (prev: TResult, current: T, i: number) => TResult, initialValue: TResult, list: T[]): TResult; -reduce(reducer: (prev: TResult, current: T) => TResult, initialValue: TResult, list: T[]): TResult; -reduce(reducer: (prev: TResult, current: T, i: number) => TResult): (initialValue: TResult, list: T[]) => TResult; -reduce(reducer: (prev: TResult, current: T, i: number) => TResult, initialValue: TResult): (list: T[]) => TResult; +pluck(property: K, list: T[]): T[K][]; +pluck(property: number, list: { [k: number]: T }[]): T[]; +pluck

(property: P): (list: Record[]) => T[]; +pluck(property: number): (list: { [k: number]: T }[]) => T[]; ```

-R.reduce source +R.pluck source ```javascript -import { isArray } from './_internals/isArray.js' -import { curry } from './curry.js' +import { map } from './map.js' -class ReduceStopper{ - constructor(value){ - this.value = value - } -} +export function pluck(property, list){ + if (arguments.length === 1) return _list => pluck(property, _list) -export function reduceFn( - reducer, acc, list -){ - if (list == null){ - return acc - } - if (!isArray(list)){ - throw new TypeError('reduce: list must be array or iterable') - } - let index = 0 - const len = list.length + const willReturn = [] - while (index < len){ - acc = reducer( - acc, list[ index ], index, list - ) - if (acc instanceof ReduceStopper){ - return acc.value + map(x => { + if (x[ property ] !== undefined){ + willReturn.push(x[ property ]) } - index++ - } + }, list) - return acc + return willReturn } - -export const reduce = curry(reduceFn) -export const reduceStopper = value => new ReduceStopper(value) ```
@@ -22115,74 +11486,23 @@ export const reduceStopper = value => new ReduceStopper(value) Tests ```javascript -import { add } from './add.js' -import { concat } from './concat.js' -import { reduce, reduceStopper } from './reduce.js' - -const reducer = ( - prev, current, i -) => { - expect(i).toBeNumber() - - return prev + current -} -const initialValue = 1 -const list = [ 1, 2, 3 ] -const ERROR = 'reduce: list must be array or iterable' +import { pluck } from './pluck.js' test('happy', () => { - expect(reduce( - reducer, initialValue, list - )).toBe(7) -}) - -test('with object as iterable', () => { - expect(() => - reduce( - reducer, initialValue, { - a : 1, - b : 2, - } - )).toThrowWithMessage(TypeError, ERROR) + expect(pluck('a')([ { a : 1 }, { a : 2 }, { b : 1 } ])).toEqual([ 1, 2 ]) }) -test('with undefined as iterable', () => { - expect(() => reduce( - reducer, 0, {} - )).toThrowWithMessage(TypeError, ERROR) +test('with undefined', () => { + expect(pluck(undefined)([ { a : 1 }, { a : 2 }, { b : 1 } ])).toEqual([ ]) }) -test('with reduceStopper', () => { - let maxIndex - const reducer = ( - prev, current, i - ) => { - maxIndex = i - - return current === 2 ? reduceStopper(current) : prev - } - expect(reduce( - reducer, initialValue, list - )).toBe(2) - expect(maxIndex).toBe(1) -}) - -test('returns the accumulator for a null list', () => { - expect(reduce( - add, 0, null - )).toBe(0) - expect(reduce( - concat, [], null - )).toEqual([]) -}) +test('with number', () => { + const input = [ + [ 1, 2 ], + [ 3, 4 ], + ] -test('returns the accumulator for an undefined list', () => { - expect(reduce( - add, 0, undefined - )).toBe(0) - expect(reduce( - concat, [], undefined - )).toEqual([]) + expect(pluck(0, input)).toEqual([ 1, 3 ]) }) ``` @@ -22193,333 +11513,77 @@ test('returns the accumulator for an undefined list', () => { TypeScript test ```typescript -import {reduce, reduceStopper} from 'rambda' - -describe('R.reduce', () => { - it('happy', () => { - const result = reduce( - (acc, elem) => { - acc // $ExpectType number - elem // $ExpectType number - return acc + elem - }, - 1, - [1, 2, 3] - ) - - result // $ExpectType number - }) - - it('with two types', () => { - const result = reduce( - (acc, elem) => { - acc // $ExpectType string - elem // $ExpectType number - - return `${acc}${elem}` - }, - 'foo', - [1, 2, 3] - ) - - result // $ExpectType string - }) - - it('with index', () => { - const result = reduce( - (acc, elem, i) => { - acc // $ExpectType number - elem // $ExpectType number - i // $ExpectType number - return acc + elem - }, - 1, - [1, 2, 3] - ) - - result // $ExpectType number - }) - - it('with index, curried', () => { - const result = reduce( - (acc, elem, i) => { - acc // $ExpectType number - elem // $ExpectType number - i // $ExpectType number - return acc + elem - }, - 1, - )([1, 2, 3]) - - result // $ExpectType number - }) - - it('using `reduceStopper` to stop the loop', () => { - const result = reduce( - (acc, elem, i) => { - acc // $ExpectType number - elem // $ExpectType number - i // $ExpectType number - return acc + elem > 1 ? reduceStopper(elem) : acc - }, - 1, - [1, 2, 3] - ) - - result // $ExpectType number - }) - - it('fallback', () => { - const result = reduce( - (acc, val) => { - acc // $ExpectType number - return acc + val - }, - 1, - [1, 2, 3] - ) - - result // $ExpectType number - }) - - it('fallback with index', () => { - const result = reduce( - (acc, val, i) => { - acc // $ExpectType number - i // $ExpectType number - return acc + val - }, - 1, - [1, 2, 3] - ) +import {pluck} from 'rambda' - result // $ExpectType number +describe('R.pluck', () => { + it('with object', () => { + interface ListMember { + a: number, + b: string, + } + const input: ListMember[] = [ + {a: 1, b: 'foo'}, + {a: 2, b: 'bar'}, + ] + const resultA = pluck('a', input) + const resultB = pluck('b')(input) + resultA // $ExpectType number[] + resultB // $ExpectType string[] }) - it('fallback with two types', () => { - const result = reduce( - (acc, val) => { - acc // $ExpectType string - return acc + val - }, - 'foo', - [1, 2, 3] - ) - - result // $ExpectType string + it('with array', () => { + const input = [ + [1, 2], + [3, 4], + [5, 6], + ] + const result = pluck(0, input) + const resultCurry = pluck(0)(input) + result // $ExpectType number[] + resultCurry // $ExpectType number[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#reduce) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pluck) -### reduceBy +### prepend ```typescript -reduceBy( - valueFn: (acc: TResult, elem: T) => TResult, -): (a: TResult, b: (elem: T) => string, c: T[]) => { [index: string]: TResult } -export function reduceBy( - valueFn: (acc: TResult, elem: T) => TResult, - acc: TResult, -): (a: (elem: T) => string, b: T[]) => { [index: string]: TResult } -export function reduceBy( - valueFn: (acc: TResult, elem: T) => TResult, - acc: TResult, - keyFn: (elem: T) => string, -): (list: T[]) => { [index: string]: TResult } +prepend(xToPrepend: T, iterable: T[]): T[] ``` -```javascript -const result = R.reduceBy( - (acc, elem) => acc + elem, - 0, - x => x > 2 ? 'big' : 'small', - [1, 2, 3, 4, 5] -) -// => { big: 12, small: 3 } -``` +It adds element `x` at the beginning of `list`. -Try this R.reduceBy example in Rambda REPL +Try this R.prepend example in Rambda REPL
All TypeScript definitions ```typescript -reduceBy( - valueFn: (acc: TResult, elem: T) => TResult, -): (a: TResult, b: (elem: T) => string, c: T[]) => { [index: string]: TResult } -reduceBy( - valueFn: (acc: TResult, elem: T) => TResult, - acc: TResult, -): (a: (elem: T) => string, b: T[]) => { [index: string]: TResult } -reduceBy( - valueFn: (acc: TResult, elem: T) => TResult, - acc: TResult, - keyFn: (elem: T) => string, -): (list: T[]) => { [index: string]: TResult }; -reduceBy( - valueFn: (acc: TResult, elem: T) => TResult, - acc: TResult, - keyFn: (elem: T) => string, - list: T[], -): { [index: string]: TResult }; -``` - -
- -
- -R.reduceBy source - -```javascript -import { clone } from './clone.js' -import { curry } from './curry.js' -import { has } from './has.js' -import { reduce } from './reduce.js' - -function reduceByFunction( - valueFn, valueAcc, keyFn, acc, elt -){ - const key = keyFn(elt) - const value = valueFn(has(key, acc) ? acc[ key ] : clone(valueAcc), elt) - - acc[ key ] = value - - return acc -} - -export function reduceByFn( - valueFn, valueAcc, keyFn, list -){ - return reduce( - (acc, elt) => reduceByFunction( - valueFn, valueAcc, keyFn, acc, elt - ), - {}, - list - ) -} - -export const reduceBy = curry(reduceByFn) +prepend(xToPrepend: T, iterable: T[]): T[]; +prepend(xToPrepend: T, iterable: IsFirstSubtypeOfSecond[]) : U[]; +prepend(xToPrepend: T): (iterable: IsFirstSubtypeOfSecond[]) => U[]; +prepend(xToPrepend: T): (iterable: T[]) => T[]; ```
-Tests +R.prepend source ```javascript -import { reduceBy } from './reduceBy.js' -import { prop } from './prop.js' - -const byType = prop('type') -const sumValues = function (acc, obj){ - return acc + obj.val -} - -const grade = function (score){ - return score < 65 ? - 'F' : - score < 70 ? - 'D' : - score < 80 ? - 'C' : - score < 90 ? - 'B' : - 'A' -} - -const byGrade = function (student){ - return grade(student.score || 0) -} - -test('splits the list into groups according to the grouping function', () => { - const collectNames = function (acc, student){ - return acc.concat(student.name) - } - expect(reduceBy( - collectNames, [], byGrade, getStudents() - )).toEqual({ - A : [ 'Dianne', 'Gillian' ], - B : [ 'Abby', 'Chris', 'Irene' ], - C : [ 'Brad', 'Hannah' ], - D : [ 'Fred', 'Jack' ], - F : [ 'Eddy' ], - }) -}) - -test('splits the list into mutation-free groups', () => { - const collectNames = function (acc, student){ - acc.push(student.name) - - return acc - } - expect(reduceBy( - collectNames, [], byGrade, getStudents() - )).toEqual({ - A : [ 'Dianne', 'Gillian' ], - B : [ 'Abby', 'Chris', 'Irene' ], - C : [ 'Brad', 'Hannah' ], - D : [ 'Fred', 'Jack' ], - F : [ 'Eddy' ], - }) -}) - -test('returns an empty object if given an empty array', () => { - expect(reduceBy( - sumValues, 0, byType, [] - )).toEqual({}) -}) - -function getStudents(){ - return [ - { - name : 'Abby', - score : 84, - }, - { - name : 'Brad', - score : 73, - }, - { - name : 'Chris', - score : 89, - }, - { - name : 'Dianne', - score : 99, - }, - { - name : 'Eddy', - score : 58, - }, - { - name : 'Fred', - score : 67, - }, - { - name : 'Gillian', - score : 91, - }, - { - name : 'Hannah', - score : 78, - }, - { - name : 'Irene', - score : 85, - }, - { - name : 'Jack', - score : 69, - }, - ] +export function prepend(x, input){ + if (arguments.length === 1) return _input => prepend(x, _input) + + if (typeof input === 'string') return [ x ].concat(input.split('')) + + return [ x ].concat(input) } ``` @@ -22527,94 +11591,61 @@ function getStudents(){
-TypeScript test +Tests -```typescript -import {reduceBy} from 'rambda' +```javascript +import { prepend } from './prepend.js' -test('R.reduceBy', () => { - interface Student { - name: string, - score: number, - } +test('happy', () => { + expect(prepend('yes', [ 'foo', 'bar', 'baz' ])).toEqual([ + 'yes', + 'foo', + 'bar', + 'baz', + ]) +}) - const reduceToNamesBy = reduceBy( - (acc: string[], student: Student) => acc.concat(student.name), - [] - ) - const students = [ - {name: 'Lucy', score: 92}, - {name: 'Drew', score: 85}, - {name: 'Bart', score: 62}, - ] - const result = reduceToNamesBy(student => { - const score = student.score - return score < 65 - ? 'F' - : score < 70 - ? 'D' - : score < 80 - ? 'C' - : score < 90 - ? 'B' - : 'A' - }, students) - result // $ExpectType { [index: string]: string[]; } +test('with empty list', () => { + expect(prepend('foo')([])).toEqual([ 'foo' ]) +}) + +test('with string instead of array', () => { + expect(prepend('foo')('bar')).toEqual([ 'foo', 'b', 'a', 'r' ]) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#reduceBy) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#prepend) -### reject +### product ```typescript -reject(predicate: Predicate, list: T[]): T[] -``` - -It has the opposite effect of `R.filter`. - -```javascript -const list = [1, 2, 3, 4] -const obj = {a: 1, b: 2} -const predicate = x => x > 1 - -const result = [ - R.reject(predicate, list), - R.reject(predicate, Record) -] -// => [[1], {a: 1}] +product(list: number[]): number ``` -Try this R.reject example in Rambda REPL +Try this R.product example in Rambda REPL
All TypeScript definitions ```typescript -reject(predicate: Predicate, list: T[]): T[]; -reject(predicate: Predicate): (list: T[]) => T[]; -reject(predicate: Predicate, obj: Dictionary): Dictionary; -reject(predicate: Predicate): (obj: Dictionary) => Dictionary; +product(list: number[]): number; ```
-R.reject source +R.product source ```javascript -import { filter } from './filter.js' - -export function reject(predicate, list){ - if (arguments.length === 1) return _list => reject(predicate, _list) +import { multiply } from './multiply.js' +import { reduce } from './reduce.js' - return filter(x => !predicate(x), list) -} +export const product = reduce(multiply, 1) ```
@@ -22624,25 +11655,15 @@ export function reject(predicate, list){ Tests ```javascript -import { reject } from './reject.js' - -const isOdd = n => n % 2 === 1 +import { product } from './product.js' -test('with array', () => { - expect(reject(isOdd)([ 1, 2, 3, 4 ])).toEqual([ 2, 4 ]) +test('happy', () => { + expect(product([ 2, 3, 4 ])).toBe(24) }) -test('with object', () => { - const obj = { - a : 1, - b : 2, - c : 3, - d : 4, - } - expect(reject(isOdd, obj)).toEqual({ - b : 2, - d : 4, - }) +test('bad input', () => { + expect(product([ null ])).toBe(0) + expect(product([])).toBe(1) }) ``` @@ -22653,92 +11674,62 @@ test('with object', () => { TypeScript test ```typescript -import {reject} from 'rambda' - -describe('R.reject with array', () => { - it('happy', () => { - const result = reject( - x => { - x // $ExpectType number - return x > 1 - }, - [1, 2, 3] - ) - result // $ExpectType number[] - }) - it('curried require explicit type', () => { - const result = reject(x => { - x // $ExpectType number - return x > 1 - })([1, 2, 3]) - result // $ExpectType number[] - }) -}) +import {product} from 'rambda' -describe('R.reject with objects', () => { +describe('R.product', () => { it('happy', () => { - const result = reject( - x => { - x // $ExpectType number + const result = product([1, 2, 3]) - return x > 1 - }, - {a: 1, b: 2} - ) - result // $ExpectType Dictionary - }) - it('curried require dummy type', () => { - const result = reject(x => { - return x > 1 - })({a: 1, b: 2}) - result // $ExpectType Dictionary + result // $ExpectType number }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#reject) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#product) -### removeIndex +### prop ```typescript -removeIndex(index: number, list: T[]): T[] +prop<_, P extends keyof never, T>(p: P, value: T): Prop ``` -It returns a copy of `list` input with removed `index`. +It returns the value of property `propToFind` in `obj`. -```javascript -const list = [1, 2, 3, 4] -const result = R.removeIndex(1, list) -// => [1, 3, 4] -``` +If there is no such property, it returns `undefined`. -Try this R.removeIndex example in Rambda REPL +Try this R.prop example in Rambda REPL
All TypeScript definitions ```typescript -removeIndex(index: number, list: T[]): T[]; -removeIndex(index: number): (list: T[]) => T[]; +prop<_, P extends keyof never, T>(p: P, value: T): Prop; +prop(p: keyof never, value: unknown): V; +prop<_, P extends keyof never>(p: P): (value: T) => Prop; +prop(p: keyof never): (value: unknown) => V; ```
-R.removeIndex source +R.prop source ```javascript -export function removeIndex(index, list){ - if (arguments.length === 1) return _list => removeIndex(index, _list) - if (index <= 0) return list.slice(1) - if (index >= list.length - 1) return list.slice(0, list.length - 1) +export function propFn(searchProperty, obj){ + if (!obj) return undefined - return [ ...list.slice(0, index), ...list.slice(index + 1) ] + return obj[ searchProperty ] +} + +export function prop(searchProperty, obj){ + if (arguments.length === 1) return _obj => prop(searchProperty, _obj) + + return propFn(searchProperty, obj) } ``` @@ -22749,23 +11740,14 @@ export function removeIndex(index, list){ Tests ```javascript -import { removeIndex } from './removeIndex.js' - -const list = [ 1, 2, 3, 4 ] +import { prop } from './prop.js' -test('first or before first index', () => { - expect(removeIndex(-2, list)).toEqual([ 2, 3, 4 ]) - expect(removeIndex(-2)(list)).toEqual([ 2, 3, 4 ]) -}) +test('prop', () => { + expect(prop('foo')({ foo : 'baz' })).toBe('baz') -test('last or after last index', () => { - expect(removeIndex(4, list)).toEqual([ 1, 2, 3 ]) - expect(removeIndex(10, list)).toEqual([ 1, 2, 3 ]) -}) + expect(prop('bar')({ foo : 'baz' })).toBeUndefined() -test('middle index', () => { - expect(removeIndex(1, list)).toEqual([ 1, 3, 4 ]) - expect(removeIndex(2, list)).toEqual([ 1, 2, 4 ]) + expect(prop('bar')(null)).toBeUndefined() }) ``` @@ -22776,63 +11758,97 @@ test('middle index', () => { TypeScript test ```typescript -import {removeIndex} from 'rambda' +import {prop} from 'rambda' -describe('R.removeIndex', () => { +describe('R.prop', () => { + interface Foo { + a: number, + b: string, + c?: number, + } + const obj: Foo = {a: 1, b: 'foo'} + + it('issue #553', () => { + const result = { + a: prop('a', obj), + b: prop('b', obj), + c: prop('c', obj), + } + const curriedResult = { + a: prop('a')(obj), + b: prop('b')(obj), + c: prop('c')(obj), + } + + result // $ExpectType { a: number; b: string; c: number | undefined; } + curriedResult // $ExpectType { a: number; b: string; c: number | undefined; } + }) +}) + +describe('with number as prop', () => { + const list = [1, 2, 3] + const index = 1 it('happy', () => { - const result = removeIndex(1, [1, 2, 3]) + const result = prop(index, list) - result // $ExpectType number[] + result // $ExpectType number }) - it('curried', () => { - const result = removeIndex(1)([1, 2, 3]) + it('curried require explicit type', () => { + const result = prop(index)(list) - result // $ExpectType number[] + result // $ExpectType number }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#removeIndex) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#prop) -### repeat +### propEq ```typescript -repeat(x: T): (timesToRepeat: number) => T[] +propEq(valueToMatch: any, propToFind: K, obj: Record): boolean ``` -```javascript -R.repeat('foo', 3) -// => ['foo', 'foo', 'foo'] -``` +It returns true if `obj` has property `propToFind` and its value is equal to `valueToMatch`. -Try this R.repeat example in Rambda REPL +Try this R.propEq example in Rambda REPL
All TypeScript definitions ```typescript -repeat(x: T): (timesToRepeat: number) => T[]; -repeat(x: T, timesToRepeat: number): T[]; +propEq(valueToMatch: any, propToFind: K, obj: Record): boolean; +propEq(valueToMatch: any, propToFind: K): (obj: Record) => boolean; +propEq(valueToMatch: any): { + (propToFind: K, obj: Record): boolean; + (propToFind: K): (obj: Record) => boolean; +}; ```
-R.repeat source +R.propEq source ```javascript -export function repeat(x, timesToRepeat){ - if (arguments.length === 1){ - return _timesToRepeat => repeat(x, _timesToRepeat) - } +import { curry } from './curry.js' +import { equals } from './equals.js' +import { prop } from './prop.js' - return Array(timesToRepeat).fill(x) +function propEqFn( + valueToMatch, propToFind, obj +){ + if (!obj) return false + + return equals(valueToMatch, prop(propToFind, obj)) } + +export const propEq = curry(propEqFn) ```
@@ -22842,18 +11858,26 @@ export function repeat(x, timesToRepeat){ Tests ```javascript -import { repeat } from './repeat.js' - -test('repeat', () => { - expect(repeat('')(3)).toEqual([ '', '', '' ]) - expect(repeat('foo', 3)).toEqual([ 'foo', 'foo', 'foo' ]) - - const obj = {} - const arr = repeat(obj, 3) +import { BAR, FOO } from './_internals/testUtils.js' +import { propEq } from './propEq.js' - expect(arr).toEqual([ {}, {}, {} ]) +test('happy', () => { + const obj = { [ FOO ] : BAR } + expect(propEq(BAR, FOO)(obj)).toBeTrue() + expect(propEq(1, FOO)(obj)).toBeFalse() + expect(propEq(1)(FOO)(obj)).toBeFalse() + expect(propEq( + 1, 1, null + )).toBeFalse() +}) - expect(arr[ 0 ] === arr[ 1 ]).toBeTrue() +test('returns false if called with a null or undefined object', () => { + expect(propEq( + 'name', 'Abby', null + )).toBeFalse() + expect(propEq( + 'name', 'Abby', undefined + )).toBeFalse() }) ``` @@ -22864,70 +11888,100 @@ test('repeat', () => { TypeScript test ```typescript -import {repeat} from 'rambda' +import {propEq} from 'rambda' -describe('R.repeat', () => { +const property = 'foo' +const numberProperty = 1 +const value = 'bar' +const obj = {[property]: value} +const objWithNumberIndex = {[numberProperty]: value} + +describe('R.propEq', () => { it('happy', () => { - const result = repeat(4, 7) + const result = propEq(value, property, obj) + result // $ExpectType boolean + }) - result // $ExpectType number[] + it('number is property', () => { + const result = propEq(value, 1, objWithNumberIndex) + result // $ExpectType boolean }) - it('curried', () => { - const result = repeat(4)(7) - result // $ExpectType number[] + it('with optional property', () => { + interface MyType { + optional?: string | number, + } + + const myObject: MyType = {} + const valueToFind = '1111' + // @ts-expect-error + propEq(valueToFind, 'optional', myObject) + }) + + it('imported from @types/ramda', () => { + interface A { + foo: string | null, + } + const obj: A = { + foo: 'bar', + } + const value = '' + const result = propEq(value, 'foo')(obj) + result // $ExpectType boolean + + // @ts-expect-error + propEq(value, 'bar')(obj) }) }) ``` -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#repeat) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#propEq) -### replace +### propIs ```typescript -replace(strOrRegex: RegExp | string, replacer: RegExpReplacer, str: string): string +propIs(type: C, name: K, obj: any): obj is Record> ``` -It replaces `strOrRegex` found in `str` with `replacer`. - -```javascript -const strOrRegex = /o/g - -const result = R.replace(strOrRegex, '|0|', 'foo') -// => 'f|0||0|' -``` +It returns `true` if `property` of `obj` is from `target` type. -Try this R.replace example in Rambda REPL +Try this R.propIs example in Rambda REPL
All TypeScript definitions ```typescript -replace(strOrRegex: RegExp | string, replacer: RegExpReplacer, str: string): string; -replace(strOrRegex: RegExp | string, replacer: RegExpReplacer): (str: string) => string; -replace(strOrRegex: RegExp | string): (replacer: RegExpReplacer) => (str: string) => string; +propIs(type: C, name: K, obj: any): obj is Record>; +propIs(type: C, name: K, obj: any): obj is Record>; +propIs(type: C, name: K): (obj: any) => obj is Record>; +propIs(type: C, name: K): (obj: any) => obj is Record>; +propIs(type: C): { + (name: K, obj: any): obj is Record>; + (name: K): (obj: any) => obj is Record>; +}; ```
-R.replace source +R.propIs source ```javascript import { curry } from './curry.js' +import { is } from './is.js' -function replaceFn( - pattern, replacer, str +function propIsFn( + targetPrototype, property, obj ){ - return str.replace(pattern, replacer) + return is(targetPrototype, obj[ property ]) } -export const replace = curry(replaceFn) +export const propIs = curry(propIsFn) ```
@@ -22937,28 +11991,29 @@ export const replace = curry(replaceFn) Tests ```javascript -import { replace } from './replace.js' +import { propIs } from './propIs.js' -test('happy', () => { - expect(replace( - /\s/g, '|', 'foo bar baz' - )).toBe('foo|bar|baz') -}) +const obj = { + a : 1, + b : 'foo', +} -test('with function as replacer input', () => { - expect(replace( - /\s/g, - ( - match, offset, str - ) => { - expect(match).toBe(' ') - expect([ 3, 7 ].includes(offset)).toBeTrue() - expect(str).toBe('foo bar baz') +test('when true', () => { + expect(propIs( + Number, 'a', obj + )).toBeTrue() + expect(propIs( + String, 'b', obj + )).toBeTrue() +}) - return '|' - }, - 'foo bar baz' - )).toBe('foo|bar|baz') +test('when false', () => { + expect(propIs( + String, 'a', obj + )).toBeFalse() + expect(propIs( + Number, 'b', obj + )).toBeFalse() }) ``` @@ -22969,106 +12024,71 @@ test('with function as replacer input', () => { TypeScript test ```typescript -import {replace} from 'rambda' - -const str = 'foo bar foo' -const replacer = 'bar' - -describe('R.replace', () => { - it('happy', () => { - const result = replace(/foo/g, replacer, str) - - result // $ExpectType string - }) - it('with string as search pattern', () => { - const result = replace('foo', replacer, str) - - result // $ExpectType string - }) - it('with function as replacer', () => { - const result = replace('f(o)o', (m: string, p1: string, offset: number) => { - m // $ExpectType string - p1 // $ExpectType string - offset // $ExpectType number - return p1 - }, str) +import {propIs} from 'rambda' - result // $ExpectType string - }) -}) +const property = 'a' +const obj = {a: 1} -describe('R.replace - curried', () => { +describe('R.propIs', () => { it('happy', () => { - const result = replace(/foo/g, replacer)(str) - - result // $ExpectType string - }) - it('with string as search pattern', () => { - const result = replace('foo', replacer)(str) - - result // $ExpectType string + const result = propIs(Number, property, obj) + result // $ExpectType boolean }) - it('with function as replacer', () => { - const result = replace('f(o)o')((m: string, p1: string, offset: number) => { - m // $ExpectType string - p1 // $ExpectType string - offset // $ExpectType number - return p1 - })(str) - result // $ExpectType string + it('curried', () => { + const result = propIs(Number, property)(obj) + result // $ExpectType boolean }) }) ``` -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#replace) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#propIs) -### reverse +### propOr ```typescript -reverse(input: T[]): T[] +propOr(defaultValue: T, property: P, obj: Partial> | undefined): T ``` -It returns a reversed copy of list or string `input`. - -```javascript -const result = [ - R.reverse('foo'), - R.reverse([1, 2, 3]) -] -// => ['oof', [3, 2, 1] -``` +It returns either `defaultValue` or the value of `property` in `obj`. -Try this R.reverse example in Rambda REPL +Try this R.propOr example in Rambda REPL
All TypeScript definitions ```typescript -reverse(input: T[]): T[]; -reverse(input: string): string; +propOr(defaultValue: T, property: P, obj: Partial> | undefined): T; +propOr(defaultValue: T, property: P): (obj: Partial> | undefined) => T; +propOr(defaultValue: T): { +

(property: P, obj: Partial> | undefined): T; +

(property: P): (obj: Partial> | undefined) => T; +} ```

-R.reverse source +R.propOr source ```javascript -export function reverse(listOrString) { - if (typeof listOrString === 'string') { - return listOrString.split('').reverse().join('') - } +import { curry } from './curry.js' +import { defaultTo } from './defaultTo.js' - const clone = listOrString.slice() +function propOrFn( + defaultValue, property, obj +){ + if (!obj) return defaultValue - return clone.reverse() + return defaultTo(defaultValue, obj[ property ]) } + +export const propOr = curry(propOrFn) ```
@@ -23078,22 +12098,27 @@ export function reverse(listOrString) { Tests ```javascript -import {reverse} from './reverse.js' - -test('happy', () => { - expect(reverse([1, 2, 3])).toEqual([3, 2, 1]) -}) +import { propOr } from './propOr.js' -test('with string', () => { - expect(reverse('baz')).toBe('zab') +test('propOr (result)', () => { + const obj = { a : 1 } + expect(propOr( + 'default', 'a', obj + )).toBe(1) + expect(propOr( + 'default', 'notExist', obj + )).toBe('default') + expect(propOr( + 'default', 'notExist', null + )).toBe('default') }) -test("it doesn't mutate", () => { - const arr = [1, 2, 3] - - expect(reverse(arr)).toEqual([3, 2, 1]) - - expect(arr).toEqual([1, 2, 3]) +test('propOr (currying)', () => { + const obj = { a : 1 } + expect(propOr('default')('a', obj)).toBe(1) + expect(propOr('default', 'a')(obj)).toBe(1) + expect(propOr('default')('notExist', obj)).toBe('default') + expect(propOr('default', 'notExist')(obj)).toBe('default') }) ``` @@ -23104,76 +12129,77 @@ test("it doesn't mutate", () => { TypeScript test ```typescript -import {reverse} from 'rambda' +import {propOr} from 'rambda' -const list = [1, 2, 3, 4, 5] +const obj = {foo: 'bar'} +const property = 'foo' +const fallback = 'fallback' -describe('R.reverse', () => { +describe('R.propOr', () => { it('happy', () => { - const result = reverse(list) - result // $ExpectType number[] + const result = propOr(fallback, property, obj) + result // $ExpectType string + }) + it('curry 1', () => { + const result = propOr(fallback)(property, obj) + result // $ExpectType string + }) + it('curry 2', () => { + const result = propOr(fallback, property)(obj) + result // $ExpectType string + }) + it('curry 3', () => { + const result = propOr(fallback)(property)(obj) + result // $ExpectType string }) }) ``` -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#reverse) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#propOr) -### set +### props ```typescript -set(lens: Lens): { - (a: A): (obj: S) => S - (a: A, obj: S): S -} +props

(propsToPick: P[], obj: Record): T[] ``` -It returns a copied **Object** or **Array** with modified `lens` focus set to `replacer` value. - -```javascript -const input = {x: 1, y: 2} -const xLens = R.lensProp('x') - -const result = [ - R.set(xLens, 4, input), - R.set(xLens, 8, input) -] -// => [{x: 4, y: 2}, {x: 8, y: 2}] -``` +It takes list with properties `propsToPick` and returns a list with property values in `obj`. -Try this R.set example in Rambda REPL +Try this R.props example in Rambda REPL

All TypeScript definitions ```typescript -set(lens: Lens): { - (a: A): (obj: S) => S - (a: A, obj: S): S -}; -set(lens: Lens, a: A): (obj: S) => S; -set(lens: Lens, a: A, obj: S): S; +props

(propsToPick: P[], obj: Record): T[]; +props

(propsToPick: P[]): (obj: Record) => T[]; +props

(propsToPick: P[]): (obj: Record) => T[]; ```

-R.set source +R.props source ```javascript -import {always} from './always.js' -import {curry} from './curry.js' -import {over} from './over.js' +import { isArray } from './_internals/isArray.js' +import { mapArray } from './map.js' -function setFn(lens, replacer, x) { - return over(lens, always(replacer), x) -} +export function props(propsToPick, obj){ + if (arguments.length === 1){ + return _obj => props(propsToPick, _obj) + } + if (!isArray(propsToPick)){ + throw new Error('propsToPick is not a list') + } -export const set = curry(setFn) + return mapArray(prop => obj[ prop ], propsToPick) +} ```
@@ -23183,111 +12209,95 @@ export const set = curry(setFn) Tests ```javascript -import {assoc} from './assoc.js' -import {lens} from './lens.js' -import {lensIndex} from './lensIndex.js' -import {lensPath} from './lensPath.js' -import {prop} from './prop.js' -import {set} from './set.js' +import { props } from './props.js' -const testObject = { - foo: 'bar', - baz: { - a: 'x', - b: 'y', - }, +const obj = { + a : 1, + b : 2, } +const propsToPick = [ 'a', 'c' ] -test('assoc lens', () => { - const assocLens = lens(prop('foo'), assoc('foo')) - const result = set(assocLens, 'FOO', testObject) - const expected = { - ...testObject, - foo: 'FOO', - } - expect(result).toEqual(expected) +test('happy', () => { + const result = props(propsToPick, obj) + expect(result).toEqual([ 1, undefined ]) }) -test('path lens', () => { - const pathLens = lensPath('baz.a') - const result = set(pathLens, 'z', testObject) - const expected = { - ...testObject, - baz: { - a: 'z', - b: 'y', - }, - } - expect(result).toEqual(expected) +test('curried', () => { + const result = props(propsToPick)(obj) + expect(result).toEqual([ 1, undefined ]) }) -test('index lens', () => { - const indexLens = lensIndex(0) - - const result = set(indexLens, 3, [1, 2]) - expect(result).toEqual([3, 2]) +test('wrong input', () => { + expect(() => props(null)(obj)).toThrowErrorMatchingInlineSnapshot('"propsToPick is not a list"') }) ``` -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#set) +
-### slice +TypeScript test ```typescript +import {props} from 'rambda' -slice(from: number, to: number, input: string): string +const obj = {a: 1, b: 2} + +describe('R.props', () => { + it('happy', () => { + const result = props(['a', 'b'], obj) + + result // $ExpectType number[] + }) + it('curried', () => { + const result = props(['a', 'b'])(obj) + + result // $ExpectType number[] + }) +}) ``` -```javascript -const list = [0, 1, 2, 3, 4, 5] -const str = 'FOO_BAR' -const from = 1 -const to = 4 +
-const result = [ - R.slice(from, to, str), - R.slice(from, to, list) -] -// => ['OO_', [1, 2, 3]] +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#props) + +### propSatisfies + +```typescript + +propSatisfies(predicate: Predicate, property: string, obj: Record): boolean ``` -Try this R.slice example in Rambda REPL +It returns `true` if the object property satisfies a given predicate. + +Try this R.propSatisfies example in Rambda REPL
All TypeScript definitions ```typescript -slice(from: number, to: number, input: string): string; -slice(from: number, to: number, input: T[]): T[]; -slice(from: number, to: number): { - (input: string): string; - (input: T[]): T[]; -}; -slice(from: number): { - (to: number, input: string): string; - (to: number, input: T[]): T[]; -}; +propSatisfies(predicate: Predicate, property: string, obj: Record): boolean; +propSatisfies(predicate: Predicate, property: string): (obj: Record) => boolean; ```
-R.slice source +R.propSatisfies source ```javascript import { curry } from './curry.js' +import { prop } from './prop.js' -function sliceFn( - from, to, list +function propSatisfiesFn( + predicate, property, obj ){ - return list.slice(from, to) + return predicate(prop(property, obj)) } -export const slice = curry(sliceFn) +export const propSatisfies = curry(propSatisfiesFn) ```
@@ -23297,24 +12307,18 @@ export const slice = curry(sliceFn) Tests ```javascript -import { slice } from './slice.js' +import { propSatisfies } from './propSatisfies.js' -test('slice', () => { - expect(slice( - 1, 3, [ 'a', 'b', 'c', 'd' ] - )).toEqual([ 'b', 'c' ]) - expect(slice( - 1, Infinity, [ 'a', 'b', 'c', 'd' ] - )).toEqual([ 'b', 'c', 'd' ]) - expect(slice( - 0, -1, [ 'a', 'b', 'c', 'd' ] - )).toEqual([ 'a', 'b', 'c' ]) - expect(slice( - -3, -1, [ 'a', 'b', 'c', 'd' ] - )).toEqual([ 'b', 'c' ]) - expect(slice( - 0, 3, 'ramda' - )).toBe('ram') +const obj = { a : 1 } + +test('when true', () => { + expect(propSatisfies( + x => x > 0, 'a', obj + )).toBeTrue() +}) + +test('when false', () => { + expect(propSatisfies(x => x < 0, 'a')(obj)).toBeFalse() }) ``` @@ -23325,78 +12329,72 @@ test('slice', () => { TypeScript test ```typescript -import {slice} from 'rambda' +import {propSatisfies} from 'rambda' -const list = [1, 2, 3, 4, 5] +const obj = {a: 1} -describe('R.slice', () => { +describe('R.propSatisfies', () => { it('happy', () => { - const result = slice(1, 3, list) - result // $ExpectType number[] + const result = propSatisfies(x => x > 0, 'a', obj) + + result // $ExpectType boolean }) - it('curried', () => { - const result = slice(1, 3)(list) - result // $ExpectType number[] + it('curried requires explicit type', () => { + const result = propSatisfies(x => x > 0, 'a')(obj) + + result // $ExpectType boolean }) }) ``` -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#slice) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#propSatisfies) -### sort +### range ```typescript -sort(sortFn: (a: T, b: T) => number, list: T[]): T[] +range(startInclusive: number, endExclusive: number): number[] ``` -It returns copy of `list` sorted by `sortFn` function, where `sortFn` needs to return only `-1`, `0` or `1`. - -```javascript -const list = [ - {a: 2}, - {a: 3}, - {a: 1} -] -const sortFn = (x, y) => { - return x.a > y.a ? 1 : -1 -} - -const result = R.sort(sortFn, list) -const expected = [ - {a: 1}, - {a: 2}, - {a: 3} -] -// => `result` is equal to `expected` -``` +It returns list of numbers between `startInclusive` to `endExclusive` markers. -Try this R.sort example in Rambda REPL +Try this R.range example in Rambda REPL
All TypeScript definitions ```typescript -sort(sortFn: (a: T, b: T) => number, list: T[]): T[]; -sort(sortFn: (a: T, b: T) => number): (list: T[]) => T[]; +range(startInclusive: number, endExclusive: number): number[]; +range(startInclusive: number): (endExclusive: number) => number[]; ```
-R.sort source +R.range source ```javascript -import { cloneList } from './_internals/cloneList.js' +export function range(start, end){ + if (arguments.length === 1) return _end => range(start, _end) -export function sort(sortFn, list){ - if (arguments.length === 1) return _list => sort(sortFn, _list) + if (Number.isNaN(Number(start)) || Number.isNaN(Number(end))){ + throw new TypeError('Both arguments to range must be numbers') + } - return cloneList(list).sort(sortFn) + if (end < start) return [] + + const len = end - start + const willReturn = Array(len) + + for (let i = 0; i < len; i++){ + willReturn[ i ] = start + i + } + + return willReturn } ``` @@ -23407,22 +12405,25 @@ export function sort(sortFn, list){ Tests ```javascript -import { sort } from './sort.js' - -const fn = (a, b) => a > b ? 1 : -1 +import { range } from './range.js' -test('sort', () => { - expect(sort((a, b) => a - b)([ 2, 3, 1 ])).toEqual([ 1, 2, 3 ]) +test('happy', () => { + expect(range(0, 10)).toEqual([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]) }) -test('it doesn\'t mutate', () => { - const list = [ 'foo', 'bar', 'baz' ] +test('end range is bigger than start range', () => { + expect(range(7, 3)).toEqual([]) + expect(range(5, 5)).toEqual([]) +}) - expect(sort(fn, list)).toEqual([ 'bar', 'baz', 'foo' ]) +test('with bad input', () => { + const throwMessage = 'Both arguments to range must be numbers' + expect(() => range('a', 6)).toThrowWithMessage(Error, throwMessage) + expect(() => range(6, 'z')).toThrowWithMessage(Error, throwMessage) +}) - expect(list[ 0 ]).toBe('foo') - expect(list[ 1 ]).toBe('bar') - expect(list[ 2 ]).toBe('baz') +test('curry', () => { + expect(range(0)(10)).toEqual([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]) }) ``` @@ -23433,21 +12434,17 @@ test('it doesn\'t mutate', () => { TypeScript test ```typescript -import {sort} from 'rambda' - -const list = [3, 0, 5, 2, 1] - -function sortFn(a: number, b: number): number { - return a > b ? 1 : -1 -} +import {range} from 'rambda' -describe('R.sort', () => { +describe('R.range', () => { it('happy', () => { - const result = sort(sortFn, list) + const result = range(1, 4) + result // $ExpectType number[] }) it('curried', () => { - const result = sort(sortFn)(list) + const result = range(1)(4) + result // $ExpectType number[] }) }) @@ -23455,68 +12452,55 @@ describe('R.sort', () => {
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sort) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#range) -### sortBy +### reduce -```typescript +Try this R.reduce example in Rambda REPL -sortBy(sortFn: (a: T) => Ord, list: T[]): T[] -``` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#reduce) -It returns copy of `list` sorted by `sortFn` function, where `sortFn` function returns a value to compare, i.e. it doesn't need to return only `-1`, `0` or `1`. +### reduceBy -```javascript -const list = [ - {a: 2}, - {a: 3}, - {a: 1} -] -const sortFn = x => x.a +Try this R.reduceBy example in Rambda REPL -const result = R.sortBy(sortFn, list) -const expected = [ - {a: 1}, - {a: 2}, - {a: 3} -] -// => `result` is equal to `expected` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#reduceBy) + +### reject + +```typescript + +reject(predicate: Predicate, list: T[]): T[] ``` -Try this R.sortBy example in Rambda REPL +It has the opposite effect of `R.filter`. + +Try this R.reject example in Rambda REPL
All TypeScript definitions ```typescript -sortBy(sortFn: (a: T) => Ord, list: T[]): T[]; -sortBy(sortFn: (a: T) => Ord): (list: T[]) => T[]; -sortBy(sortFn: (a: any) => Ord): (list: T[]) => T[]; +reject(predicate: Predicate, list: T[]): T[]; +reject(predicate: Predicate): (list: T[]) => T[]; +reject(predicate: Predicate, obj: Dictionary): Dictionary; +reject(predicate: Predicate): (obj: Dictionary) => Dictionary; ```
-R.sortBy source +R.reject source ```javascript -import { cloneList } from './_internals/cloneList.js' - -export function sortBy(sortFn, list){ - if (arguments.length === 1) return _list => sortBy(sortFn, _list) - - const clone = cloneList(list) - - return clone.sort((a, b) => { - const aSortResult = sortFn(a) - const bSortResult = sortFn(b) +import { filter } from './filter.js' - if (aSortResult === bSortResult) return 0 +export function reject(predicate, list){ + if (arguments.length === 1) return _list => reject(predicate, _list) - return aSortResult < bSortResult ? -1 : 1 - }) + return filter(x => !predicate(x), list) } ``` @@ -23527,36 +12511,25 @@ export function sortBy(sortFn, list){ Tests ```javascript -import { compose } from './compose.js' -import { prop } from './prop.js' -import { sortBy } from './sortBy.js' -import { toLower } from './toLower.js' +import { reject } from './reject.js' -test('happy', () => { - const input = [ { a : 2 }, { a : 1 }, { a : 1 }, { a : 3 } ] - const expected = [ { a : 1 }, { a : 1 }, { a : 2 }, { a : 3 } ] +const isOdd = n => n % 2 === 1 - const result = sortBy(x => x.a)(input) - expect(result).toEqual(expected) +test('with array', () => { + expect(reject(isOdd)([ 1, 2, 3, 4 ])).toEqual([ 2, 4 ]) }) -test('with compose', () => { - const alice = { - name : 'ALICE', - age : 101, - } - const bob = { - name : 'Bob', - age : -10, - } - const clara = { - name : 'clara', - age : 314.159, +test('with object', () => { + const obj = { + a : 1, + b : 2, + c : 3, + d : 4, } - const people = [ clara, bob, alice ] - const sortByNameCaseInsensitive = sortBy(compose(toLower, prop('name'))) - - expect(sortByNameCaseInsensitive(people)).toEqual([ alice, bob, clara ]) + expect(reject(isOdd, obj)).toEqual({ + b : 2, + d : 4, + }) }) ``` @@ -23567,322 +12540,174 @@ test('with compose', () => { TypeScript test ```typescript -import {sortBy, pipe} from 'rambda' - -interface Input { - a: number, -} - -describe('R.sortBy', () => { - it('passing type to sort function', () => { - function fn(x: any): number { - return x.a - } - function fn2(x: Input): number { - return x.a - } - - const input = [{a: 2}, {a: 1}, {a: 0}] - const result = sortBy(fn, input) - const curriedResult = sortBy(fn2)(input) +import {reject} from 'rambda' - result // $ExpectType { a: number; }[] - curriedResult // $ExpectType Input[] - result[0].a // $ExpectType number - curriedResult[0].a // $ExpectType number +describe('R.reject with array', () => { + it('happy', () => { + const result = reject( + x => { + x // $ExpectType number + return x > 1 + }, + [1, 2, 3] + ) + result // $ExpectType number[] }) - it('passing type to sort function and list', () => { - function fn(x: Input): number { - return x.a - } + it('curried require explicit type', () => { + const result = reject(x => { + x // $ExpectType number + return x > 1 + })([1, 2, 3]) + result // $ExpectType number[] + }) +}) - const input: Input[] = [{a: 2}, {a: 1}, {a: 0}] - const result = sortBy(fn, input) - const curriedResult = sortBy(fn)(input) +describe('R.reject with objects', () => { + it('happy', () => { + const result = reject( + x => { + x // $ExpectType number - result // $ExpectType Input[] - curriedResult // $ExpectType Input[] - result[0].a // $ExpectType number + return x > 1 + }, + {a: 1, b: 2} + ) + result // $ExpectType Dictionary }) - it('with R.pipe', () => { - interface Obj { - value: number, - } - const fn = pipe(sortBy(x => x.value)) - - const result = fn([{value: 1}, {value: 2}]) - result // $ExpectType Obj[] + it('curried require dummy type', () => { + const result = reject(x => { + return x > 1 + })({a: 1, b: 2}) + result // $ExpectType Dictionary }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortBy) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#reject) -### sortWith +### removeIndex ```typescript -sortWith(fns: Array<(a: T, b: T) => number>): (list: T[]) => T[] +removeIndex(index: number, list: T[]): T[] ``` -```javascript -const result = R.sortWith([ - (a, b) => a.a === b.a ? 0 : a.a > b.a ? 1 : -1, - (a, b) => a.b === b.b ? 0 : a.b > b.b ? 1 : -1, -], [ - {a: 1, b: 2}, - {a: 2, b: 1}, - {a: 2, b: 2}, - {a: 1, b: 1}, -]) -const expected = [ - {a: 1, b: 1}, - {a: 1, b: 2}, - {a: 2, b: 1}, - {a: 2, b: 2}, -] -// => `result` is equal to `expected` -``` +It returns a copy of `list` input with removed `index`. -Try this R.sortWith example in Rambda REPL +Try this R.removeIndex example in Rambda REPL
All TypeScript definitions -```typescript -sortWith(fns: Array<(a: T, b: T) => number>): (list: T[]) => T[]; -sortWith(fns: Array<(a: T, b: T) => number>, list: T[]): T[]; +```typescript +removeIndex(index: number, list: T[]): T[]; +removeIndex(index: number): (list: T[]) => T[]; +``` + +
+ +
+ +R.removeIndex source + +```javascript +export function removeIndex(index, list){ + if (arguments.length === 1) return _list => removeIndex(index, _list) + if (index <= 0) return list.slice(1) + if (index >= list.length - 1) return list.slice(0, list.length - 1) + + return [ ...list.slice(0, index), ...list.slice(index + 1) ] +} ```
-R.sortWith source +Tests ```javascript -function sortHelper( - a, b, listOfSortingFns -){ - let result = 0 - let i = 0 - while (result === 0 && i < listOfSortingFns.length){ - result = listOfSortingFns[ i ](a, b) - i += 1 - } - - return result -} +import { removeIndex } from './removeIndex.js' -export function sortWith(listOfSortingFns, list){ - if (arguments.length === 1) - return _list => sortWith(listOfSortingFns, _list) +const list = [ 1, 2, 3, 4 ] - if (Array.isArray(list) === false) - return [] +test('first or before first index', () => { + expect(removeIndex(-2, list)).toEqual([ 2, 3, 4 ]) + expect(removeIndex(-2)(list)).toEqual([ 2, 3, 4 ]) +}) - const clone = list.slice() - clone.sort((a, b) => sortHelper( - a, b, listOfSortingFns - )) +test('last or after last index', () => { + expect(removeIndex(4, list)).toEqual([ 1, 2, 3 ]) + expect(removeIndex(10, list)).toEqual([ 1, 2, 3 ]) +}) - return clone -} +test('middle index', () => { + expect(removeIndex(1, list)).toEqual([ 1, 3, 4 ]) + expect(removeIndex(2, list)).toEqual([ 1, 2, 4 ]) +}) ```
-Tests +TypeScript test -```javascript -import { ascend, prop } from '../rambda.js' -import { sortWith } from './sortWith.js' +```typescript +import {removeIndex} from 'rambda' -const albums = [ - { - artist : 'Rush', - genre : 'Rock', - score : 3, - title : 'A Farewell to Kings', - }, - { - artist : 'Dave Brubeck Quartet', - genre : 'Jazz', - score : 3, - title : 'Timeout', - }, - { - artist : 'Rush', - genre : 'Rock', - score : 5, - title : 'Fly By Night', - }, - { - artist : 'Daniel Barenboim', - genre : 'Baroque', - score : 3, - title : 'Goldberg Variations', - }, - { - artist : 'Glenn Gould', - genre : 'Baroque', - score : 3, - title : 'Art of the Fugue', - }, - { - artist : 'Leonard Bernstein', - genre : 'Romantic', - score : 4, - title : 'New World Symphony', - }, - { - artist : 'Don Byron', - genre : 'Jazz', - score : 5, - title : 'Romance with the Unseen', - }, - { - artist : 'Iron Maiden', - genre : 'Metal', - score : 2, - title : 'Somewhere In Time', - }, - { - artist : 'Danny Holt', - genre : 'Modern', - score : 1, - title : 'In Times of Desparation', - }, - { - artist : 'Various', - genre : 'Broadway', - score : 3, - title : 'Evita', - }, - { - artist : 'Nick Drake', - genre : 'Folk', - score : 1, - title : 'Five Leaves Left', - }, - { - artist : 'John Eliot Gardiner', - genre : 'Classical', - score : 4, - title : 'The Magic Flute', - }, -] +describe('R.removeIndex', () => { + it('happy', () => { + const result = removeIndex(1, [1, 2, 3]) -test('sorts by a simple property of the objects', () => { - const sortedAlbums = sortWith([ ascend(prop('title')) ], albums) - expect(sortedAlbums).toHaveLength(albums.length) - expect(sortedAlbums[ 0 ].title).toBe('A Farewell to Kings') - expect(sortedAlbums[ 11 ].title).toBe('Timeout') -}) - -test('sorts by multiple properties of the objects', () => { - const sortedAlbums = sortWith([ ascend(prop('score')), ascend(prop('title')) ], - albums) - expect(sortedAlbums).toHaveLength(albums.length) - expect(sortedAlbums[ 0 ].title).toBe('Five Leaves Left') - expect(sortedAlbums[ 1 ].title).toBe('In Times of Desparation') - expect(sortedAlbums[ 11 ].title).toBe('Romance with the Unseen') -}) - -test('sorts by 3 properties of the objects', () => { - const sortedAlbums = sortWith([ ascend(prop('genre')), ascend(prop('score')), ascend(prop('title')) ], - albums) - expect(sortedAlbums).toHaveLength(albums.length) - expect(sortedAlbums[ 0 ].title).toBe('Art of the Fugue') - expect(sortedAlbums[ 1 ].title).toBe('Goldberg Variations') - expect(sortedAlbums[ 11 ].title).toBe('New World Symphony') -}) - -test('sorts by multiple properties using ascend and descend', () => { - const sortedAlbums = sortWith([ ascend(prop('score')), ascend(prop('title')) ], - albums) - expect(sortedAlbums).toHaveLength(albums.length) - expect(sortedAlbums[ 0 ].title).toBe('Five Leaves Left') - expect(sortedAlbums[ 1 ].title).toBe('In Times of Desparation') - expect(sortedAlbums[ 11 ].title).toBe('Romance with the Unseen') -}) - -test('sorts only arrays not array-like object', () => { - const args = (function (){ - return arguments - })( - 'c', 'a', 'b' - ) - expect(sortWith([ ascend(prop('value')) ], args)).toEqual([]) -}) - -test('sorts only arrays not primitives', () => { - const result =sortWith([ - (a, b) => a.a === b.a ? 0 : a.a > b.a ? 1 : -1, - (a, b) => a.b === b.b ? 0 : a.b > b.b ? 1 : -1, - ], [ - {a: 1, b: 2}, - {a: 2, b: 1}, - {a: 2, b: 2}, - {a: 1, b: 1}, - ]) - const expected = [ - {a: 1, b: 1}, - {a: 1, b: 2}, - {a: 2, b: 1}, - {a: 2, b: 2}, - ] - expect(result).toEqual(expected) + result // $ExpectType number[] + }) + it('curried', () => { + const result = removeIndex(1)([1, 2, 3]) + + result // $ExpectType number[] + }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortWith) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#removeIndex) -### split +### repeat ```typescript -split(separator: string | RegExp): (str: string) => string[] -``` - -Curried version of `String.prototype.split` - -```javascript -const str = 'foo|bar|baz' -const separator = '|' -const result = R.split(separator, str) -// => [ 'foo', 'bar', 'baz' ] +repeat(x: T): (timesToRepeat: number) => T[] ``` -Try this R.split example in Rambda REPL +Try this R.repeat example in Rambda REPL
All TypeScript definitions ```typescript -split(separator: string | RegExp): (str: string) => string[]; -split(separator: string | RegExp, str: string): string[]; +repeat(x: T): (timesToRepeat: number) => T[]; +repeat(x: T, timesToRepeat: number): T[]; ```
-R.split source +R.repeat source ```javascript -export function split(separator, str){ - if (arguments.length === 1) return _str => split(separator, _str) +export function repeat(x, timesToRepeat){ + if (arguments.length === 1){ + return _timesToRepeat => repeat(x, _timesToRepeat) + } - return str.split(separator) + return Array(timesToRepeat).fill(x) } ``` @@ -23893,18 +12718,18 @@ export function split(separator, str){ Tests ```javascript -import { split } from './split.js' +import { repeat } from './repeat.js' -const str = 'foo|bar|baz' -const splitChar = '|' -const expected = [ 'foo', 'bar', 'baz' ] +test('repeat', () => { + expect(repeat('')(3)).toEqual([ '', '', '' ]) + expect(repeat('foo', 3)).toEqual([ 'foo', 'foo', 'foo' ]) -test('happy', () => { - expect(split(splitChar, str)).toEqual(expected) -}) + const obj = {} + const arr = repeat(obj, 3) -test('curried', () => { - expect(split(splitChar)(str)).toEqual(expected) + expect(arr).toEqual([ {}, {}, {} ]) + + expect(arr[ 0 ] === arr[ 1 ]).toBeTrue() }) ``` @@ -23915,87 +12740,63 @@ test('curried', () => { TypeScript test ```typescript -import {split} from 'rambda' - -const str = 'foo|bar|baz' -const splitChar = '|' +import {repeat} from 'rambda' -describe('R.split', () => { +describe('R.repeat', () => { it('happy', () => { - const result = split(splitChar, str) + const result = repeat(4, 7) - result // $ExpectType string[] + result // $ExpectType number[] }) it('curried', () => { - const result = split(splitChar)(str) + const result = repeat(4)(7) - result // $ExpectType string[] + result // $ExpectType number[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#split) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#repeat) -### splitAt +### replace ```typescript -splitAt(index: number, input: T[]): [T[], T[]] +replace(strOrRegex: RegExp | string, replacer: RegExpReplacer, str: string): string ``` -It splits string or array at a given index. - -```javascript -const list = [ 1, 2, 3 ] -const result = R.splitAt(2, list) -// => [[ 1, 2 ], [ 3 ]] -``` +It replaces `strOrRegex` found in `str` with `replacer`. -Try this R.splitAt example in Rambda REPL +Try this R.replace example in Rambda REPL
All TypeScript definitions ```typescript -splitAt(index: number, input: T[]): [T[], T[]]; -splitAt(index: number, input: string): [string, string]; -splitAt(index: number): { - (input: T[]): [T[], T[]]; - (input: string): [string, string]; -}; +replace(strOrRegex: RegExp | string, replacer: RegExpReplacer, str: string): string; +replace(strOrRegex: RegExp | string, replacer: RegExpReplacer): (str: string) => string; +replace(strOrRegex: RegExp | string): (replacer: RegExpReplacer) => (str: string) => string; ```
-R.splitAt source +R.replace source ```javascript -import { isArray } from './_internals/isArray.js' -import { drop } from './drop.js' -import { maybe } from './maybe.js' -import { take } from './take.js' - -export function splitAt(index, input){ - if (arguments.length === 1){ - return _list => splitAt(index, _list) - } - if (!input) throw new TypeError(`Cannot read property 'slice' of ${ input }`) - - if (!isArray(input) && typeof input !== 'string') return [ [], [] ] - - const correctIndex = maybe( - index < 0, - input.length + index < 0 ? 0 : input.length + index, - index - ) +import { curry } from './curry.js' - return [ take(correctIndex, input), drop(correctIndex, input) ] +function replaceFn( + pattern, replacer, str +){ + return str.replace(pattern, replacer) } + +export const replace = curry(replaceFn) ```
@@ -24005,64 +12806,28 @@ export function splitAt(index, input){ Tests ```javascript -import { splitAt as splitAtRamda } from 'ramda' - -import { splitAt } from './splitAt.js' - -const list = [ 1, 2, 3 ] -const str = 'foo bar' - -test('with array', () => { - const result = splitAt(2, list) - expect(result).toEqual([ [ 1, 2 ], [ 3 ] ]) -}) - -test('with array - index is negative number', () => { - const result = splitAt(-6, list) - expect(result).toEqual([ [], list ]) -}) - -test('with array - index is out of scope', () => { - const result = splitAt(4, list) - expect(result).toEqual([ [ 1, 2, 3 ], [] ]) -}) - -test('with string', () => { - const result = splitAt(4, str) - expect(result).toEqual([ 'foo ', 'bar' ]) -}) - -test('with string - index is negative number', () => { - const result = splitAt(-2, str) - expect(result).toEqual([ 'foo b', 'ar' ]) -}) - -test('with string - index is out of scope', () => { - const result = splitAt(10, str) - expect(result).toEqual([ str, '' ]) -}) +import { replace } from './replace.js' -test('with array - index is out of scope', () => { - const result = splitAt(4)(list) - expect(result).toEqual([ [ 1, 2, 3 ], [] ]) +test('happy', () => { + expect(replace( + /\s/g, '|', 'foo bar baz' + )).toBe('foo|bar|baz') }) -const badInputs = [ 1, true, /foo/g, {} ] -const throwingBadInputs = [ null, undefined ] - -test('with bad inputs', () => { - throwingBadInputs.forEach(badInput => { - expect(() => splitAt(1, badInput)).toThrowWithMessage(TypeError, - `Cannot read property 'slice' of ${ badInput }`) - expect(() => splitAtRamda(1, badInput)).toThrowWithMessage(TypeError, - `Cannot read properties of ${ badInput } (reading 'slice')`) - }) - - badInputs.forEach(badInput => { - const result = splitAt(1, badInput) - const ramdaResult = splitAtRamda(1, badInput) - expect(result).toEqual(ramdaResult) - }) +test('with function as replacer input', () => { + expect(replace( + /\s/g, + ( + match, offset, str + ) => { + expect(match).toBe(' ') + expect([ 3, 7 ].includes(offset)).toBeTrue() + expect(str).toBe('foo bar baz') + + return '|' + }, + 'foo bar baz' + )).toBe('foo|bar|baz') }) ``` @@ -24073,104 +12838,97 @@ test('with bad inputs', () => { TypeScript test ```typescript -import {splitAt} from 'rambda' +import {replace} from 'rambda' -const index = 1 -const str = 'foo' -const list = [1, 2, 3] +const str = 'foo bar foo' +const replacer = 'bar' -describe('R.splitAt with array', () => { +describe('R.replace', () => { it('happy', () => { - const result = splitAt(index, list) + const result = replace(/foo/g, replacer, str) - result // $ExpectType [number[], number[]] + result // $ExpectType string }) - it('curried', () => { - const result = splitAt(index)(list) + it('with string as search pattern', () => { + const result = replace('foo', replacer, str) - result // $ExpectType [number[], number[]] + result // $ExpectType string + }) + it('with function as replacer', () => { + const result = replace('f(o)o', (m: string, p1: string, offset: number) => { + m // $ExpectType string + p1 // $ExpectType string + offset // $ExpectType number + return p1 + }, str) + + result // $ExpectType string }) }) -describe('R.splitAt with string', () => { +describe('R.replace - curried', () => { it('happy', () => { - const result = splitAt(index, str) + const result = replace(/foo/g, replacer)(str) - result // $ExpectType [string, string] + result // $ExpectType string }) - it('curried', () => { - const result = splitAt(index)(str) + it('with string as search pattern', () => { + const result = replace('foo', replacer)(str) - result // $ExpectType [string, string] + result // $ExpectType string + }) + it('with function as replacer', () => { + const result = replace('f(o)o')((m: string, p1: string, offset: number) => { + m // $ExpectType string + p1 // $ExpectType string + offset // $ExpectType number + return p1 + })(str) + + result // $ExpectType string }) }) ``` -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#splitAt) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#replace) -### splitEvery +### reverse ```typescript -splitEvery(sliceLength: number, input: T[]): (T[])[] +reverse(input: T[]): T[] ``` -It splits `input` into slices of `sliceLength`. - -```javascript -const result = [ - R.splitEvery(2, [1, 2, 3]), - R.splitEvery(3, 'foobar') -] - -const expected = [ - [[1, 2], [3]], - ['foo', 'bar'] -] -// => `result` is equal to `expected` -``` +It returns a reversed copy of list or string `input`. -Try this R.splitEvery example in Rambda REPL +Try this R.reverse example in Rambda REPL
All TypeScript definitions ```typescript -splitEvery(sliceLength: number, input: T[]): (T[])[]; -splitEvery(sliceLength: number, input: string): string[]; -splitEvery(sliceLength: number): { - (input: string): string[]; - (input: T[]): (T[])[]; -}; +reverse(input: T[]): T[]; +reverse(input: string): string; ```
-R.splitEvery source +R.reverse source ```javascript -export function splitEvery(sliceLength, listOrString){ - if (arguments.length === 1){ - return _listOrString => splitEvery(sliceLength, _listOrString) - } - - if (sliceLength < 1){ - throw new Error('First argument to splitEvery must be a positive integer') +export function reverse(listOrString) { + if (typeof listOrString === 'string') { + return listOrString.split('').reverse().join('') } - const willReturn = [] - let counter = 0 - - while (counter < listOrString.length){ - willReturn.push(listOrString.slice(counter, counter += sliceLength)) - } + const clone = listOrString.slice() - return willReturn + return clone.reverse() } ``` @@ -24181,21 +12939,22 @@ export function splitEvery(sliceLength, listOrString){ Tests ```javascript -import { splitEvery } from './splitEvery.js' +import {reverse} from './reverse.js' test('happy', () => { - expect(splitEvery(3, [ 1, 2, 3, 4, 5, 6, 7 ])).toEqual([ - [ 1, 2, 3 ], - [ 4, 5, 6 ], - [ 7 ], - ]) + expect(reverse([1, 2, 3])).toEqual([3, 2, 1]) +}) - expect(splitEvery(3)('foobarbaz')).toEqual([ 'foo', 'bar', 'baz' ]) +test('with string', () => { + expect(reverse('baz')).toBe('zab') }) -test('with bad input', () => { - expect(() => - expect(splitEvery(0)('foo')).toEqual([ 'f', 'o', 'o' ])).toThrowErrorMatchingInlineSnapshot('"First argument to splitEvery must be a positive integer"') +test("it doesn't mutate", () => { + const arr = [1, 2, 3] + + expect(reverse(arr)).toEqual([3, 2, 1]) + + expect(arr).toEqual([1, 2, 3]) }) ``` @@ -24206,130 +12965,193 @@ test('with bad input', () => { TypeScript test ```typescript -import {splitEvery} from 'rambda' +import {reverse} from 'rambda' -const list = [1, 2, 3, 4, 5, 6, 7] +const list = [1, 2, 3, 4, 5] -describe('R.splitEvery', () => { +describe('R.reverse', () => { it('happy', () => { - const result = splitEvery(3, list) - - result // $ExpectType number[][] - }) - it('curried', () => { - const result = splitEvery(3)(list) - - result // $ExpectType number[][] + const result = reverse(list) + result // $ExpectType number[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#splitEvery) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#reverse) -### splitWhen +### set ```typescript -splitWhen(predicate: Predicate, list: U[]): (U[])[] +set(lens: Lens): { + (a: A): (obj: S) => S + (a: A, obj: S): S +} ``` -It splits `list` to two arrays according to a `predicate` function. +It returns a copied **Object** or **Array** with modified `lens` focus set to `replacer` value. -The first array contains all members of `list` before `predicate` returns `true`. +Try this R.set example in Rambda REPL -```javascript -const list = [1, 2, 1, 2] -const result = R.splitWhen(R.equals(2), list) -// => [[1], [2, 1, 2]] +
+ +All TypeScript definitions + +```typescript +set(lens: Lens): { + (a: A): (obj: S) => S + (a: A, obj: S): S +}; +set(lens: Lens, a: A): (obj: S) => S; +set(lens: Lens, a: A, obj: S): S; ``` -Try this R.splitWhen example in Rambda REPL +
-All TypeScript definitions +R.set source -```typescript -splitWhen(predicate: Predicate, list: U[]): (U[])[]; -splitWhen(predicate: Predicate): (list: U[]) => (U[])[]; +```javascript +import {always} from './always.js' +import {curry} from './curry.js' +import {over} from './over.js' + +function setFn(lens, replacer, x) { + return over(lens, always(replacer), x) +} + +export const set = curry(setFn) ```
-R.splitWhen source +Tests ```javascript -export function splitWhen(predicate, input){ - if (arguments.length === 1){ - return _input => splitWhen(predicate, _input) - } - if (!input) - throw new TypeError(`Cannot read property 'length' of ${ input }`) +import {assoc} from './assoc.js' +import {lens} from './lens.js' +import {lensIndex} from './lensIndex.js' +import {lensPath} from './lensPath.js' +import {prop} from './prop.js' +import {set} from './set.js' - const preFound = [] - const postFound = [] - let found = false - let counter = -1 +const testObject = { + foo: 'bar', + baz: { + a: 'x', + b: 'y', + }, +} - while (counter++ < input.length - 1){ - if (found){ - postFound.push(input[ counter ]) - } else if (predicate(input[ counter ])){ - postFound.push(input[ counter ]) - found = true - } else { - preFound.push(input[ counter ]) - } +test('assoc lens', () => { + const assocLens = lens(prop('foo'), assoc('foo')) + const result = set(assocLens, 'FOO', testObject) + const expected = { + ...testObject, + foo: 'FOO', } + expect(result).toEqual(expected) +}) + +test('path lens', () => { + const pathLens = lensPath('baz.a') + const result = set(pathLens, 'z', testObject) + const expected = { + ...testObject, + baz: { + a: 'z', + b: 'y', + }, + } + expect(result).toEqual(expected) +}) + +test('index lens', () => { + const indexLens = lensIndex(0) + + const result = set(indexLens, 3, [1, 2]) + expect(result).toEqual([3, 2]) +}) +``` + +
+ +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#set) + +### slice + +```typescript + +slice(from: number, to: number, input: string): string +``` - return [ preFound, postFound ] -} +Try this R.slice example in Rambda REPL + +
+ +All TypeScript definitions + +```typescript +slice(from: number, to: number, input: string): string; +slice(from: number, to: number, input: T[]): T[]; +slice(from: number, to: number): { + (input: string): string; + (input: T[]): T[]; +}; +slice(from: number): { + (to: number, input: string): string; + (to: number, input: T[]): T[]; +}; ```
-Tests +R.slice source ```javascript -import { splitWhen as splitWhenRamda } from 'ramda' +import { curry } from './curry.js' -import { equals } from './equals.js' -import { splitWhen } from './splitWhen.js' +function sliceFn( + from, to, list +){ + return list.slice(from, to) +} -const list = [ 1, 2, 1, 2 ] +export const slice = curry(sliceFn) +``` -test('happy', () => { - const result = splitWhen(equals(2), list) - expect(result).toEqual([ [ 1 ], [ 2, 1, 2 ] ]) -}) +
-test('when predicate returns false', () => { - const result = splitWhen(equals(3))(list) - expect(result).toEqual([ list, [] ]) -}) +
-const badInputs = [ 1, true, /foo/g, {} ] -const throwingBadInputs = [ null, undefined ] +Tests -test('with bad inputs', () => { - throwingBadInputs.forEach(badInput => { - expect(() => splitWhen(equals(2), badInput)).toThrowWithMessage(TypeError, - `Cannot read property 'length' of ${ badInput }`) - expect(() => splitWhenRamda(equals(2), badInput)).toThrowWithMessage(TypeError, - `Cannot read properties of ${ badInput } (reading 'length')`) - }) +```javascript +import { slice } from './slice.js' - badInputs.forEach(badInput => { - const result = splitWhen(equals(2), badInput) - const ramdaResult = splitWhenRamda(equals(2), badInput) - expect(result).toEqual(ramdaResult) - }) +test('slice', () => { + expect(slice( + 1, 3, [ 'a', 'b', 'c', 'd' ] + )).toEqual([ 'b', 'c' ]) + expect(slice( + 1, Infinity, [ 'a', 'b', 'c', 'd' ] + )).toEqual([ 'b', 'c', 'd' ]) + expect(slice( + 0, -1, [ 'a', 'b', 'c', 'd' ] + )).toEqual([ 'a', 'b', 'c' ]) + expect(slice( + -3, -1, [ 'a', 'b', 'c', 'd' ] + )).toEqual([ 'b', 'c' ]) + expect(slice( + 0, 3, 'ramda' + )).toBe('ram') }) ``` @@ -24340,94 +13162,59 @@ test('with bad inputs', () => { TypeScript test ```typescript -import {splitWhen} from 'rambda' +import {slice} from 'rambda' -const list = [1, 2, 1, 2] -const predicate = (x: number) => x === 2 +const list = [1, 2, 3, 4, 5] -describe('R.splitWhen', () => { +describe('R.slice', () => { it('happy', () => { - const result = splitWhen(predicate, list) - - result // $ExpectType number[][] + const result = slice(1, 3, list) + result // $ExpectType number[] }) it('curried', () => { - const result = splitWhen(predicate)(list) - - result // $ExpectType number[][] + const result = slice(1, 3)(list) + result // $ExpectType number[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#splitWhen) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#slice) -### startsWith +### sort ```typescript -startsWith(question: T, input: string): boolean +sort(sortFn: (a: T, b: T) => number, list: T[]): T[] ``` -When iterable is a string, then it behaves as `String.prototype.startsWith`. -When iterable is a list, then it uses R.equals to determine if the target list starts in the same way as the given target. - -> :boom: It doesn't work with arrays unlike its corresponding **Ramda** method. - -```javascript -const str = 'foo-bar' -const list = [{a:1}, {a:2}, {a:3}] - -const result = [ - R.startsWith('foo', str), - R.startsWith([{a:1}, {a:2}], list) -] -// => [true, true] -``` +It returns copy of `list` sorted by `sortFn` function, where `sortFn` needs to return only `-1`, `0` or `1`. -Try this R.startsWith example in Rambda REPL +Try this R.sort example in Rambda REPL
All TypeScript definitions ```typescript -startsWith(question: T, input: string): boolean; -startsWith(question: T): (input: string) => boolean; -startsWith(question: T[], input: T[]): boolean; -startsWith(question: T[]): (input: T[]) => boolean; +sort(sortFn: (a: T, b: T) => number, list: T[]): T[]; +sort(sortFn: (a: T, b: T) => number): (list: T[]) => T[]; ```
-R.startsWith source +R.sort source ```javascript -import { isArray } from './_internals/isArray.js' -import { equals } from './equals.js' - -export function startsWith(question, iterable){ - if (arguments.length === 1) - return _iterable => startsWith(question, _iterable) - - if (typeof iterable === 'string'){ - return iterable.startsWith(question) - } - if (!isArray(question)) return false - - let correct = true - const filtered = question.filter((x, index) => { - if (!correct) return false - const result = equals(x, iterable[ index ]) - if (!result) correct = false +import { cloneList } from './_internals/cloneList.js' - return result - }) +export function sort(sortFn, list){ + if (arguments.length === 1) return _list => sort(sortFn, _list) - return filtered.length === question.length + return cloneList(list).sort(sortFn) } ``` @@ -24438,45 +13225,22 @@ export function startsWith(question, iterable){ Tests ```javascript -import { startsWith as startsWithRamda } from 'ramda' +import { sort } from './sort.js' -import { compareCombinations } from './_internals/testUtils.js' -import { possibleIterables, possibleTargets } from './endsWith.spec.js' -import { startsWith } from './startsWith.js' +const fn = (a, b) => a > b ? 1 : -1 -test('with string', () => { - expect(startsWith('foo', 'foo-bar')).toBeTrue() - expect(startsWith('baz')('foo-bar')).toBeFalse() +test('sort', () => { + expect(sort((a, b) => a - b)([ 2, 3, 1 ])).toEqual([ 1, 2, 3 ]) }) -test('use R.equals with array', () => { - const list = [ { a : 1 }, { a : 2 }, { a : 3 } ] - expect(startsWith({ a : 1 }, list)).toBeFalse() - expect(startsWith([ { a : 1 } ], list)).toBeTrue() - expect(startsWith([ { a : 1 }, { a : 2 } ], list)).toBeTrue() - expect(startsWith(list, list)).toBeTrue() - expect(startsWith([ { a : 2 } ], list)).toBeFalse() -}) +test('it doesn\'t mutate', () => { + const list = [ 'foo', 'bar', 'baz' ] -describe('brute force', () => { - compareCombinations({ - fn : startsWith, - fnRamda : startsWithRamda, - firstInput : possibleTargets, - secondInput : possibleIterables, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 0, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 0, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 32, - } - `) - }, - }) + expect(sort(fn, list)).toEqual([ 'bar', 'baz', 'foo' ]) + + expect(list[ 0 ]).toBe('foo') + expect(list[ 1 ]).toBe('bar') + expect(list[ 2 ]).toBe('baz') }) ``` @@ -24487,224 +13251,214 @@ describe('brute force', () => { TypeScript test ```typescript -import {startsWith} from 'rambda' +import {sort} from 'rambda' -describe('R.startsWith - array', () => { - const question = [{a: 1}] - const iterable = [{a: 1}, {a: 2}] - it('happy', () => { - const result = startsWith(question, iterable) - result // $ExpectType boolean - }) - it('curried', () => { - const result = startsWith(question)(iterable) - result // $ExpectType boolean - }) -}) +const list = [3, 0, 5, 2, 1] -describe('R.startsWith - string', () => { - const question = 'foo' - const iterable = 'foo bar' +function sortFn(a: number, b: number): number { + return a > b ? 1 : -1 +} + +describe('R.sort', () => { it('happy', () => { - const result = startsWith(question, iterable) - result // $ExpectType boolean + const result = sort(sortFn, list) + result // $ExpectType number[] }) it('curried', () => { - const result = startsWith(question)(iterable) - result // $ExpectType boolean + const result = sort(sortFn)(list) + result // $ExpectType number[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#startsWith) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sort) -### subtract +### sortBy ```typescript -subtract(x: number, y: number): number +sortBy(sortFn: (a: T) => Ord, list: T[]): T[] ``` -Curried version of `x - y` - -```javascript -const x = 3 -const y = 1 - -const result = R.subtract(x, y) -// => 2 -``` +It returns copy of `list` sorted by `sortFn` function, where `sortFn` function returns a value to compare, i.e. it doesn't need to return only `-1`, `0` or `1`. -Try this R.subtract example in Rambda REPL +Try this R.sortBy example in Rambda REPL
All TypeScript definitions ```typescript -subtract(x: number, y: number): number; -subtract(x: number): (y: number) => number; +sortBy(sortFn: (a: T) => Ord, list: T[]): T[]; +sortBy(sortFn: (a: T) => Ord): (list: T[]) => T[]; +sortBy(sortFn: (a: any) => Ord): (list: T[]) => T[]; ```
-R.subtract source +R.sortBy source ```javascript -export function subtract(a, b){ - if (arguments.length === 1) return _b => subtract(a, _b) - - return a - b -} -``` +import { cloneList } from './_internals/cloneList.js' -
+export function sortBy(sortFn, list){ + if (arguments.length === 1) return _list => sortBy(sortFn, _list) -
+ const clone = cloneList(list) -Tests + return clone.sort((a, b) => { + const aSortResult = sortFn(a) + const bSortResult = sortFn(b) -```javascript -import { subtract } from './subtract.js' + if (aSortResult === bSortResult) return 0 -test('happy', () => { - expect(subtract(2, 1)).toBe(1) - expect(subtract(2)(1)).toBe(1) -}) + return aSortResult < bSortResult ? -1 : 1 + }) +} ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#subtract) - -### sum - -```typescript +
-sum(list: number[]): number -``` +Tests ```javascript -R.sum([1, 2, 3, 4, 5]) -// => 15 -``` +import { compose } from './compose.js' +import { prop } from './prop.js' +import { sortBy } from './sortBy.js' +import { toLower } from './toLower.js' -Try this R.sum example in Rambda REPL +test('happy', () => { + const input = [ { a : 2 }, { a : 1 }, { a : 1 }, { a : 3 } ] + const expected = [ { a : 1 }, { a : 1 }, { a : 2 }, { a : 3 } ] -
+ const result = sortBy(x => x.a)(input) + expect(result).toEqual(expected) +}) -All TypeScript definitions +test('with compose', () => { + const alice = { + name : 'ALICE', + age : 101, + } + const bob = { + name : 'Bob', + age : -10, + } + const clara = { + name : 'clara', + age : 314.159, + } + const people = [ clara, bob, alice ] + const sortByNameCaseInsensitive = sortBy(compose(toLower, prop('name'))) -```typescript -sum(list: number[]): number; + expect(sortByNameCaseInsensitive(people)).toEqual([ alice, bob, clara ]) +}) ```
-R.sum source +TypeScript test -```javascript -export function sum(list){ - return list.reduce((prev, current) => prev + current, 0) +```typescript +import {sortBy, pipe} from 'rambda' + +interface Input { + a: number, } -``` -
+describe('R.sortBy', () => { + it('passing type to sort function', () => { + function fn(x: any): number { + return x.a + } + function fn2(x: Input): number { + return x.a + } -
+ const input = [{a: 2}, {a: 1}, {a: 0}] + const result = sortBy(fn, input) + const curriedResult = sortBy(fn2)(input) -Tests + result // $ExpectType { a: number; }[] + curriedResult // $ExpectType Input[] + result[0].a // $ExpectType number + curriedResult[0].a // $ExpectType number + }) + it('passing type to sort function and list', () => { + function fn(x: Input): number { + return x.a + } -```javascript -import { sum } from './sum.js' + const input: Input[] = [{a: 2}, {a: 1}, {a: 0}] + const result = sortBy(fn, input) + const curriedResult = sortBy(fn)(input) -test('happy', () => { - expect(sum([ 1, 2, 3, 4, 5 ])).toBe(15) + result // $ExpectType Input[] + curriedResult // $ExpectType Input[] + result[0].a // $ExpectType number + }) + it('with R.pipe', () => { + interface Obj { + value: number, + } + const fn = pipe(sortBy(x => x.value)) + + const result = fn([{value: 1}, {value: 2}]) + result // $ExpectType Obj[] + }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sum) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortBy) -### swap +### sortWith + +Try this R.sortWith example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortWith) + +### split ```typescript -swap(indexA: number, indexB: number): (list: T[]) => T[] +split(separator: string | RegExp): (str: string) => string[] ``` -```javascript -const result = R.swap(1, 2, [1, 2, 3]) -// => [1, 3, 2] -``` +Curried version of `String.prototype.split` -Try this R.swap example in Rambda REPL +Try this R.split example in Rambda REPL
All TypeScript definitions ```typescript -swap(indexA: number, indexB: number): (list: T[]) => T[]; -swap(indexA: number, indexB: number, list: T[]): T[]; +split(separator: string | RegExp): (str: string) => string[]; +split(separator: string | RegExp, str: string): string[]; ```
-R.swap source +R.split source ```javascript -import { isArray } from './_internals/isArray.js'; -import { curry } from './curry.js'; +export function split(separator, str){ + if (arguments.length === 1) return _str => split(separator, _str) -function swapArrayOrString(indexA, indexB, iterable) { - const actualIndexA = indexA < 0 ? iterable.length + indexA : indexA; - const actualIndexB = indexB < 0 ? iterable.length + indexB : indexB; - if ( - actualIndexA === actualIndexB || - Math.min(actualIndexA, actualIndexB) < 0 || - Math.max(actualIndexA, actualIndexB) >= iterable.length - ) - return iterable; - if (typeof iterable === 'string') { - return ( - iterable.slice(0, actualIndexA) + - iterable[actualIndexB] + - iterable.slice(actualIndexA + 1, actualIndexB) + - iterable[actualIndexA] + - iterable.slice(actualIndexB + 1) - ); - } - const clone = iterable.slice(); - const temp = clone[actualIndexA]; - clone[actualIndexA] = clone[actualIndexB]; - clone[actualIndexB] = temp; - return clone; -} -function swapFn(indexA, indexB, iterable) { - if (isArray(iterable) || typeof iterable === 'string') - return swapArrayOrString(indexA, indexB, iterable); - - const aVal = iterable[indexA]; - const bVal = iterable[indexB]; - if (aVal === undefined || bVal === undefined) return iterable; - return { - ...iterable, - [indexA]: iterable[indexB], - [indexB]: iterable[indexA], - }; + return str.split(separator) } - -export const swap = curry(swapFn); ```
@@ -24714,100 +13468,102 @@ export const swap = curry(swapFn); Tests ```javascript -import { swap } from './swap'; -const list = ['a', 'b', 'c', 'd', 'e', 'f']; - -it('swaps an element from one index to the other', () => { - expect(swap(0, 1, list)).toEqual(['b', 'a', 'c', 'd', 'e', 'f']); - expect(swap(2, 1, list)).toEqual(['a', 'c', 'b', 'd', 'e', 'f']); - expect(swap(-1, 0, list)).toEqual(['f', 'b', 'c', 'd', 'e', 'a']); - expect(swap(4, 1, list)).toEqual(['a', 'e', 'c', 'd', 'b', 'f']); -}); - -it('does nothing when indexes are outside the list boundaries', () => { - expect(swap(-20, 2, list)).toEqual(list); - expect(swap(20, 2, list)).toEqual(list); - expect(swap(2, 20, list)).toEqual(list); - expect(swap(2, -20, list)).toEqual(list); - expect(swap(20, 20, list)).toEqual(list); - expect(swap(-20, -20, list)).toEqual(list); -}); - -it('does nothing when indexes are equal', () => { - expect(swap(0, 0, list)).toEqual(list); -}); - -it('should be the same when swapping index order', () => { - expect(swap(0, 1, list)).toEqual(swap(1, 0, list)); -}); +import { split } from './split.js' -it('swaps property values from one property to another', () => { - expect(swap('a', 'b', { a: 1, b: 2 })).toEqual({ a: 2, b: 1 }); - expect(swap('b', 'a', { a: 1, b: 2 })).toEqual({ a: 2, b: 1 }); -}); +const str = 'foo|bar|baz' +const splitChar = '|' +const expected = [ 'foo', 'bar', 'baz' ] -it('does nothing when property names are not defined', () => { - expect(swap('a', 'b', { a: 1 })).toEqual({ a: 1 }); - expect(swap('a', 'b', { b: 2 })).toEqual({ b: 2 }); -}); +test('happy', () => { + expect(split(splitChar, str)).toEqual(expected) +}) -it('swaps characters in string from one index to another', () => { - expect(swap(0, 2, 'foo')).toEqual('oof'); -}); +test('curried', () => { + expect(split(splitChar)(str)).toEqual(expected) +}) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#swap) +
-### symmetricDifference +TypeScript test ```typescript +import {split} from 'rambda' -symmetricDifference(x: T[], y: T[]): T[] +const str = 'foo|bar|baz' +const splitChar = '|' + +describe('R.split', () => { + it('happy', () => { + const result = split(splitChar, str) + + result // $ExpectType string[] + }) + it('curried', () => { + const result = split(splitChar)(str) + + result // $ExpectType string[] + }) +}) ``` -It returns a merged list of `x` and `y` with all equal elements removed. +
-`R.equals` is used to determine equality. +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#split) -```javascript -const x = [ 1, 2, 3, 4 ] -const y = [ 3, 4, 5, 6 ] +### splitAt + +```typescript -const result = R.symmetricDifference(x, y) -// => [ 1, 2, 5, 6 ] +splitAt(index: number, input: T[]): [T[], T[]] ``` -Try this R.symmetricDifference example in Rambda REPL +It splits string or array at a given index. + +Try this R.splitAt example in Rambda REPL
All TypeScript definitions ```typescript -symmetricDifference(x: T[], y: T[]): T[]; -symmetricDifference(x: T[]): (y: T[]) => T[]; +splitAt(index: number, input: T[]): [T[], T[]]; +splitAt(index: number, input: string): [string, string]; +splitAt(index: number): { + (input: T[]): [T[], T[]]; + (input: string): [string, string]; +}; ```
-R.symmetricDifference source +R.splitAt source ```javascript -import { concat } from './concat.js' -import { filter } from './filter.js' -import { includes } from './includes.js' +import { isArray } from './_internals/isArray.js' +import { drop } from './drop.js' +import { maybe } from './maybe.js' +import { take } from './take.js' -export function symmetricDifference(x, y){ +export function splitAt(index, input){ if (arguments.length === 1){ - return _y => symmetricDifference(x, _y) + return _list => splitAt(index, _list) } + if (!input) throw new TypeError(`Cannot read property 'slice' of ${ input }`) - return concat(filter(value => !includes(value, y), x), - filter(value => !includes(value, x), y)) + if (!isArray(input) && typeof input !== 'string') return [ [], [] ] + + const correctIndex = maybe( + index < 0, + input.length + index < 0 ? 0 : input.length + index, + index + ) + + return [ take(correctIndex, input), drop(correctIndex, input) ] } ``` @@ -24818,25 +13574,64 @@ export function symmetricDifference(x, y){ Tests ```javascript -import { symmetricDifference } from './symmetricDifference.js' +import { splitAt as splitAtRamda } from 'ramda' -test('symmetricDifference', () => { - const list1 = [ 1, 2, 3, 4 ] - const list2 = [ 3, 4, 5, 6 ] - expect(symmetricDifference(list1)(list2)).toEqual([ 1, 2, 5, 6 ]) +import { splitAt } from './splitAt.js' + +const list = [ 1, 2, 3 ] +const str = 'foo bar' + +test('with array', () => { + const result = splitAt(2, list) + expect(result).toEqual([ [ 1, 2 ], [ 3 ] ]) +}) + +test('with array - index is negative number', () => { + const result = splitAt(-6, list) + expect(result).toEqual([ [], list ]) +}) + +test('with array - index is out of scope', () => { + const result = splitAt(4, list) + expect(result).toEqual([ [ 1, 2, 3 ], [] ]) +}) + +test('with string', () => { + const result = splitAt(4, str) + expect(result).toEqual([ 'foo ', 'bar' ]) +}) + +test('with string - index is negative number', () => { + const result = splitAt(-2, str) + expect(result).toEqual([ 'foo b', 'ar' ]) +}) + +test('with string - index is out of scope', () => { + const result = splitAt(10, str) + expect(result).toEqual([ str, '' ]) +}) + +test('with array - index is out of scope', () => { + const result = splitAt(4)(list) + expect(result).toEqual([ [ 1, 2, 3 ], [] ]) +}) + +const badInputs = [ 1, true, /foo/g, {} ] +const throwingBadInputs = [ null, undefined ] - expect(symmetricDifference([], [])).toEqual([]) -}) +test('with bad inputs', () => { + throwingBadInputs.forEach(badInput => { + expect(() => splitAt(1, badInput)).toThrowWithMessage(TypeError, + `Cannot read property 'slice' of ${ badInput }`) + expect(() => splitAtRamda(1, badInput)).toThrowWithMessage(TypeError, + `Cannot read properties of ${ badInput } (reading 'slice')`) + }) -test('symmetricDifference with objects', () => { - const list1 = [ { id : 1 }, { id : 2 }, { id : 3 }, { id : 4 } ] - const list2 = [ { id : 3 }, { id : 4 }, { id : 5 }, { id : 6 } ] - expect(symmetricDifference(list1)(list2)).toEqual([ - { id : 1 }, - { id : 2 }, - { id : 5 }, - { id : 6 }, - ]) + badInputs.forEach(badInput => { + const result = splitAt(1, badInput) + const ramdaResult = splitAtRamda(1, badInput) + expect(result).toEqual(ramdaResult) + }) }) ``` @@ -24847,108 +13642,91 @@ test('symmetricDifference with objects', () => { TypeScript test ```typescript -import {symmetricDifference} from 'rambda' +import {splitAt} from 'rambda' -describe('R.symmetricDifference', () => { +const index = 1 +const str = 'foo' +const list = [1, 2, 3] + +describe('R.splitAt with array', () => { it('happy', () => { - const list1 = [1, 2, 3, 4] - const list2 = [3, 4, 5, 6] - const result = symmetricDifference(list1, list2) + const result = splitAt(index, list) - result // $ExpectType number[] + result // $ExpectType [number[], number[]] + }) + it('curried', () => { + const result = splitAt(index)(list) + + result // $ExpectType [number[], number[]] }) +}) + +describe('R.splitAt with string', () => { + it('happy', () => { + const result = splitAt(index, str) + result // $ExpectType [string, string] + }) it('curried', () => { - const list1 = [{id: 1}, {id: 2}, {id: 3}, {id: 4}] - const list2 = [{id: 3}, {id: 4}, {id: 5}, {id: 6}] - const result = symmetricDifference(list1)(list2) + const result = splitAt(index)(str) - result // $ExpectType { id: number; }[] + result // $ExpectType [string, string] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#symmetricDifference) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#splitAt) -### T +### splitEvery ```typescript -T(): boolean +splitEvery(sliceLength: number, input: T[]): (T[])[] ``` -```javascript -R.T() -// => true -``` +It splits `input` into slices of `sliceLength`. -Try this R.T example in Rambda REPL +Try this R.splitEvery example in Rambda REPL
All TypeScript definitions ```typescript -T(): boolean; +splitEvery(sliceLength: number, input: T[]): (T[])[]; +splitEvery(sliceLength: number, input: string): string[]; +splitEvery(sliceLength: number): { + (input: string): string[]; + (input: T[]): (T[])[]; +}; ```
-R.T source - -```javascript -export function T(){ - return true -} -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#T) - -### tail - -```typescript - -tail(input: T): T extends [any, ...infer U] ? U : [...T] -``` - -It returns all but the first element of `input`. +R.splitEvery source ```javascript -const result = [ - R.tail([1, 2, 3]), - R.tail('foo') -] -// => [[2, 3], 'oo'] -``` - -Try this R.tail example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -tail(input: T): T extends [any, ...infer U] ? U : [...T]; -tail(input: string): string; -``` - -
+export function splitEvery(sliceLength, listOrString){ + if (arguments.length === 1){ + return _listOrString => splitEvery(sliceLength, _listOrString) + } -
+ if (sliceLength < 1){ + throw new Error('First argument to splitEvery must be a positive integer') + } -R.tail source + const willReturn = [] + let counter = 0 -```javascript -import { drop } from './drop.js' + while (counter < listOrString.length){ + willReturn.push(listOrString.slice(counter, counter += sliceLength)) + } -export function tail(listOrString){ - return drop(1, listOrString) + return willReturn } ``` @@ -24959,18 +13737,21 @@ export function tail(listOrString){ Tests ```javascript -import { tail } from './tail.js' +import { splitEvery } from './splitEvery.js' -test('tail', () => { - expect(tail([ 1, 2, 3 ])).toEqual([ 2, 3 ]) - expect(tail([ 1, 2 ])).toEqual([ 2 ]) - expect(tail([ 1 ])).toEqual([]) - expect(tail([])).toEqual([]) +test('happy', () => { + expect(splitEvery(3, [ 1, 2, 3, 4, 5, 6, 7 ])).toEqual([ + [ 1, 2, 3 ], + [ 4, 5, 6 ], + [ 7 ], + ]) - expect(tail('abc')).toBe('bc') - expect(tail('ab')).toBe('b') - expect(tail('a')).toBe('') - expect(tail('')).toBe('') + expect(splitEvery(3)('foobarbaz')).toEqual([ 'foo', 'bar', 'baz' ]) +}) + +test('with bad input', () => { + expect(() => + expect(splitEvery(0)('foo')).toEqual([ 'f', 'o', 'o' ])).toThrowErrorMatchingInlineSnapshot('"First argument to splitEvery must be a positive integer"') }) ``` @@ -24981,80 +13762,81 @@ test('tail', () => { TypeScript test ```typescript -import {tail} from 'rambda' +import {splitEvery} from 'rambda' -describe('R.tail', () => { - it('with string', () => { - const result = tail('foo') +const list = [1, 2, 3, 4, 5, 6, 7] - result // $ExpectType string - }) - it('with list - one type', () => { - const result = tail([1, 2, 3]) +describe('R.splitEvery', () => { + it('happy', () => { + const result = splitEvery(3, list) - result // $ExpectType number[] + result // $ExpectType number[][] }) - it('with list - mixed types', () => { - const result = tail(['foo', 'bar', 1, 2, 3]) + it('curried', () => { + const result = splitEvery(3)(list) - result // $ExpectType (string | number)[] + result // $ExpectType number[][] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#tail) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#splitEvery) -### take +### splitWhen ```typescript -take(howMany: number, input: T[]): T[] +splitWhen(predicate: Predicate, list: U[]): (U[])[] ``` -It returns the first `howMany` elements of `input`. - -```javascript -const howMany = 2 +It splits `list` to two arrays according to a `predicate` function. -const result = [ - R.take(howMany, [1, 2, 3]), - R.take(howMany, 'foobar'), -] -// => [[1, 2], 'fo'] -``` +The first array contains all members of `list` before `predicate` returns `true`. -Try this R.take example in Rambda REPL +Try this R.splitWhen example in Rambda REPL
All TypeScript definitions ```typescript -take(howMany: number, input: T[]): T[]; -take(howMany: number, input: string): string; -take(howMany: number) : (input: T[]) => T[]; +splitWhen(predicate: Predicate, list: U[]): (U[])[]; +splitWhen(predicate: Predicate): (list: U[]) => (U[])[]; ```
-R.take source +R.splitWhen source ```javascript -import baseSlice from './_internals/baseSlice.js' +export function splitWhen(predicate, input){ + if (arguments.length === 1){ + return _input => splitWhen(predicate, _input) + } + if (!input) + throw new TypeError(`Cannot read property 'length' of ${ input }`) -export function take(howMany, listOrString){ - if (arguments.length === 1) - return _listOrString => take(howMany, _listOrString) - if (howMany < 0) return listOrString.slice() - if (typeof listOrString === 'string') return listOrString.slice(0, howMany) + const preFound = [] + const postFound = [] + let found = false + let counter = -1 - return baseSlice( - listOrString, 0, howMany - ) + while (counter++ < input.length - 1){ + if (found){ + postFound.push(input[ counter ]) + } else if (predicate(input[ counter ])){ + postFound.push(input[ counter ]) + found = true + } else { + preFound.push(input[ counter ]) + } + } + + return [ preFound, postFound ] } ``` @@ -25065,28 +13847,39 @@ export function take(howMany, listOrString){ Tests ```javascript -import { take } from './take.js' - -test('happy', () => { - const arr = [ 'foo', 'bar', 'baz' ] +import { splitWhen as splitWhenRamda } from 'ramda' - expect(take(1, arr)).toEqual([ 'foo' ]) +import { equals } from './equals.js' +import { splitWhen } from './splitWhen.js' - expect(arr).toEqual([ 'foo', 'bar', 'baz' ]) +const list = [ 1, 2, 1, 2 ] - expect(take(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar' ]) - expect(take(3, [ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar', 'baz' ]) - expect(take(4, [ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar', 'baz' ]) - expect(take(3)('rambda')).toBe('ram') +test('happy', () => { + const result = splitWhen(equals(2), list) + expect(result).toEqual([ [ 1 ], [ 2, 1, 2 ] ]) }) -test('with negative index', () => { - expect(take(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) - expect(take(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) +test('when predicate returns false', () => { + const result = splitWhen(equals(3))(list) + expect(result).toEqual([ list, [] ]) }) -test('with zero index', () => { - expect(take(0, [ 1, 2, 3 ])).toEqual([]) +const badInputs = [ 1, true, /foo/g, {} ] +const throwingBadInputs = [ null, undefined ] + +test('with bad inputs', () => { + throwingBadInputs.forEach(badInput => { + expect(() => splitWhen(equals(2), badInput)).toThrowWithMessage(TypeError, + `Cannot read property 'length' of ${ badInput }`) + expect(() => splitWhenRamda(equals(2), badInput)).toThrowWithMessage(TypeError, + `Cannot read properties of ${ badInput } (reading 'length')`) + }) + + badInputs.forEach(badInput => { + const result = splitWhen(equals(2), badInput) + const ramdaResult = splitWhenRamda(equals(2), badInput) + expect(result).toEqual(ramdaResult) + }) }) ``` @@ -25097,99 +13890,81 @@ test('with zero index', () => { TypeScript test ```typescript -import {take} from 'rambda' - -const list = [1, 2, 3, 4] -const str = 'foobar' -const howMany = 2 - -describe('R.take - array', () => { - it('happy', () => { - const result = take(howMany, list) - - result // $ExpectType number[] - }) - it('curried', () => { - const result = take(howMany)(list) +import {splitWhen} from 'rambda' - result // $ExpectType number[] - }) -}) +const list = [1, 2, 1, 2] +const predicate = (x: number) => x === 2 -describe('R.take - string', () => { +describe('R.splitWhen', () => { it('happy', () => { - const result = take(howMany, str) + const result = splitWhen(predicate, list) - result // $ExpectType string + result // $ExpectType number[][] }) it('curried', () => { - const result = take(howMany)(str) + const result = splitWhen(predicate)(list) - result // $ExpectType string + result // $ExpectType number[][] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#take) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#splitWhen) -### takeLast +### startsWith ```typescript -takeLast(howMany: number, input: T[]): T[] +startsWith(question: T, input: string): boolean ``` -It returns the last `howMany` elements of `input`. - -```javascript -const howMany = 2 - -const result = [ - R.takeLast(howMany, [1, 2, 3]), - R.takeLast(howMany, 'foobar'), -] -// => [[2, 3], 'ar'] -``` +When iterable is a string, then it behaves as `String.prototype.startsWith`. +When iterable is a list, then it uses R.equals to determine if the target list starts in the same way as the given target. -Try this R.takeLast example in Rambda REPL +Try this R.startsWith example in Rambda REPL
All TypeScript definitions ```typescript -takeLast(howMany: number, input: T[]): T[]; -takeLast(howMany: number, input: string): string; -takeLast(howMany: number) : (input: T[]) => T[]; +startsWith(question: T, input: string): boolean; +startsWith(question: T): (input: string) => boolean; +startsWith(question: T[], input: T[]): boolean; +startsWith(question: T[]): (input: T[]) => boolean; ```
-R.takeLast source +R.startsWith source ```javascript -import baseSlice from './_internals/baseSlice.js' +import { isArray } from './_internals/isArray.js' +import { equals } from './equals.js' -export function takeLast(howMany, listOrString){ +export function startsWith(question, iterable){ if (arguments.length === 1) - return _listOrString => takeLast(howMany, _listOrString) + return _iterable => startsWith(question, _iterable) - const len = listOrString.length - if (howMany < 0) return listOrString.slice() - let numValue = howMany > len ? len : howMany + if (typeof iterable === 'string'){ + return iterable.startsWith(question) + } + if (!isArray(question)) return false - if (typeof listOrString === 'string') - return listOrString.slice(len - numValue) + let correct = true + const filtered = question.filter((x, index) => { + if (!correct) return false + const result = equals(x, iterable[ index ]) + if (!result) correct = false - numValue = len - numValue + return result + }) - return baseSlice( - listOrString, numValue, len - ) + return filtered.length === question.length } ``` @@ -25200,29 +13975,45 @@ export function takeLast(howMany, listOrString){ Tests ```javascript -import { takeLast } from './takeLast.js' - -test('with arrays', () => { - expect(takeLast(1, [ 'foo', 'bar', 'baz' ])).toEqual([ 'baz' ]) - - expect(takeLast(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'bar', 'baz' ]) - - expect(takeLast(3, [ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar', 'baz' ]) +import { startsWith as startsWithRamda } from 'ramda' - expect(takeLast(4, [ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar', 'baz' ]) +import { compareCombinations } from './_internals/testUtils.js' +import { possibleIterables, possibleTargets } from './endsWith.spec.js' +import { startsWith } from './startsWith.js' - expect(takeLast(10, [ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar', 'baz' ]) +test('with string', () => { + expect(startsWith('foo', 'foo-bar')).toBeTrue() + expect(startsWith('baz')('foo-bar')).toBeFalse() }) -test('with strings', () => { - expect(takeLast(3, 'rambda')).toBe('bda') - - expect(takeLast(7, 'rambda')).toBe('rambda') +test('use R.equals with array', () => { + const list = [ { a : 1 }, { a : 2 }, { a : 3 } ] + expect(startsWith({ a : 1 }, list)).toBeFalse() + expect(startsWith([ { a : 1 } ], list)).toBeTrue() + expect(startsWith([ { a : 1 }, { a : 2 } ], list)).toBeTrue() + expect(startsWith(list, list)).toBeTrue() + expect(startsWith([ { a : 2 } ], list)).toBeFalse() }) -test('with negative index', () => { - expect(takeLast(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) - expect(takeLast(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) +describe('brute force', () => { + compareCombinations({ + fn : startsWith, + fnRamda : startsWithRamda, + firstInput : possibleTargets, + secondInput : possibleIterables, + callback : errorsCounters => { + expect(errorsCounters).toMatchInlineSnapshot(` + { + "ERRORS_MESSAGE_MISMATCH": 0, + "ERRORS_TYPE_MISMATCH": 0, + "RESULTS_MISMATCH": 0, + "SHOULD_NOT_THROW": 0, + "SHOULD_THROW": 0, + "TOTAL_TESTS": 32, + } + `) + }, + }) }) ``` @@ -25233,246 +14024,140 @@ test('with negative index', () => { TypeScript test ```typescript -import {filter, piped, takeLast} from 'rambda' - -const list = [1, 2, 3, 4] -const str = 'foobar' -const howMany = 2 +import {startsWith} from 'rambda' -describe('R.takeLast - array', () => { +describe('R.startsWith - array', () => { + const question = [{a: 1}] + const iterable = [{a: 1}, {a: 2}] it('happy', () => { - const result = takeLast(howMany, list) - - result // $ExpectType number[] + const result = startsWith(question, iterable) + result // $ExpectType boolean }) it('curried', () => { - const result = takeLast(howMany)(list) - - result // $ExpectType number[] + const result = startsWith(question)(iterable) + result // $ExpectType boolean }) - it('real case', () => { - let data = ['foo'] - let result = piped( - data, - filter( - x => x.length >= 100 - ), - takeLast(5), - ) - result // $ExpectType string[] - }) }) -describe('R.takeLast - string', () => { +describe('R.startsWith - string', () => { + const question = 'foo' + const iterable = 'foo bar' it('happy', () => { - const result = takeLast(howMany, str) - - result // $ExpectType string + const result = startsWith(question, iterable) + result // $ExpectType boolean }) it('curried', () => { - const result = takeLast(howMany)(str) - - result // $ExpectType string + const result = startsWith(question)(iterable) + result // $ExpectType boolean }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#takeLast) - -### takeLastWhile - -```typescript - -takeLastWhile(predicate: (x: string) => boolean, input: string): string -``` - -```javascript -const result = R.takeLastWhile( - x => x > 2, - [1, 2, 3, 4] -) -// => [3, 4] -``` - -Try this R.takeLastWhile example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -takeLastWhile(predicate: (x: string) => boolean, input: string): string; -takeLastWhile(predicate: (x: string) => boolean): (input: string) => string; -takeLastWhile(predicate: (x: T) => boolean, input: T[]): T[]; -takeLastWhile(predicate: (x: T) => boolean): (input: T[]) => T[]; -``` - -
- -
- -R.takeLastWhile source - -```javascript -import { isArray } from './_internals/isArray.js' - -export function takeLastWhile(predicate, input){ - if (arguments.length === 1){ - return _input => takeLastWhile(predicate, _input) - } - if (input.length === 0) return input - - const toReturn = [] - let counter = input.length - - while (counter){ - const item = input[ --counter ] - if (!predicate(item)){ - break - } - toReturn.push(item) - } - - return isArray(input) ? toReturn.reverse() : toReturn.reverse().join('') -} -``` - -
- -
- -Tests - -```javascript -import { takeLastWhile } from './takeLastWhile.js' -const assert = require('assert') +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#startsWith) -const list = [ 1, 2, 3, 4 ] +### subtract -test('happy', () => { - const predicate = x => x > 2 - const result = takeLastWhile(predicate, list) - expect(result).toEqual([ 3, 4 ]) -}) +Curried version of `x - y` -test('predicate is always true', () => { - const predicate = () => true - const result = takeLastWhile(predicate)(list) - expect(result).toEqual(list) -}) +Try this R.subtract example in Rambda REPL -test('predicate is always false', () => { - const predicate = () => false - const result = takeLastWhile(predicate, list) - expect(result).toEqual([]) -}) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#subtract) -test('with string', () => { - const result = takeLastWhile(x => x !== 'F', 'FOOBAR') - expect(result).toBe('OOBAR') -}) +### sum + +```typescript + +sum(list: number[]): number ``` -
+Try this R.sum example in Rambda REPL
-TypeScript test +All TypeScript definitions ```typescript -import {takeLastWhile} from 'rambda' +sum(list: number[]): number; +``` -const list = [1, 2, 3] -const str = 'FOO' +
-describe('R.takeLastWhile', () => { - it('with array', () => { - const result = takeLastWhile(x => x > 1, list) +
- result // $ExpectType number[] - }) - it('with array - curried', () => { - const result = takeLastWhile(x => x > 1, list) +R.sum source - result // $ExpectType number[] - }) - it('with string', () => { - const result = takeLastWhile(x => x !== 'F', str) +```javascript +export function sum(list){ + return list.reduce((prev, current) => prev + current, 0) +} +``` - result // $ExpectType string - }) - it('with string - curried', () => { - const result = takeLastWhile(x => x !== 'F')(str) +
- result // $ExpectType string - }) +
+ +Tests + +```javascript +import { sum } from './sum.js' + +test('happy', () => { + expect(sum([ 1, 2, 3, 4, 5 ])).toBe(15) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#takeLastWhile) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sum) -### takeWhile +### swap + +Try this R.swap example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#swap) + +### symmetricDifference ```typescript -takeWhile(fn: Predicate, iterable: string): string +symmetricDifference(x: T[], y: T[]): T[] ``` -```javascript -const list = [1, 2, 3, 4] -const predicate = x => x < 3 +It returns a merged list of `x` and `y` with all equal elements removed. -const result = R.takeWhile(predicate, list) -// => [1, 2] -``` +`R.equals` is used to determine equality. -Try this R.takeWhile example in Rambda REPL +Try this R.symmetricDifference example in Rambda REPL
All TypeScript definitions ```typescript -takeWhile(fn: Predicate, iterable: string): string; -takeWhile(fn: Predicate): (iterable: string) => string; -takeWhile(fn: Predicate, iterable: T[]): T[]; -takeWhile(fn: Predicate): (iterable: T[]) => T[]; +symmetricDifference(x: T[], y: T[]): T[]; +symmetricDifference(x: T[]): (y: T[]) => T[]; ```
-R.takeWhile source +R.symmetricDifference source ```javascript -import { isArray as isArrayModule } from './_internals/isArray.js' +import { concat } from './concat.js' +import { filter } from './filter.js' +import { includes } from './includes.js' -export function takeWhile(predicate, iterable){ +export function symmetricDifference(x, y){ if (arguments.length === 1){ - return _iterable => takeWhile(predicate, _iterable) - } - const isArray = isArrayModule(iterable) - if (!isArray && typeof iterable !== 'string'){ - throw new Error('`iterable` is neither list nor a string') - } - - const toReturn = [] - let counter = 0 - - while (counter < iterable.length){ - const item = iterable[ counter++ ] - if (!predicate(item)){ - break - } - toReturn.push(item) + return _y => symmetricDifference(x, _y) } - return isArray ? toReturn : toReturn.join('') + return concat(filter(value => !includes(value, y), x), + filter(value => !includes(value, x), y)) } ``` @@ -25483,75 +14168,25 @@ export function takeWhile(predicate, iterable){ Tests ```javascript -import { takeWhile as takeWhileRamda } from 'ramda' - -import { compareCombinations } from './_internals/testUtils.js' -import { takeWhile } from './takeWhile.js' - -const list = [ 1, 2, 3, 4, 5 ] - -test('happy', () => { - const result = takeWhile(x => x < 3, list) - expect(result).toEqual([ 1, 2 ]) -}) - -test('always true', () => { - const result = takeWhile(x => true)(list) - expect(result).toEqual(list) -}) +import { symmetricDifference } from './symmetricDifference.js' -test('always false', () => { - const result = takeWhile(x => 0, list) - expect(result).toEqual([]) -}) +test('symmetricDifference', () => { + const list1 = [ 1, 2, 3, 4 ] + const list2 = [ 3, 4, 5, 6 ] + expect(symmetricDifference(list1)(list2)).toEqual([ 1, 2, 5, 6 ]) -test('with string', () => { - const result = takeWhile(x => x !== 'b', 'foobar') - expect(result).toBe('foo') + expect(symmetricDifference([], [])).toEqual([]) }) -const possiblePredicates = [ - null, - undefined, - () => 0, - () => true, - x => x !== 'b', - /foo/g, - {}, - [], -] - -const possibleIterables = [ - null, - undefined, - [], - {}, - 1, - '', - 'foobar', - [ '' ], - [ 1, 2, 3, 4, 5 ], -] - -describe('brute force', () => { - compareCombinations({ - firstInput : possiblePredicates, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 15, - "ERRORS_TYPE_MISMATCH": 16, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 16, - "SHOULD_THROW": 0, - "TOTAL_TESTS": 72, - } - `) - }, - secondInput : possibleIterables, - fn : takeWhile, - fnRamda : takeWhileRamda, - }) +test('symmetricDifference with objects', () => { + const list1 = [ { id : 1 }, { id : 2 }, { id : 3 }, { id : 4 } ] + const list2 = [ { id : 3 }, { id : 4 }, { id : 5 }, { id : 6 } ] + expect(symmetricDifference(list1)(list2)).toEqual([ + { id : 1 }, + { id : 2 }, + { id : 5 }, + { id : 6 }, + ]) }) ``` @@ -25562,88 +14197,95 @@ describe('brute force', () => { TypeScript test ```typescript -import {takeWhile} from 'rambda' - -const list = [1, 2, 3, 4] +import {symmetricDifference} from 'rambda' -describe('R.takeWhile', () => { +describe('R.symmetricDifference', () => { it('happy', () => { - const result = takeWhile(x => x > 2, list) - - result // $ExpectType number[] - }) - it('curried require explicit type', () => { - const result = takeWhile(x => x > 2)(list) + const list1 = [1, 2, 3, 4] + const list2 = [3, 4, 5, 6] + const result = symmetricDifference(list1, list2) result // $ExpectType number[] }) -}) - -describe('with string as iterable', () => { - const str = 'foobar' - it('happy', () => { - const result = takeWhile(x => x !== 'b', str) - result // $ExpectType string - }) - it('curried require explicit type', () => { - const result = takeWhile(x => x !== 'b')(str) + it('curried', () => { + const list1 = [{id: 1}, {id: 2}, {id: 3}, {id: 4}] + const list2 = [{id: 3}, {id: 4}, {id: 5}, {id: 6}] + const result = symmetricDifference(list1)(list2) - result // $ExpectType string + result // $ExpectType { id: number; }[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#takeWhile) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#symmetricDifference) -### tap +### T ```typescript -tap(fn: (x: T) => void, input: T): T +T(): boolean ``` -It applies function `fn` to input `x` and returns `x`. +Try this R.T example in Rambda REPL -One use case is debugging in the middle of `R.compose`. +
+ +All TypeScript definitions + +```typescript +T(): boolean; +``` + +
+ +
+ +R.T source ```javascript -const list = [1, 2, 3] +export function T(){ + return true +} +``` + +
+ +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#T) + +### tail + +```typescript -R.compose( - R.map(x => x * 2) - R.tap(console.log), - R.filter(x => x > 1) -)(list) -// => `2` and `3` will be logged +tail(input: T): T extends [any, ...infer U] ? U : [...T] ``` -Try this R.tap example in Rambda REPL +It returns all but the first element of `input`. + +Try this R.tail example in Rambda REPL
All TypeScript definitions ```typescript -tap(fn: (x: T) => void, input: T): T; -tap(fn: (x: T) => void): (input: T) => T; +tail(input: T): T extends [any, ...infer U] ? U : [...T]; +tail(input: string): string; ```
-R.tap source +R.tail source ```javascript -export function tap(fn, x){ - if (arguments.length === 1) return _x => tap(fn, _x) - - fn(x) +import { drop } from './drop.js' - return x +export function tail(listOrString){ + return drop(1, listOrString) } ``` @@ -25654,15 +14296,18 @@ export function tap(fn, x){ Tests ```javascript -import { tap } from './tap.js' +import { tail } from './tail.js' -test('tap', () => { - let a = 1 - const sayX = x => a = x +test('tail', () => { + expect(tail([ 1, 2, 3 ])).toEqual([ 2, 3 ]) + expect(tail([ 1, 2 ])).toEqual([ 2 ]) + expect(tail([ 1 ])).toEqual([]) + expect(tail([])).toEqual([]) - expect(tap(sayX, 100)).toBe(100) - expect(tap(sayX)(100)).toBe(100) - expect(a).toBe(100) + expect(tail('abc')).toBe('bc') + expect(tail('ab')).toBe('b') + expect(tail('a')).toBe('') + expect(tail('')).toBe('') }) ``` @@ -25673,64 +14318,69 @@ test('tap', () => { TypeScript test ```typescript -import {tap, pipe} from 'rambda' +import {tail} from 'rambda' + +describe('R.tail', () => { + it('with string', () => { + const result = tail('foo') + + result // $ExpectType string + }) + it('with list - one type', () => { + const result = tail([1, 2, 3]) + + result // $ExpectType number[] + }) + it('with list - mixed types', () => { + const result = tail(['foo', 'bar', 1, 2, 3]) -describe('R.tap', () => { - it('happy', () => { - pipe( - tap(x => { - x // $ExpectType number[] - }), - (x: number[]) => x.length - )([1, 2]) + result // $ExpectType (string | number)[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#tap) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#tail) -### test +### take ```typescript -test(regExpression: RegExp): (str: string) => boolean +take(howMany: number, input: T): T extends string ? string : T ``` -It determines whether `str` matches `regExpression`. - -```javascript -R.test(/^f/, 'foo') -// => true -``` +It returns the first `howMany` elements of `input`. -Try this R.test example in Rambda REPL +Try this R.take example in Rambda REPL
All TypeScript definitions ```typescript -test(regExpression: RegExp): (str: string) => boolean; -test(regExpression: RegExp, str: string): boolean; +take(howMany: number, input: T): T extends string ? string : T; +take(howMany: number) : (input: T) => T extends string ? string : T; ```
-R.test source +R.take source ```javascript -export function test(pattern, str){ - if (arguments.length === 1) return _str => test(pattern, _str) +import baseSlice from './_internals/baseSlice.js' - if (typeof pattern === 'string'){ - throw new TypeError(`R.test requires a value of type RegExp as its first argument; received "${ pattern }"`) - } +export function take(howMany, listOrString){ + if (arguments.length === 1) + return _listOrString => take(howMany, _listOrString) + if (howMany < 0) return listOrString.slice() + if (typeof listOrString === 'string') return listOrString.slice(0, howMany) - return str.search(pattern) !== -1 + return baseSlice( + listOrString, 0, howMany + ) } ``` @@ -25741,16 +14391,28 @@ export function test(pattern, str){ Tests ```javascript -import { test as testMethod } from './test.js' +import { take } from './take.js' test('happy', () => { - expect(testMethod(/^x/, 'xyz')).toBeTrue() + const arr = [ 'foo', 'bar', 'baz' ] - expect(testMethod(/^y/)('xyz')).toBeFalse() + expect(take(1, arr)).toEqual([ 'foo' ]) + + expect(arr).toEqual([ 'foo', 'bar', 'baz' ]) + + expect(take(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar' ]) + expect(take(3, [ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar', 'baz' ]) + expect(take(4, [ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar', 'baz' ]) + expect(take(3)('rambda')).toBe('ram') }) -test('throws if first argument is not regex', () => { - expect(() => testMethod('foo', 'bar')).toThrowErrorMatchingInlineSnapshot('"R.test requires a value of type RegExp as its first argument; received "foo""') +test('with negative index', () => { + expect(take(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) + expect(take(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) +}) + +test('with zero index', () => { + expect(take(0, [ 1, 2, 3 ])).toEqual([]) }) ``` @@ -25761,77 +14423,88 @@ test('throws if first argument is not regex', () => { TypeScript test ```typescript -import {test} from 'rambda' +import {take} from 'rambda' -const input = 'foo ' -const regex = /foo/ +const list = [1, 2, 3, 4] +const str = 'foobar' +const howMany = 2 -describe('R.test', () => { +describe('R.take - array', () => { it('happy', () => { - const result = test(regex, input) + const result = take(howMany, list) - result // $ExpectType boolean + result // $ExpectType number[] }) it('curried', () => { - const result = test(regex)(input) + const result = take(howMany)(list) - result // $ExpectType boolean + result // $ExpectType number[] + }) +}) + +describe('R.take - string', () => { + it('happy', () => { + const result = take(howMany, str) + + result // $ExpectType string + }) + it('curried', () => { + const result = take(howMany)(str) + + result // $ExpectType string }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#test) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#take) -### times +### takeLast ```typescript -times(fn: (i: number) => T, howMany: number): T[] +takeLast(howMany: number, input: T): T extends string ? string : T ``` -It returns the result of applying function `fn` over members of range array. - -The range array includes numbers between `0` and `howMany`(exclusive). - -```javascript -const fn = x => x * 2 -const howMany = 5 - -R.times(fn, howMany) -// => [0, 2, 4, 6, 8] -``` +It returns the last `howMany` elements of `input`. -Try this R.times example in Rambda REPL +Try this R.takeLast example in Rambda REPL
All TypeScript definitions ```typescript -times(fn: (i: number) => T, howMany: number): T[]; -times(fn: (i: number) => T): (howMany: number) => T[]; +takeLast(howMany: number, input: T): T extends string ? string : T; +takeLast(howMany: number) : (input: T) => T extends string ? string : T; ```
-R.times source +R.takeLast source ```javascript -import { isInteger } from './_internals/isInteger.js' -import { map } from './map.js' -import { range } from './range.js' +import baseSlice from './_internals/baseSlice.js' -export function times(fn, howMany){ - if (arguments.length === 1) return _howMany => times(fn, _howMany) - if (!isInteger(howMany) || howMany < 0){ - throw new RangeError('n must be an integer') - } +export function takeLast(howMany, listOrString){ + if (arguments.length === 1) + return _listOrString => takeLast(howMany, _listOrString) - return map(fn, range(0, howMany)) + const len = listOrString.length + if (howMany < 0) return listOrString.slice() + let numValue = howMany > len ? len : howMany + + if (typeof listOrString === 'string') + return listOrString.slice(len - numValue) + + numValue = len - numValue + + return baseSlice( + listOrString, numValue, len + ) } ``` @@ -25842,30 +14515,29 @@ export function times(fn, howMany){ Tests ```javascript -import assert from 'assert' +import { takeLast } from './takeLast.js' -import { identity } from './identity.js' -import { times } from './times.js' +test('with arrays', () => { + expect(takeLast(1, [ 'foo', 'bar', 'baz' ])).toEqual([ 'baz' ]) -test('happy', () => { - const result = times(identity, 5) + expect(takeLast(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'bar', 'baz' ]) - expect(result).toEqual([ 0, 1, 2, 3, 4 ]) -}) + expect(takeLast(3, [ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar', 'baz' ]) -test('with bad input', () => { - assert.throws(() => { - times(3)('cheers!') - }, RangeError) - assert.throws(() => { - times(identity, -1) - }, RangeError) + expect(takeLast(4, [ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar', 'baz' ]) + + expect(takeLast(10, [ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar', 'baz' ]) }) -test('curry', () => { - const result = times(identity)(5) +test('with strings', () => { + expect(takeLast(3, 'rambda')).toBe('bda') - expect(result).toEqual([ 0, 1, 2, 3, 4 ]) + expect(takeLast(7, 'rambda')).toBe('rambda') +}) + +test('with negative index', () => { + expect(takeLast(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) + expect(takeLast(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ]) }) ``` @@ -25876,114 +14548,221 @@ test('curry', () => { TypeScript test ```typescript -import {times, identity} from 'rambda' +import {filter, piped, takeLast} from 'rambda' -describe('R.times', () => { +const list = [1, 2, 3, 4] +const str = 'foobar' +const howMany = 2 + +describe('R.takeLast - array', () => { it('happy', () => { - const result = times(identity, 5) + const result = takeLast(howMany, list) + + result // $ExpectType number[] + }) + it('curried', () => { + const result = takeLast(howMany)(list) + result // $ExpectType number[] + }) + it('real case', () => { + const data = ['foo', 'bar', 'baz', 'qux'] + const result = piped( + data, + filter( + x => x.length >= 100 + ), + takeLast(2), + ) + result // $ExpectType string[] + }) +}) + +describe('R.takeLast - string', () => { + it('happy', () => { + const result = takeLast(howMany, str) + + result // $ExpectType string + }) + it('curried', () => { + const result = takeLast(howMany)(str) + + result // $ExpectType string }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#times) - -### toLower - -```typescript +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#takeLast) + +### takeLastWhile + +```typescript + +takeLastWhile(predicate: (x: string) => boolean, input: string): string +``` + +Try this R.takeLastWhile example in Rambda REPL + +
+ +All TypeScript definitions + +```typescript +takeLastWhile(predicate: (x: string) => boolean, input: string): string; +takeLastWhile(predicate: (x: string) => boolean): (input: string) => string; +takeLastWhile(predicate: (x: T) => boolean, input: T[]): T[]; +takeLastWhile(predicate: (x: T) => boolean): (input: T[]) => T[]; +``` + +
+ +
+ +R.takeLastWhile source + +```javascript +import { isArray } from './_internals/isArray.js' + +export function takeLastWhile(predicate, input){ + if (arguments.length === 1){ + return _input => takeLastWhile(predicate, _input) + } + if (input.length === 0) return input + + const toReturn = [] + let counter = input.length + + while (counter){ + const item = input[ --counter ] + if (!predicate(item)){ + break + } + toReturn.push(item) + } + + return isArray(input) ? toReturn.reverse() : toReturn.reverse().join('') +} +``` + +
+ +
-toLower(str: S): Lowercase -``` +Tests ```javascript -R.toLower('FOO') -// => 'foo' -``` +import { takeLastWhile } from './takeLastWhile.js' +const assert = require('assert') -Try this R.toLower example in Rambda REPL +const list = [ 1, 2, 3, 4 ] -
+test('happy', () => { + const predicate = x => x > 2 + const result = takeLastWhile(predicate, list) + expect(result).toEqual([ 3, 4 ]) +}) -All TypeScript definitions +test('predicate is always true', () => { + const predicate = () => true + const result = takeLastWhile(predicate)(list) + expect(result).toEqual(list) +}) -```typescript -toLower(str: S): Lowercase; -toLower(str: string): string; +test('predicate is always false', () => { + const predicate = () => false + const result = takeLastWhile(predicate, list) + expect(result).toEqual([]) +}) + +test('with string', () => { + const result = takeLastWhile(x => x !== 'F', 'FOOBAR') + expect(result).toBe('OOBAR') +}) ```
-R.toLower source +TypeScript test -```javascript -export function toLower(str){ - return str.toLowerCase() -} -``` +```typescript +import {takeLastWhile} from 'rambda' -
+const list = [1, 2, 3] +const str = 'FOO' -
+describe('R.takeLastWhile', () => { + it('with array', () => { + const result = takeLastWhile(x => x > 1, list) -Tests + result // $ExpectType number[] + }) + it('with array - curried', () => { + const result = takeLastWhile(x => x > 1, list) -```javascript -import { toLower } from './toLower.js' + result // $ExpectType number[] + }) + it('with string', () => { + const result = takeLastWhile(x => x !== 'F', str) -test('toLower', () => { - expect(toLower('FOO|BAR|BAZ')).toBe('foo|bar|baz') + result // $ExpectType string + }) + it('with string - curried', () => { + const result = takeLastWhile(x => x !== 'F')(str) + + result // $ExpectType string + }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#toLower) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#takeLastWhile) -### toPairs +### takeWhile -```typescript +Try this R.takeWhile example in Rambda REPL -toPairs>(obj: O): Array<{ [key in K]: [`${key}`, O[key]] }[K]> -``` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#takeWhile) -It transforms an object to a list. +### tap -```javascript -const list = { - a : 1, - b : 2, - c : [ 3, 4 ], -} -const expected = [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', [ 3, 4 ] ] ] +```typescript -const result = R.toPairs(list) -// => `result` is equal to `expected` +tap(fn: (x: T) => void, input: T): T ``` -Try this R.toPairs example in Rambda REPL +It applies function `fn` to input `x` and returns `x`. + +One use case is debugging in the middle of `R.compose`. + +Try this R.tap example in Rambda REPL
All TypeScript definitions ```typescript -toPairs>(obj: O): Array<{ [key in K]: [`${key}`, O[key]] }[K]>; -toPairs(obj: Record): Array<[string, S]>; +tap(fn: (x: T) => void, input: T): T; +tap(fn: (x: T) => void): (input: T) => T; ```
-R.toPairs source +R.tap source ```javascript -export function toPairs(obj){ - return Object.entries(obj) +export function tap(fn, x){ + if (arguments.length === 1) return _x => tap(fn, _x) + + fn(x) + + return x } ``` @@ -25994,59 +14773,78 @@ export function toPairs(obj){ Tests ```javascript -import { toPairs } from './toPairs.js' +import { tap } from './tap.js' -const obj = { - a : 1, - b : 2, - c : [ 3, 4 ], -} -const expected = [ - [ 'a', 1 ], - [ 'b', 2 ], - [ 'c', [ 3, 4 ] ], -] +test('tap', () => { + let a = 1 + const sayX = x => a = x -test('happy', () => { - expect(toPairs(obj)).toEqual(expected) + expect(tap(sayX, 100)).toBe(100) + expect(tap(sayX)(100)).toBe(100) + expect(a).toBe(100) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#toPairs) +
-### toString +TypeScript test ```typescript +import {tap, pipe} from 'rambda' -toString(x: unknown): string +describe('R.tap', () => { + it('happy', () => { + pipe( + tap(x => { + x // $ExpectType number[] + }), + (x: number[]) => x.length + )([1, 2]) + }) +}) ``` -```javascript -R.toString([1, 2]) -// => '1,2' +
+ +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#tap) + +### test + +```typescript + +test(regExpression: RegExp): (str: string) => boolean ``` -Try this R.toString example in Rambda REPL +It determines whether `str` matches `regExpression`. + +Try this R.test example in Rambda REPL
All TypeScript definitions ```typescript -toString(x: unknown): string; +test(regExpression: RegExp): (str: string) => boolean; +test(regExpression: RegExp, str: string): boolean; ```
-R.toString source +R.test source ```javascript -export function toString(x){ - return x.toString() +export function test(pattern, str){ + if (arguments.length === 1) return _str => test(pattern, _str) + + if (typeof pattern === 'string'){ + throw new TypeError(`R.test requires a value of type RegExp as its first argument; received "${ pattern }"`) + } + + return str.search(pattern) !== -1 } ``` @@ -26057,111 +14855,89 @@ export function toString(x){ Tests ```javascript -import { toString } from './toString.js' +import { test as testMethod } from './test.js' test('happy', () => { - expect(toString([ 1, 2, 3 ])).toBe('1,2,3') -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#toString) - -### toUpper - -```typescript - -toUpper(str: S): Uppercase -``` - -```javascript -R.toUpper('foo') -// => 'FOO' -``` - -Try this R.toUpper example in Rambda REPL - -
+ expect(testMethod(/^x/, 'xyz')).toBeTrue() -All TypeScript definitions + expect(testMethod(/^y/)('xyz')).toBeFalse() +}) -```typescript -toUpper(str: S): Uppercase; -toUpper(str: string): string; +test('throws if first argument is not regex', () => { + expect(() => testMethod('foo', 'bar')).toThrowErrorMatchingInlineSnapshot('"R.test requires a value of type RegExp as its first argument; received "foo""') +}) ```
-R.toUpper source - -```javascript -export function toUpper(str){ - return str.toUpperCase() -} -``` +TypeScript test -
+```typescript +import {test} from 'rambda' -
+const input = 'foo ' +const regex = /foo/ -Tests +describe('R.test', () => { + it('happy', () => { + const result = test(regex, input) -```javascript -import { toUpper } from './toUpper.js' + result // $ExpectType boolean + }) + it('curried', () => { + const result = test(regex)(input) -test('toUpper', () => { - expect(toUpper('foo|bar|baz')).toBe('FOO|BAR|BAZ') + result // $ExpectType boolean + }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#toUpper) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#test) -### transpose +### times ```typescript -transpose(list: (T[])[]): (T[])[] +times(fn: (i: number) => T, howMany: number): T[] ``` -```javascript -const list = [[10, 11], [20], [], [30, 31, 32]] -const expected = [[10, 20, 30], [11, 31], [32]] +It returns the result of applying function `fn` over members of range array. -const result = R.transpose(list) -// => `result` is equal to `expected` -``` +The range array includes numbers between `0` and `howMany`(exclusive). -Try this R.transpose example in Rambda REPL +Try this R.times example in Rambda REPL
All TypeScript definitions ```typescript -transpose(list: (T[])[]): (T[])[]; +times(fn: (i: number) => T, howMany: number): T[]; +times(fn: (i: number) => T): (howMany: number) => T[]; ```
-R.transpose source +R.times source ```javascript -import { isArray } from './_internals/isArray.js' +import { isInteger } from './_internals/isInteger.js' +import { map } from './map.js' +import { range } from './range.js' -export function transpose(array){ - return array.reduce((acc, el) => { - el.forEach((nestedEl, i) => - isArray(acc[ i ]) ? acc[ i ].push(nestedEl) : acc.push([ nestedEl ])) +export function times(fn, howMany){ + if (arguments.length === 1) return _howMany => times(fn, _howMany) + if (!isInteger(howMany) || howMany < 0){ + throw new RangeError('n must be an integer') + } - return acc - }, []) + return map(fn, range(0, howMany)) } ``` @@ -26172,43 +14948,30 @@ export function transpose(array){ Tests ```javascript -import { transpose } from './transpose.js' +import assert from 'assert' + +import { identity } from './identity.js' +import { times } from './times.js' test('happy', () => { - const input = [ - [ 'a', 1 ], - [ 'b', 2 ], - [ 'c', 3 ], - ] + const result = times(identity, 5) - expect(transpose(input)).toEqual([ - [ 'a', 'b', 'c' ], - [ 1, 2, 3 ], - ]) + expect(result).toEqual([ 0, 1, 2, 3, 4 ]) }) -test('when rows are shorter', () => { - const actual = transpose([ [ 10, 11 ], [ 20 ], [], [ 30, 31, 32 ] ]) - const expected = [ [ 10, 20, 30 ], [ 11, 31 ], [ 32 ] ] - expect(actual).toEqual(expected) +test('with bad input', () => { + assert.throws(() => { + times(3)('cheers!') + }, RangeError) + assert.throws(() => { + times(identity, -1) + }, RangeError) }) -test('with empty array', () => { - expect(transpose([])).toEqual([]) -}) +test('curry', () => { + const result = times(identity)(5) -test('array with falsy values', () => { - const actual = transpose([ - [ true, false, undefined, null ], - [ null, undefined, false, true ], - ]) - const expected = [ - [ true, null ], - [ false, undefined ], - [ undefined, false ], - [ null, true ], - ] - expect(actual).toEqual(expected) + expect(result).toEqual([ 0, 1, 2, 3, 4 ]) }) ``` @@ -26219,58 +14982,47 @@ test('array with falsy values', () => { TypeScript test ```typescript -import {transpose} from 'rambda' - -const input = [ - ['a', 1], - ['b', 2], - ['c', 3], -] +import {times, identity} from 'rambda' -describe('R.transpose', () => { +describe('R.times', () => { it('happy', () => { - const result = transpose(input) - - result // $ExpectType (string | number)[][] + const result = times(identity, 5) + result // $ExpectType number[] }) }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#transpose) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#times) -### trim +### toLower ```typescript -trim(str: string): string -``` - -```javascript -R.trim(' foo ') -// => 'foo' +toLower(str: S): Lowercase ``` -Try this R.trim example in Rambda REPL +Try this R.toLower example in Rambda REPL
All TypeScript definitions ```typescript -trim(str: string): string; +toLower(str: S): Lowercase; +toLower(str: string): string; ```
-R.trim source +R.toLower source ```javascript -export function trim(str){ - return str.trim() +export function toLower(str){ + return str.toLowerCase() } ``` @@ -26281,90 +15033,46 @@ export function trim(str){ Tests ```javascript -import { trim } from './trim.js' +import { toLower } from './toLower.js' -test('trim', () => { - expect(trim(' foo ')).toBe('foo') +test('toLower', () => { + expect(toLower('FOO|BAR|BAZ')).toBe('foo|bar|baz') }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#trim) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#toLower) -### tryCatch +### toPairs ```typescript -tryCatch( - fn: (input: T) => U, - fallback: U -): (input: T) => U +toPairs>(obj: O): Array<{ [key in K]: [`${key}`, O[key]] }[K]> ``` -It returns function that runs `fn` in `try/catch` block. If there was an error, then `fallback` is used to return the result. Note that `fn` can be value or asynchronous/synchronous function(unlike `Ramda` where fallback can only be a synchronous function). - -> :boom: Please check the tests of `R.tryCatch` to fully understand how this method works. - -```javascript -const fn = x => x.foo - -const result = [ - R.tryCatch(fn, false)(null), - R.tryCatch(fn, false)({foo: 'bar'}) -] -// => [false, 'bar'] -``` +It transforms an object to a list. -Try this R.tryCatch example in Rambda REPL +Try this R.toPairs example in Rambda REPL
All TypeScript definitions ```typescript -tryCatch( - fn: (input: T) => U, - fallback: U -): (input: T) => U; -tryCatch( - fn: (input: T) => U, - fallback: (input: T) => U -): (input: T) => U; -tryCatch( - fn: (input: any) => Promise, - fallback: T -): (input: any) => Promise; -tryCatch( - fn: (input: any) => Promise, - fallback: (input: any) => Promise, -): (input: any) => Promise; +toPairs>(obj: O): Array<{ [key in K]: [`${key}`, O[key]] }[K]>; +toPairs(obj: Record): Array<[string, S]>; ```
-R.tryCatch source +R.toPairs source ```javascript -import { type } from './type.js' - -const isFunction = x => [ 'Promise', 'Function' ].includes(type(x)) - -export function tryCatch(fn, fallback){ - if (!isFunction(fn)){ - throw new Error(`R.tryCatch | fn '${ fn }'`) - } - const passFallback = isFunction(fallback) - - return (...inputs) => { - try { - return fn(...inputs) - } catch (e){ - return passFallback ? fallback(e, ...inputs) : fallback - } - } +export function toPairs(obj){ + return Object.entries(obj) } ``` @@ -26375,244 +15083,156 @@ export function tryCatch(fn, fallback){ Tests ```javascript -import { tryCatch as tryCatchRamda } from 'ramda' +import { toPairs } from './toPairs.js' -import { compareCombinations } from './_internals/testUtils.js' -import { prop } from './prop.js' -import { tryCatch } from './tryCatch.js' +const obj = { + a : 1, + b : 2, + c : [ 3, 4 ], +} +const expected = [ + [ 'a', 1 ], + [ 'b', 2 ], + [ 'c', [ 3, 4 ] ], +] test('happy', () => { - const fn = () => { - throw new Error('foo') - } - const result = tryCatch(fn, () => true)() - expect(result).toBeTrue() + expect(toPairs(obj)).toEqual(expected) }) +``` -test('when fallback is used', () => { - const fn = x => x.x - - expect(tryCatch(fn, false)(null)).toBeFalse() -}) +
-test('with json parse', () => { - const good = () => JSON.parse(JSON.stringify({ a : 1 })) - const bad = () => JSON.parse('a{a') +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#toPairs) - expect(tryCatch(good, 1)()).toEqual({ a : 1 }) - expect(tryCatch(bad, 1)()).toBe(1) -}) +### toString -test('when fallback is function', () => { - const fn = x => x.x +```typescript - expect(tryCatch(fn, () => 1)(null)).toBe(1) -}) +toString(x: unknown): string +``` -test('when fn is used', () => { - const fn = prop('x') +Try this R.toString example in Rambda REPL - expect(tryCatch(fn, false)({})).toBeUndefined() - expect(tryCatch(fn, false)({ x : 1 })).toBe(1) -}) +
-test('fallback receives error object and all initial inputs', () => { - function thrower( - a, b, c - ){ - void c - throw new Error('throwerError') - } +All TypeScript definitions - function catchFn( - e, a, b, c - ){ - return [ e.message, a, b, c ].join('|') - } +```typescript +toString(x: unknown): string; +``` - const willThrow = tryCatch(thrower, catchFn) - const result = willThrow( - 'A', 'B', 'C' - ) - expect(result).toBe('throwerError|A|B|C') -}) +
-test('fallback receives error object', () => { - function throwFn(){ - throw new Error(10) - } +
- function eCatcher( - e, a, b - ){ - return e.message - } +R.toString source - const willThrow = tryCatch(throwFn, eCatcher) - expect(willThrow([])).toBe('10') - expect(willThrow([ {}, {}, {} ])).toBe('10') -}) +```javascript +export function toString(x){ + return x.toString() +} +``` -const possibleFns = [ - null, - () => 1, - () => 0, - () => JSON.parse('{a:1'), - () => { - const x = {} +
- return x.x - }, - x => x.foo, - () => { - throw new Error('foo') - }, -] +
-const possibleCatchers = [ - null, - e => e.message.length, - (e, ...inputs) => `${ e.message.length } ${ inputs.length }`, - () => { - throw new Error('bar') - }, -] +Tests -const possibleInputs = [ null, {}, { foo : 1 } ] +```javascript +import { toString } from './toString.js' -describe('brute force', () => { - compareCombinations({ - returnsFunctionFlag : true, - firstInput : possibleFns, - callback : errorsCounters => { - expect(errorsCounters).toMatchInlineSnapshot(` - { - "ERRORS_MESSAGE_MISMATCH": 0, - "ERRORS_TYPE_MISMATCH": 12, - "RESULTS_MISMATCH": 0, - "SHOULD_NOT_THROW": 0, - "SHOULD_THROW": 7, - "TOTAL_TESTS": 84, - } - `) - }, - secondInput : possibleCatchers, - thirdInput : possibleInputs, - fn : tryCatch, - fnRamda : tryCatchRamda, - }) +test('happy', () => { + expect(toString([ 1, 2, 3 ])).toBe('1,2,3') }) ```
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#toString) + +### toUpper + +```typescript + +toUpper(str: S): Uppercase +``` + +Try this R.toUpper example in Rambda REPL +
-TypeScript test +All TypeScript definitions ```typescript -import {tryCatch, delay} from 'rambda' +toUpper(str: S): Uppercase; +toUpper(str: string): string; +``` -describe('R.tryCatch', () => { - it('synchronous', () => { - const fn = (x: any) => x.x === 1 +
- const result = tryCatch(fn, false)(null) - result // $ExpectType boolean - }) - it('synchronous + fallback is function', () => { - const fn = (x: any) => typeof x.x - const fallback = (x: any) => typeof x - const result = tryCatch(fn, fallback)(null) - result // $ExpectType string - }) +
- it('asynchronous', async() => { - const fn = async(input: any) => { - return typeof JSON.parse('{a:') - } - const result = await tryCatch(fn, 'fallback')(100) - result // $ExpectType string - }) +R.toUpper source - it('asynchronous + fallback is asynchronous', async() => { - const fn = async(input: any) => { - await delay(100) - return JSON.parse(`{a:${input}`) - } - const fallback = async(input: any) => { - await delay(100) - return 'foo' - } - const result = await tryCatch(fn, fallback)(100) - result // $ExpectType string - }) +```javascript +export function toUpper(str){ + return str.toUpperCase() +} +``` + +
+ +
+ +Tests + +```javascript +import { toUpper } from './toUpper.js' + +test('toUpper', () => { + expect(toUpper('foo|bar|baz')).toBe('FOO|BAR|BAZ') }) ```
-[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#tryCatch) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#toUpper) -### type +### transpose ```typescript -type(x: any): RambdaTypes -``` - -It accepts any input and it returns its type. - -> :boom: `NaN`, `Promise` and `Async` are types specific for **Rambda**. - -```javascript -const result = R.type(() => {}) // => 'Function' -R.type(async () => {}) // => 'Async' -R.type([]) // => 'Array' -R.type({}) // => 'Object' -R.type('foo') // => 'String' -R.type(1) // => 'Number' -R.type(true) // => 'Boolean' -R.type(null) // => 'Null' -R.type(/[A-z]/) // => 'RegExp' -R.type('foo'*1) // => 'NaN' - -const delay = ms => new Promise(resolve => { - setTimeout(function () { - resolve() - }, ms) -}) -R.type(delay) // => 'Promise' +transpose(list: (T[])[]): (T[])[] ``` -Try this R.type example in Rambda REPL +Try this R.transpose example in Rambda REPL
All TypeScript definitions ```typescript -type(x: any): RambdaTypes; +transpose(list: (T[])[]): (T[])[]; ```
-R.type source +R.transpose source ```javascript -export function type(input){ - if (input === null){ - return 'Null' - } else if (input === undefined){ - return 'Undefined' - } else if (Number.isNaN(input)){ - return 'NaN' - } - const typeResult = Object.prototype.toString.call(input).slice(8, -1) +import { isArray } from './_internals/isArray.js' + +export function transpose(array){ + return array.reduce((acc, el) => { + el.forEach((nestedEl, i) => + isArray(acc[ i ]) ? acc[ i ].push(nestedEl) : acc.push([ nestedEl ])) - return typeResult === 'AsyncFunction' ? 'Promise' : typeResult + return acc + }, []) } ``` @@ -26623,199 +15243,134 @@ export function type(input){ Tests ```javascript -import { type as typeRamda } from 'ramda' - -import { type } from './type.js' - -test('with buffer', () => { - expect(type(new Buffer.from('foo'))).toBe('Uint8Array') -}) - -test('with array buffer', () => { - expect(type(new ArrayBuffer(8))).toBe('ArrayBuffer') -}) - -test('with big int', () => { - expect(type(BigInt(9007199254740991))).toBe('BigInt') -}) - -test('with generators', () => { - function* generator(){ - yield 1 - yield 2 - yield 3 - } - - const gen = generator() - expect(type(generator)).toBe('GeneratorFunction') - expect(type(gen)).toBe('Generator') -}) - -test('with Date', () => { - const date = new Date('December 17, 1995 03:24:00') - expect(type(date)).toBe('Date') -}) +import { transpose } from './transpose.js' -test('with infinity', () => { - expect(type(Infinity)).toBe('Number') -}) +test('happy', () => { + const input = [ + [ 'a', 1 ], + [ 'b', 2 ], + [ 'c', 3 ], + ] -test('with weak map', () => { - expect(type(new WeakMap())).toBe('WeakMap') + expect(transpose(input)).toEqual([ + [ 'a', 'b', 'c' ], + [ 1, 2, 3 ], + ]) }) -test('with map', () => { - expect(type(new Map())).toBe('Map') +test('when rows are shorter', () => { + const actual = transpose([ [ 10, 11 ], [ 20 ], [], [ 30, 31, 32 ] ]) + const expected = [ [ 10, 20, 30 ], [ 11, 31 ], [ 32 ] ] + expect(actual).toEqual(expected) }) -test('with symbol', () => { - expect(type(Symbol())).toBe('Symbol') +test('with empty array', () => { + expect(transpose([])).toEqual([]) }) -test('with simple promise', () => { - expect(type(Promise.resolve(1))).toBe('Promise') +test('array with falsy values', () => { + const actual = transpose([ + [ true, false, undefined, null ], + [ null, undefined, false, true ], + ]) + const expected = [ + [ true, null ], + [ false, undefined ], + [ undefined, false ], + [ null, true ], + ] + expect(actual).toEqual(expected) }) +``` -test('with new Boolean', () => { - expect(type(new Boolean(true))).toBe('Boolean') -}) +
-test('with new String', () => { - expect(type(new String('I am a String object'))).toBe('String') -}) +
-test('with new Number', () => { - expect(type(new Number(1))).toBe('Number') -}) +TypeScript test -test('with error', () => { - expect(type(Error('foo'))).toBe('Error') - expect(typeRamda(Error('foo'))).toBe('Error') -}) +```typescript +import {transpose} from 'rambda' -test('with error - wrong @types/ramda test', () => { - // @types/ramda expect the result to be 'Error' but it is not - class ExtendedError extends Error{} - expect(type(ExtendedError)).toBe('Function') - expect(typeRamda(ExtendedError)).toBe('Function') -}) +const input = [ + ['a', 1], + ['b', 2], + ['c', 3], +] -test('with new promise', () => { - const delay = ms => - new Promise(resolve => { - setTimeout(() => { - resolve(ms + 110) - }, ms) - }) +describe('R.transpose', () => { + it('happy', () => { + const result = transpose(input) - expect(type(delay(10))).toBe('Promise') + result // $ExpectType (string | number)[][] + }) }) +``` -test('async function', () => { - expect(type(async () => {})).toBe('Promise') -}) +
-test('async arrow', () => { - const asyncArrow = async () => {} - expect(type(asyncArrow)).toBe('Promise') -}) +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#transpose) -test('function', () => { - const fn1 = () => {} - const fn2 = function (){} +### trim - function fn3(){} +```typescript - ;[ () => {}, fn1, fn2, fn3 ].map(val => { - expect(type(val)).toBe('Function') - }) -}) +trim(str: string): string +``` -test('object', () => { - expect(type({})).toBe('Object') -}) +Try this R.trim example in Rambda REPL -test('number', () => { - expect(type(1)).toBe('Number') -}) +
-test('boolean', () => { - expect(type(false)).toBe('Boolean') -}) +All TypeScript definitions -test('string', () => { - expect(type('foo')).toBe('String') -}) +```typescript +trim(str: string): string; +``` -test('null', () => { - expect(type(null)).toBe('Null') -}) +
-test('array', () => { - expect(type([])).toBe('Array') - expect(type([ 1, 2, 3 ])).toBe('Array') -}) +
-test('regex', () => { - expect(type(/\s/g)).toBe('RegExp') -}) +R.trim source -test('undefined', () => { - expect(type(undefined)).toBe('Undefined') -}) +```javascript +export function trim(str){ + return str.trim() +} +``` -test('not a number', () => { - expect(type(Number('s'))).toBe('NaN') -}) +
-test('set', () => { - const exampleSet = new Set([ 1, 2, 3 ]) - expect(type(exampleSet)).toBe('Set') - expect(typeRamda(exampleSet)).toBe('Set') -}) +
-test('function inside object 1', () => { - const obj = { - f(){ - return 4 - }, - } +Tests - expect(type(obj.f)).toBe('Function') - expect(typeRamda(obj.f)).toBe('Function') -}) +```javascript +import { trim } from './trim.js' -test('function inside object 2', () => { - const name = 'f' - const obj = { - [ name ](){ - return 4 - }, - } - expect(type(obj.f)).toBe('Function') - expect(typeRamda(obj.f)).toBe('Function') +test('trim', () => { + expect(trim(' foo ')).toBe('foo') }) ```
-
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#trim) -TypeScript test +### tryCatch -```typescript -import {type} from 'rambda' +It returns function that runs `fn` in `try/catch` block. If there was an error, then `fallback` is used to return the result. Note that `fn` can be value or asynchronous/synchronous function(unlike `Ramda` where fallback can only be a synchronous function). -describe('R.type', () => { - it('happy', () => { - const result = type(4) +Try this R.tryCatch example in Rambda REPL - result // $ExpectType RambdaTypes - }) -}) -``` +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#tryCatch) -
+### type + +It accepts any input and it returns its type. + +Try this R.type example in Rambda REPL [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#type) @@ -26830,11 +15385,6 @@ It calls a function `fn` with the list of values of the returned function. `R.unapply` is the opposite of `R.apply` method. -```javascript -R.unapply(JSON.stringify)(1, 2, 3) -//=> '[1,2,3]' -``` - Try this R.unapply example in Rambda REPL
@@ -26997,11 +15547,6 @@ It takes two lists and return a new list containing a merger of both list with r `R.equals` is used to compare for duplication. -```javascript -const result = R.union([1,2,3], [3,4,5]); -// => [1, 2, 3, 4, 5] -``` - Try this R.union example in Rambda REPL
@@ -27123,13 +15668,6 @@ It returns a new array containing only one copy of each element of `list`. `R.equals` is used to determine equality. -```javascript -const list = [1, 1, {a: 1}, {a: 2}, {a:1}] - -R.uniq(list) -// => [1, {a: 1}, {a: 2}] -``` - Try this R.uniq example in Rambda REPL
@@ -27186,115 +15724,15 @@ test('with nested array', () => { }) test('with booleans', () => { - expect(uniq([ [ false ], [ false ], [ true ] ])).toEqual([ [ false ], [ true ] ]) -}) - -test('with falsy values', () => { - expect(uniq([ undefined, null ])).toEqual([ undefined, null ]) -}) - -test('can distinct between string and number', () => { - expect(uniq([ 1, '1' ])).toEqual([ 1, '1' ]) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {uniq} from 'rambda' - -describe('R.uniq', () => { - it('happy', () => { - const result = uniq([1, 2, 3, 3, 3, 1, 2, 0]) - result // $ExpectType number[] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#uniq) - -### uniqBy - -```typescript - -uniqBy(fn: (a: T) => U, list: T[]): T[] -``` - -It applies uniqueness to input list based on function that defines what to be used for comparison between elements. - -`R.equals` is used to determine equality. - -```javascript -const list = [{a:1}, {a:2}, {a:1}] -const result = R.uniqBy(x => x, list) - -// => [{a:1}, {a:2}] -``` - -Try this R.uniqBy example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -uniqBy(fn: (a: T) => U, list: T[]): T[]; -uniqBy(fn: (a: T) => U): (list: T[]) => T[]; -``` - -
- -
- -R.uniqBy source - -```javascript -import { _Set } from '../src/_internals/set.js' - -export function uniqBy(fn, list){ - if (arguments.length === 1){ - return _list => uniqBy(fn, _list) - } - const set = new _Set() - - return list.filter(item => set.checkUniqueness(fn(item))) -} -``` - -
- -
- -Tests - -```javascript -import { uniqBy as uniqByRamda } from 'ramda' - -import { uniqBy } from './uniqBy.js' - -test('happy', () => { - expect(uniqBy(Math.abs, [ -2, -1, 0, 1, 2 ])).toEqual([ -2, -1, 0 ]) -}) - -test('keeps elements from the left', () => { - expect(uniqBy(Math.abs, [ -1, 2, 4, 3, 1, 3 ])).toEqual([ -1, 2, 4, 3 ]) + expect(uniq([ [ false ], [ false ], [ true ] ])).toEqual([ [ false ], [ true ] ]) }) -test('returns an empty array for an empty array', () => { - expect(uniqBy(Math.abs, [])).toEqual([]) +test('with falsy values', () => { + expect(uniq([ undefined, null ])).toEqual([ undefined, null ]) }) -test('uses R.uniq', () => { - const list = [ { a : 1 }, { a : 2 }, { a : 1 } ] - const expected = [ { a : 1 }, { a : 2 } ] - expect(uniqBy(x => x, list)).toEqual(expected) - expect(uniqByRamda(x => x, list)).toEqual(expected) +test('can distinct between string and number', () => { + expect(uniq([ 1, '1' ])).toEqual([ 1, '1' ]) }) ``` @@ -27305,17 +15743,11 @@ test('uses R.uniq', () => { TypeScript test ```typescript -import {uniqBy} from 'rambda' +import {uniq} from 'rambda' -describe('R.uniqBy', () => { +describe('R.uniq', () => { it('happy', () => { - const result = uniqBy(Math.abs, [-2, -1, 0]) - - result // $ExpectType number[] - }) - it('curried', () => { - const result = uniqBy(Math.abs)([-2, -1, 0]) - + const result = uniq([1, 2, 3, 3, 3, 1, 2, 0]) result // $ExpectType number[] }) }) @@ -27323,6 +15755,16 @@ describe('R.uniqBy', () => {
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#uniq) + +### uniqBy + +It applies uniqueness to input list based on function that defines what to be used for comparison between elements. + +`R.equals` is used to determine equality. + +Try this R.uniqBy example in Rambda REPL + [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#uniqBy) ### uniqWith @@ -27336,27 +15778,6 @@ It returns a new array containing only one copy of each element in `list` accord This predicate should return true, if two elements are equal. -```javascript -const list = [ - {id: 0, title:'foo'}, - {id: 1, title:'bar'}, - {id: 2, title:'baz'}, - {id: 3, title:'foo'}, - {id: 4, title:'bar'}, -] - -const expected = [ - {id: 0, title:'foo'}, - {id: 1, title:'bar'}, - {id: 2, title:'baz'}, -] - -const predicate = (x,y) => x.title === y.title - -const result = R.uniqWith(predicate, list) -// => `result` is equal to `expected` -``` - Try this R.uniqWith example in Rambda REPL
@@ -27480,19 +15901,6 @@ If `predicate(input)` returns `false`, then the end result will be the outcome o In the other case, the final output will be the `input` itself. -```javascript -const fn = R.unless( - x => x > 2, - x => x + 10 -) - -const result = [ - fn(1), - fn(5) -] -// => [11, 5] -``` - Try this R.unless example in Rambda REPL
@@ -27535,290 +15943,22 @@ export const unless = curry(unlessFn) ```javascript import { inc } from './inc.js' import { isNil } from './isNil.js' -import { unless } from './unless.js' - -test('happy', () => { - const safeInc = unless(isNil, inc) - expect(safeInc(null)).toBeNull() - expect(safeInc(1)).toBe(2) -}) - -test('curried', () => { - const safeIncCurried = unless(isNil)(inc) - expect(safeIncCurried(null)).toBeNull() -}) - -test('with 3 inputs', () => { - let result = unless(x => x.startsWith('/'), x=> x.concat('/'), '/api') - expect(result).toBe('/api') -}) -``` - -
- -
- -TypeScript test - -```typescript -import {unless, inc} from 'rambda' - -describe('R.unless', () => { - it('happy', () => { - const fn = unless(x => x > 5, inc) - const result = fn(1) - result // $ExpectType number - }) - it('with one explicit type', () => { - const result = unless( - x => { - x // $ExpectType number - return x > 5 - }, - x => { - x // $ExpectType number - return x + 1 - }, - 1 - ) - result // $ExpectType number - }) - it('with two different explicit types', () => { - const result = unless( - x => { - x // $ExpectType number - return x > 5 - }, - x => { - x // $ExpectType number - return `${x}-foo` - }, - 1 - ) - result // $ExpectType string | number - }) -}) - -describe('R.unless - curried', () => { - it('happy', () => { - const fn = unless(x => x > 5, inc) - const result = fn(1) - result // $ExpectType number - }) - it('with one explicit type', () => { - const fn = unless( - x => { - x // $ExpectType number - return x > 5 - }, - x => { - x // $ExpectType number - return x + 1 - } - ) - const result = fn(1) - result // $ExpectType number - }) - it('with two different explicit types', () => { - const fn = unless( - x => { - x // $ExpectType number - return x > 5 - }, - x => { - x // $ExpectType number - return `${x}-foo` - } - ) - const result = fn(1) - result // $ExpectType string | number - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#unless) - -### unnest - -```typescript - -unnest(list: unknown[]): unknown[] -``` - -```javascript -const result = R.unnest([1, [2], [[3]]]) -// => [1, 2, [3]] -``` - -Try this R.unnest example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -unnest(list: unknown[]): unknown[]; -unnest(list: unknown[]): T; -``` - -
- -
- -R.unnest source - -```javascript -export function unnest(list){ - return list.reduce((acc, item) => { - if (Array.isArray(item)){ - return [ ...acc, ...item ] - } - - return [ ...acc, item ] - }, []) -} -``` - -
- -
- -Tests - -```javascript -import { unnest } from './unnest.js' - -test('happy', () => { - const result = unnest([ 1, [ 2 ], [ [ 3 ] ] ]) - expect(result).toEqual([ 1, 2, [ 3 ] ]) -}) - -test('has no effect on simple list', () => { - const list = [ 1, 2 ] - const result = unnest(list) - expect(result).toEqual([ 1, 2 ]) -}) - -test('flattens an array of empty arrays', () => { - const list = [ [], [], [] ] - const result = unnest(list) - expect(result).toEqual([]) -}) -``` - -
- -
- -TypeScript test - -```typescript -import {unnest} from 'rambda' - -const list = [1, [2], [[3]]] - -describe('R.unnest', () => { - it('without passing type', () => { - const result = unnest(list) - - result // $ExpectType unknown[] - }) - it('with passing type', () => { - const result = unnest<[number, number, number[]]>([1, [2], [[3]]]) - - result // $ExpectType [number, number, number[]] - }) -}) -``` - -
- -[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#unnest) - -### unwind - -```typescript - -unwind(prop: keyof T, obj: T): U[] -``` - -```javascript -const obj = { - a: 1, - b: [2, 3], -} -const result = R.unwind('b', obj) -const expected = [{a:1, b:2}, {a:1, b:3}] -// => `result` is equal to `expected` -``` - -Try this R.unwind example in Rambda REPL - -
- -All TypeScript definitions - -```typescript -unwind(prop: keyof T, obj: T): U[]; -unwind(prop: keyof T): (obj: T) => U[]; -``` - -
- -
- -R.unwind source - -```javascript -import { isArray } from './_internals/isArray.js' -import { mapArray } from './map.js' - -export function unwind(property, obj){ - if (arguments.length === 1){ - return _obj => unwind(property, _obj) - } - - if (!isArray(obj[ property ])) return [ obj ] - - return mapArray(x => ({ - ...obj, - [ property ] : x, - }), - obj[ property ]) -} -``` - -
- -
- -Tests - -```javascript -import { unwind } from './unwind.js' +import { unless } from './unless.js' test('happy', () => { - const obj = { - a : 1, - b : [ 2, 3 ], - c : [ 3, 4 ], - } - const expected = [ - { - a : 1, - b : 2, - c : [ 3, 4 ], - }, - { - a : 1, - b : 3, - c : [ 3, 4 ], - }, - ] - const result = unwind('b', obj) - expect(result).toEqual(expected) + const safeInc = unless(isNil, inc) + expect(safeInc(null)).toBeNull() + expect(safeInc(1)).toBe(2) +}) + +test('curried', () => { + const safeIncCurried = unless(isNil)(inc) + expect(safeIncCurried(null)).toBeNull() +}) + +test('with 3 inputs', () => { + let result = unless(x => x.startsWith('/'), x=> x.concat('/'), '/api') + expect(result).toBe('/api') }) ``` @@ -27829,32 +15969,95 @@ test('happy', () => { TypeScript test ```typescript -import {unwind} from 'rambda' - -interface Input { - a: number, - b: number[], -} -interface Output { - a: number, - b: number, -} +import {unless, inc} from 'rambda' -describe('R.unwind', () => { +describe('R.unless', () => { it('happy', () => { - const obj = { - a: 1, - b: [2, 3], - } - const result = unwind('b', obj) + const fn = unless(x => x > 5, inc) + const result = fn(1) + result // $ExpectType number + }) + it('with one explicit type', () => { + const result = unless( + x => { + x // $ExpectType number + return x > 5 + }, + x => { + x // $ExpectType number + return x + 1 + }, + 1 + ) + result // $ExpectType number + }) + it('with two different explicit types', () => { + const result = unless( + x => { + x // $ExpectType number + return x > 5 + }, + x => { + x // $ExpectType number + return `${x}-foo` + }, + 1 + ) + result // $ExpectType string | number + }) +}) - result // $ExpectType Output[] +describe('R.unless - curried', () => { + it('happy', () => { + const fn = unless(x => x > 5, inc) + const result = fn(1) + result // $ExpectType number + }) + it('with one explicit type', () => { + const fn = unless( + x => { + x // $ExpectType number + return x > 5 + }, + x => { + x // $ExpectType number + return x + 1 + } + ) + const result = fn(1) + result // $ExpectType number + }) + it('with two different explicit types', () => { + const fn = unless( + x => { + x // $ExpectType number + return x > 5 + }, + x => { + x // $ExpectType number + return `${x}-foo` + } + ) + const result = fn(1) + result // $ExpectType string | number }) }) ```
+[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#unless) + +### unnest + +Try this R.unnest example in Rambda REPL + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#unnest) + +### unwind + +Try this R.unwind example in Rambda REPL + [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#unwind) ### update @@ -27866,15 +16069,6 @@ update(index: number, newValue: T, list: T[]): T[] It returns a copy of `list` with updated element at `index` with `newValue`. -```javascript -const index = 2 -const newValue = 88 -const list = [1, 2, 3, 4, 5] - -const result = R.update(index, newValue, list) -// => [1, 2, 88, 4, 5] -``` - Try this R.update example in Rambda REPL
@@ -27995,13 +16189,6 @@ values(obj: T): T[K][] With correct input, this is nothing more than `Object.values(Record)`. If `obj` is not an object, then it returns an empty array. -```javascript -const obj = {a:1, b:2} - -R.values(Record) -// => [1, 2] -``` - Try this R.values example in Rambda REPL
@@ -28091,13 +16278,6 @@ view(lens: Lens): (obj: S) => A It returns the value of `lens` focus over `target` object. -```javascript -const lens = R.lensProp('x') - -R.view(lens, {x: 1, y: 2}) // => 1 -R.view(lens, {x: 4, y: 2}) // => 4 -``` - Try this R.view example in Rambda REPL
@@ -28162,27 +16342,6 @@ when(predicate: (x: T) => boolean, whenTrueFn: (a: T) => U, input: T): T | It pass `input` to `predicate` function and if the result is `true`, it will return the result of `whenTrueFn(input)`. If the `predicate` returns `false`, then it will simply return `input`. -```javascript -const predicate = x => typeof x === 'number' -const whenTrueFn = R.add(11) - -const fn = when(predicate, whenTrueResult) - -const positiveInput = 88 -const negativeInput = 'foo' - -const result = [ - fn(positiveInput), - fn(positiveInput), -] - -const expected = [ - 99, - 'foo', -] -// => `result` is equal to `expected` -``` - Try this R.when example in Rambda REPL
@@ -28279,21 +16438,6 @@ where(conditions: T, input: U): boolean It returns `true` if all each property in `conditions` returns `true` when applied to corresponding property in `input` object. -```javascript -const condition = R.where({ - a : x => typeof x === "string", - b : x => x === 4 -}) -const input = { - a : "foo", - b : 4, - c : 11, -} - -const result = condition(input) -// => true -``` - Try this R.where example in Rambda REPL
@@ -28411,109 +16555,10 @@ describe('R.where', () => { ### whereAny -```typescript - -whereAny(conditions: T, input: U): boolean -``` - Same as `R.where`, but it will return `true` if at least one condition check returns `true`. -```javascript -const conditions = { - a: a => a > 1, - b: b => b > 2, -} -const result = [ - R.whereAny(conditions, {b:3}), - R.whereAny(conditions, {c:4}) -] -// => [true, false] -``` - Try this R.whereAny example in Rambda REPL -
- -All TypeScript definitions - -```typescript -whereAny(conditions: T, input: U): boolean; -whereAny(conditions: T): (input: U) => boolean; -whereAny(conditions: ObjFunc2, input: U): boolean; -whereAny(conditions: ObjFunc2): (input: U) => boolean; -``` - -
- -
- -R.whereAny source - -```javascript -export function whereAny(conditions, input){ - if (input === undefined){ - return _input => whereAny(conditions, _input) - } - for (const prop in conditions){ - if (conditions[ prop ](input[ prop ])){ - return true - } - } - - return false -} -``` - -
- -
- -Tests - -```javascript -import { equals } from './equals.js' -import { whereAny } from './whereAny.js' - -const conditions = { - a : equals('foo'), - b : equals('bar'), -} - -test('happy', () => { - expect(whereAny(conditions, { - a : 1, - b : 'bar', - })).toBeTrue() -}) - -test('curried', () => { - expect(whereAny(conditions)({ a : 1 })).toBeFalse() -}) -``` - -
- -
- -TypeScript test - -```typescript -import {whereAny} from 'rambda' - -describe('R.whereAny', () => { - it('happy', () => { - const conditions = { - a: (a: number) => a > 1, - b: (b: number) => b > 2, - } - const result = whereAny(conditions, {b: 3}) - result // $ExpectType boolean - }) -}) -``` - -
- [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#whereAny) ### whereEq @@ -28527,17 +16572,6 @@ It will return `true` if all of `input` object fully or partially include `rule` `R.equals` is used to determine equality. -```javascript -const condition = { a : { b : 1 } } -const input = { - a : { b : 1 }, - c : 2 -} - -const result = whereEq(condition, input) -// => true -``` - Try this R.whereEq example in Rambda REPL
@@ -28658,14 +16692,6 @@ It will return a new array, based on all members of `source` list that are not p `R.equals` is used to determine equality. -```javascript -const source = [1, 2, 3, 4] -const matchAgainst = [2, 3] - -const result = R.without(matchAgainst, source) -// => [1, 4] -``` - Try this R.without example in Rambda REPL
@@ -28780,15 +16806,6 @@ xor(x: boolean, y: boolean): boolean Logical XOR -```javascript -const result = [ - xor(true, true), - xor(false, false), - xor(false, true), -] -// => [false, false, true] -``` - Try this R.xor example in Rambda REPL
@@ -28902,17 +16919,6 @@ It will return a new array containing tuples of equally positions items from bot The returned list will be truncated to match the length of the shortest supplied list. -```javascript -const x = [1, 2] -const y = ['A', 'B'] -R.zip(x, y) -// => [[1, 'A'], [2, 'B']] - -// truncates to shortest list -R.zip([...x, 3], ['A', 'B']) -// => [[1, 'A'], [2, 'B']] -``` - Try this R.zip example in Rambda REPL
@@ -29022,17 +17028,6 @@ zipObj(keys: K[], values: T[]): { [P in K]: T } It will return a new object with keys of `keys` array and values of `values` array. -```javascript -const keys = ['a', 'b', 'c'] - -R.zipObj(keys, [1, 2, 3]) -// => {a: 1, b: 2, c: 3} - -// truncates to shortest list -R.zipObj(keys, [1, 2]) -// => {a: 1, b: 2} -``` - Try this R.zipObj example in Rambda REPL
@@ -29147,16 +17142,6 @@ describe('R.zipObj', () => { zipWith(fn: (x: T, y: U) => TResult, list1: T[], list2: U[]): TResult[] ``` -```javascript -const list1 = [ 10, 20, 30, 40 ] -const list2 = [ 100, 200 ] - -const result = R.zipWith( - R.add, list1, list2 -) -// => [110, 220] -``` - Try this R.zipWith example in Rambda REPL
@@ -29261,12 +17246,27 @@ describe('R.zipWith', () => { ## ❯ CHANGELOG -9.4.2 +9.4.2 -- Fix bug with `R.differenceWith` when two arrays has same length - [Issue #750](https://github.com/selfrefactor/rambda/issues/757) +- Fix TS issue when `R.take` is used as part of `R.pipe`. + +Moving away from `Ramda` types which are problematic in this case: + +```typescript +const data = ['foo', 'bar', 'baz', 'qux'] +const result = piped( + data, + filter( + x => x.length >= 2 + ), + takeLast(2), +) +``` 9.4.1 +- Fix bug with `R.differenceWith` when two arrays has same length - [Issue #750](https://github.com/selfrefactor/rambda/issues/757) + - Allow path input to not be transformed when string numbers are there - [Issue #750](https://github.com/selfrefactor/rambda/issues/750) 9.4.0 diff --git a/docs/README.md b/docs/README.md index 31ec05e3e..978a3ddb7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25010,7 +25010,7 @@ describe('R.tail', () => { ```typescript -take(howMany: number, input: T[]): T[] +take(howMany: number, input: T): T extends string ? string : T ``` It returns the first `howMany` elements of `input`. @@ -25032,9 +25032,8 @@ const result = [ All TypeScript definitions ```typescript -take(howMany: number, input: T[]): T[]; -take(howMany: number, input: string): string; -take(howMany: number) : (input: T[]) => T[]; +take(howMany: number, input: T): T extends string ? string : T; +take(howMany: number) : (input: T) => T extends string ? string : T; ```
@@ -25110,7 +25109,7 @@ describe('R.take - array', () => { result // $ExpectType number[] }) it('curried', () => { - const result = take(howMany)(list) + const result = take(howMany)(list) result // $ExpectType number[] }) @@ -25123,7 +25122,7 @@ describe('R.take - string', () => { result // $ExpectType string }) it('curried', () => { - const result = take(howMany)(str) + const result = take(howMany)(str) result // $ExpectType string }) @@ -25138,7 +25137,7 @@ describe('R.take - string', () => { ```typescript -takeLast(howMany: number, input: T[]): T[] +takeLast(howMany: number, input: T): T extends string ? string : T ``` It returns the last `howMany` elements of `input`. @@ -25160,9 +25159,8 @@ const result = [ All TypeScript definitions ```typescript -takeLast(howMany: number, input: T[]): T[]; -takeLast(howMany: number, input: string): string; -takeLast(howMany: number) : (input: T[]) => T[]; +takeLast(howMany: number, input: T): T extends string ? string : T; +takeLast(howMany: number) : (input: T) => T extends string ? string : T; ```
@@ -25246,18 +25244,18 @@ describe('R.takeLast - array', () => { result // $ExpectType number[] }) it('curried', () => { - const result = takeLast(howMany)(list) + const result = takeLast(howMany)(list) result // $ExpectType number[] }) it('real case', () => { - let data = ['foo'] - let result = piped( + const data = ['foo', 'bar', 'baz', 'qux'] + const result = piped( data, filter( x => x.length >= 100 ), - takeLast(5), + takeLast(2), ) result // $ExpectType string[] }) @@ -25270,7 +25268,7 @@ describe('R.takeLast - string', () => { result // $ExpectType string }) it('curried', () => { - const result = takeLast(howMany)(str) + const result = takeLast(howMany)(str) result // $ExpectType string }) @@ -29261,12 +29259,27 @@ describe('R.zipWith', () => { ## ❯ CHANGELOG -9.4.2 +9.4.2 -- Fix bug with `R.differenceWith` when two arrays has same length - [Issue #750](https://github.com/selfrefactor/rambda/issues/757) +- Fix TS issue when `R.take` is used as part of `R.pipe`. + +Moving away from `Ramda` types which are problematic in this case: + +```typescript +const data = ['foo', 'bar', 'baz', 'qux'] +const result = piped( + data, + filter( + x => x.length >= 2 + ), + takeLast(2), +) +``` 9.4.1 +- Fix bug with `R.differenceWith` when two arrays has same length - [Issue #750](https://github.com/selfrefactor/rambda/issues/757) + - Allow path input to not be transformed when string numbers are there - [Issue #750](https://github.com/selfrefactor/rambda/issues/750) 9.4.0 diff --git a/files/index.d.ts b/files/index.d.ts index 74af81ec0..544fe006d 100644 --- a/files/index.d.ts +++ b/files/index.d.ts @@ -4003,9 +4003,8 @@ Notes: */ // @SINGLE_MARKER -export function take(howMany: number, input: T[]): T[]; -export function take(howMany: number, input: string): string; -export function take(howMany: number) : (input: T[]) => T[]; +export function take(howMany: number, input: T): T extends string ? string : T; +export function take(howMany: number) : (input: T) => T extends string ? string : T; /* Method: takeLast @@ -4030,10 +4029,8 @@ Notes: */ // @SINGLE_MARKER -export function takeLast(howMany: number, input: T[]): T[]; -export function takeLast(howMany: number, input: string): string; -export function takeLast(howMany: number) : (input: T[]) => T[]; - +export function takeLast(howMany: number, input: T): T extends string ? string : T; +export function takeLast(howMany: number) : (input: T) => T extends string ? string : T; /* Method: tap diff --git a/immutable.d.ts b/immutable.d.ts index fe9a0e100..85a3302b1 100644 --- a/immutable.d.ts +++ b/immutable.d.ts @@ -1642,16 +1642,14 @@ export function tail(input: string): string; /** * It returns the first `howMany` elements of `input`. */ -export function take(howMany: number, input: readonly T[]): readonly T[]; -export function take(howMany: number, input: string): string; -export function take(howMany: number) : (input: readonly T[]) => readonly T[]; +export function take(howMany: number, input: T): T extends string ? string : T; +export function take(howMany: number) : (input: T) => T extends string ? string : T; /** * It returns the last `howMany` elements of `input`. */ -export function takeLast(howMany: number, input: readonly T[]): readonly T[]; -export function takeLast(howMany: number, input: string): string; -export function takeLast(howMany: number) : (input: readonly T[]) => readonly T[]; +export function takeLast(howMany: number, input: T): T extends string ? string : T; +export function takeLast(howMany: number) : (input: T) => T extends string ? string : T; export function takeLastWhile(predicate: (x: string) => boolean, input: string): string; export function takeLastWhile(predicate: (x: string) => boolean): (input: string) => string; diff --git a/index.d.ts b/index.d.ts index b3d6a1976..e23545f88 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1642,16 +1642,14 @@ export function tail(input: string): string; /** * It returns the first `howMany` elements of `input`. */ -export function take(howMany: number, input: T[]): T[]; -export function take(howMany: number, input: string): string; -export function take(howMany: number) : (input: T[]) => T[]; +export function take(howMany: number, input: T): T extends string ? string : T; +export function take(howMany: number) : (input: T) => T extends string ? string : T; /** * It returns the last `howMany` elements of `input`. */ -export function takeLast(howMany: number, input: T[]): T[]; -export function takeLast(howMany: number, input: string): string; -export function takeLast(howMany: number) : (input: T[]) => T[]; +export function takeLast(howMany: number, input: T): T extends string ? string : T; +export function takeLast(howMany: number) : (input: T) => T extends string ? string : T; export function takeLastWhile(predicate: (x: string) => boolean, input: string): string; export function takeLastWhile(predicate: (x: string) => boolean): (input: string) => string; diff --git a/source/take-spec.ts b/source/take-spec.ts index 83175bf3c..f3d6e6c62 100644 --- a/source/take-spec.ts +++ b/source/take-spec.ts @@ -11,7 +11,7 @@ describe('R.take - array', () => { result // $ExpectType number[] }) it('curried', () => { - const result = take(howMany)(list) + const result = take(howMany)(list) result // $ExpectType number[] }) @@ -24,7 +24,7 @@ describe('R.take - string', () => { result // $ExpectType string }) it('curried', () => { - const result = take(howMany)(str) + const result = take(howMany)(str) result // $ExpectType string }) diff --git a/source/takeLast-spec.ts b/source/takeLast-spec.ts index dcbaeea14..7526445b4 100644 --- a/source/takeLast-spec.ts +++ b/source/takeLast-spec.ts @@ -11,18 +11,18 @@ describe('R.takeLast - array', () => { result // $ExpectType number[] }) it('curried', () => { - const result = takeLast(howMany)(list) + const result = takeLast(howMany)(list) result // $ExpectType number[] }) it('real case', () => { - let data = ['foo'] - let result = piped( + const data = ['foo', 'bar', 'baz', 'qux'] + const result = piped( data, filter( x => x.length >= 100 ), - takeLast(5), + takeLast(2), ) result // $ExpectType string[] }) @@ -35,7 +35,7 @@ describe('R.takeLast - string', () => { result // $ExpectType string }) it('curried', () => { - const result = takeLast(howMany)(str) + const result = takeLast(howMany)(str) result // $ExpectType string })