From e2fbcc1797ede50dca6a563217d3c1b5001c1b76 Mon Sep 17 00:00:00 2001 From: Deyan Totev Date: Fri, 22 Sep 2023 22:04:26 +0300 Subject: [PATCH 1/4] fix: revert change --- NEXT_VERSION_CHECKLIST.md | 6 +++++- files/index.d.ts | 1 - package.json | 1 + rambda.js | 6 +++--- source/anyPass-spec.ts | 16 ++++++++-------- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/NEXT_VERSION_CHECKLIST.md b/NEXT_VERSION_CHECKLIST.md index fe0f7ea8..272fc263 100644 --- a/NEXT_VERSION_CHECKLIST.md +++ b/NEXT_VERSION_CHECKLIST.md @@ -108,4 +108,8 @@ check again deno as dissocpath doesn't add js extension to imports --- try omitPath as method instead of multiple paths --- -replace missing ramda methods with text that argument is missing \ No newline at end of file +replace missing ramda methods with text that argument is missing +=== +publish after march 2024 + +export function anyPass(predicates: { [K in keyof U]: (x: T) => x is U[K]; }): (input: T) => input is U[number]; \ No newline at end of file diff --git a/files/index.d.ts b/files/index.d.ts index 34a69890..32489c4f 100644 --- a/files/index.d.ts +++ b/files/index.d.ts @@ -393,7 +393,6 @@ Notes: */ // @SINGLE_MARKER -export function anyPass(predicates: { [K in keyof U]: (x: T) => x is U[K]; }): (input: T) => input is U[number]; export function anyPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean; export function anyPass(predicates: ((...inputs: T[]) => boolean)[]): (...inputs: T[]) => boolean; diff --git a/package.json b/package.json index 67c0173a..56ce9bd2 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "test:all": "jest source/*.spec.js -u --bail=false", "test:ci": "jest source/*.spec.js --coverage --no-cache -w 1", "test:typings": "dtslint --localTs ./node_modules/typescript/lib --expectOnly ./source", + "ts": "yarn test:typings", "usedby": "cd ../rambda-scripts && yarn usedby", "x": "yarn populatedocs:x && yarn populatereadme:x && yarn immutable:x" }, diff --git a/rambda.js b/rambda.js index 2576136b..89058e36 100644 --- a/rambda.js +++ b/rambda.js @@ -1,6 +1,4 @@ /// -export * from './src/F.js' -export * from './src/T.js' export * from './src/add.js' export * from './src/addIndex.js' export * from './src/addIndexRight.js' @@ -58,6 +56,7 @@ export * from './src/endsWith.js' export * from './src/eqProps.js' export * from './src/equals.js' export * from './src/evolve.js' +export * from './src/F.js' export * from './src/filter.js' export * from './src/find.js' export * from './src/findIndex.js' @@ -143,8 +142,8 @@ export * from './src/prop.js' export * from './src/propEq.js' export * from './src/propIs.js' export * from './src/propOr.js' -export * from './src/propSatisfies.js' export * from './src/props.js' +export * from './src/propSatisfies.js' export * from './src/range.js' export * from './src/reduce.js' export * from './src/reject.js' @@ -164,6 +163,7 @@ export * from './src/startsWith.js' export * from './src/subtract.js' export * from './src/sum.js' export * from './src/symmetricDifference.js' +export * from './src/T.js' export * from './src/tail.js' export * from './src/take.js' export * from './src/takeLast.js' diff --git a/source/anyPass-spec.ts b/source/anyPass-spec.ts index 16f332c0..583ac6cc 100644 --- a/source/anyPass-spec.ts +++ b/source/anyPass-spec.ts @@ -33,18 +33,18 @@ describe('anyPass', () => { filtered2 // $ExpectType number[] }) it('functions as a type guard', () => { - const isString = (x: unknown): x is string => typeof x === 'string'; - const isNumber = (x: unknown): x is number => typeof x === 'number'; - const isBoolean = (x: unknown): x is boolean => typeof x === 'boolean'; - - const isStringNumberOrBoolean = anyPass([isString, isNumber, isBoolean]); + const isString = (x: unknown): x is string => typeof x === 'string' + const isNumber = (x: unknown): x is number => typeof x === 'number' + const isBoolean = (x: unknown): x is boolean => typeof x === 'boolean' - isStringNumberOrBoolean // $ExpectType (input: unknown) => input is string | number | boolean + const isStringNumberOrBoolean = anyPass([isString, isNumber, isBoolean]) - const aValue: unknown = 1; + isStringNumberOrBoolean // $ExpectType (input: unknown) => boolean + + const aValue: unknown = 1 if (isStringNumberOrBoolean(aValue)) { - aValue // $ExpectType string | number | boolean + aValue // $ExpectType unknown } }) }) From ff18588f071551d24fd28981bd7b56433da378d7 Mon Sep 17 00:00:00 2001 From: Deyan Totev Date: Mon, 2 Oct 2023 09:48:22 +0300 Subject: [PATCH 2/4] add new methods --- NEXT_VERSION_CHECKLIST.md | 19 ++++-- files/index.d.ts | 123 ++++++++++++++++++++++++++++------- source/dropRepeatsBy.js | 21 ++++++ source/dropRepeatsBy.spec.js | 59 +++++++++++++++++ source/empty.js | 15 +++++ source/empty.spec.js | 22 +++++++ source/eqBy.js | 10 +++ source/eqBy.spec.js | 34 ++++++++++ source/forEach.js | 50 ++++++++------ source/mergeWith.js | 16 ++--- source/mergeWith.spec.js | 16 ++--- source/partial-spec.ts | 32 ++------- 12 files changed, 322 insertions(+), 95 deletions(-) create mode 100644 source/dropRepeatsBy.js create mode 100644 source/dropRepeatsBy.spec.js create mode 100644 source/empty.js create mode 100644 source/empty.spec.js create mode 100644 source/eqBy.js create mode 100644 source/eqBy.spec.js diff --git a/NEXT_VERSION_CHECKLIST.md b/NEXT_VERSION_CHECKLIST.md index 272fc263..5f749ece 100644 --- a/NEXT_VERSION_CHECKLIST.md +++ b/NEXT_VERSION_CHECKLIST.md @@ -1,3 +1,16 @@ +fix partial + +- dropRepeatsBy +- empty +- eqBy +- forEachObjIndexed + +forEachObjIndexed should not contain source file nor test file +--- + + + + in js project like niketa theme, go to source lead to readable code, is ramda the same? fix https://github.com/selfrefactor/rambdax/issues/93 @@ -10,6 +23,8 @@ automate deno release deno is advantage as Ramda last release is 3 years ago --- +no testing curry when method is exporting curried function +--- group TS test for similar methods eventual create additional topic in methods - related methods, so it is easy to find tests as the first one alpabetically is the one containing all TS tests @@ -17,10 +32,6 @@ eventual create additional topic in methods - related methods, so it is easy to - construct - it is class helper and classes are not very functional oriented - constructN -- dropRepeatsBy -- empty -- eqBy -- forEachObjIndexed - gt - gte - hasIn diff --git a/files/index.d.ts b/files/index.d.ts index 32489c4f..ee7ae9fc 100644 --- a/files/index.d.ts +++ b/files/index.d.ts @@ -2806,31 +2806,22 @@ Notes: Rambda's partial doesn't need the input arguments to be wrapped as array. */ // @SINGLE_MARKER -export function partial< - Args extends unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - ...args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never; - -export function partial< - Args extends readonly unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never; +export function partial(fn: (x0: V0, x1: V1) => T, args: [V0]): (x1: V1) => T; +export function partial(fn: (x0: V0, x1: V1, x2: V2) => T, args: [V0, V1]): (x2: V2) => T; +export function partial(fn: (x0: V0, x1: V1, x2: V2) => T, args: [V0]): (x1: V1, x2: V2) => T; +export function partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: [V0, V1, V2], +): (x2: V3) => T; +export function partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: [V0, V1], +): (x2: V2, x3: V3) => T; +export function partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: [V0], +): (x1: V1, x2: V2, x3: V3) => T; +export function partial(fn: (...a: any[]) => T, args: any[]): (...a: any[]) => T; /* @@ -5623,6 +5614,88 @@ Notes: export function removeIndex(index: number, list: T[]): T[]; export function removeIndex(index: number): (list: T[]) => T[]; +/* +Method: dropRepeatsBy + +Explanation: + +Example: + +``` +``` + +Categories: + +Notes: + +*/ +// @SINGLE_MARKER +export function dropRepeatsBy(fn: (a: T) => U, list: T[]): T[]; +export function dropRepeatsBy( + fn: (a: T) => U +): (list: T[]) => T[]; +export function dropRepeatsBy(fn: any): (list: T[]) => T[]; + +/* +Method: empty + +Explanation: + +Example: + +``` +``` + +Categories: + +Notes: + +*/ +// @SINGLE_MARKER +export function empty(x: T): T; + +/* +Method: eqBy + +Explanation: + +Example: + +``` +``` + +Categories: + +Notes: + +*/ +// @SINGLE_MARKER +export function eqBy(fn: (a: T) => unknown, a: T, b: T): boolean; +export function eqBy(fn: (a: T) => unknown, a: T): (b: T) => boolean; +export function eqBy(fn: (a: T) => unknown): { + (a: T, b: T): boolean; + (a: T): (b: T) => boolean; +}; + +/* +Method: forEachObjIndexed + +Explanation: + +Example: + +``` +``` + +Categories: + +Notes: + +*/ +// @SINGLE_MARKER +export function forEachObjIndexed(fn: (value: T[keyof T], key: keyof T, obj: T) => void, obj: T): T; +export function forEachObjIndexed(fn: (value: T[keyof T], key: keyof T, obj: T) => void): (obj: T) => T; + // RAMBDAX_MARKER_START /* diff --git a/source/dropRepeatsBy.js b/source/dropRepeatsBy.js new file mode 100644 index 00000000..285918ea --- /dev/null +++ b/source/dropRepeatsBy.js @@ -0,0 +1,21 @@ +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 + }) +} diff --git a/source/dropRepeatsBy.spec.js b/source/dropRepeatsBy.spec.js new file mode 100644 index 00000000..37489ee7 --- /dev/null +++ b/source/dropRepeatsBy.spec.js @@ -0,0 +1,59 @@ +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([]) +}) diff --git a/source/empty.js b/source/empty.js new file mode 100644 index 00000000..7d7cdd9a --- /dev/null +++ b/source/empty.js @@ -0,0 +1,15 @@ +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 {} +} diff --git a/source/empty.spec.js b/source/empty.spec.js new file mode 100644 index 00000000..f98e0095 --- /dev/null +++ b/source/empty.spec.js @@ -0,0 +1,22 @@ +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() +}) diff --git a/source/eqBy.js b/source/eqBy.js new file mode 100644 index 00000000..b20a6342 --- /dev/null +++ b/source/eqBy.js @@ -0,0 +1,10 @@ +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) diff --git a/source/eqBy.spec.js b/source/eqBy.spec.js new file mode 100644 index 00000000..40e19947 --- /dev/null +++ b/source/eqBy.spec.js @@ -0,0 +1,34 @@ +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) +}) diff --git a/source/forEach.js b/source/forEach.js index 4f7e6d3d..a320b28f 100644 --- a/source/forEach.js +++ b/source/forEach.js @@ -1,34 +1,44 @@ import { isArray } from './_internals/isArray.js' import { keys } from './_internals/keys.js' -export function forEach(fn, list){ - if (arguments.length === 1) return _list => forEach(fn, _list) +export function forEachObjIndexedFn(fn, obj){ + let index = 0 + const listKeys = keys(obj) + const len = listKeys.length - if (list === undefined){ - return + while (index < len){ + const key = listKeys[ index ] + fn( + obj[ key ], key, obj + ) + index++ } - if (isArray(list)){ - let index = 0 - const len = list.length + return obj +} - while (index < len){ - fn(list[ index ]) - index++ - } - } else { +export function forEachObjIndexed(fn, list){ + if (arguments.length === 1) return _list => forEachObjIndexed(fn, _list) + + if (list === undefined) return + + return forEachObjIndexedFn(fn, list) +} + +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 listKeys = keys(list) - const len = listKeys.length + const len = iterable.length while (index < len){ - const key = listKeys[ index ] - fn( - list[ key ], key, list - ) + fn(iterable[ index ]) index++ } - } + } else return forEachObjIndexedFn(fn, iterable) - return list + return iterable } diff --git a/source/mergeWith.js b/source/mergeWith.js index 4eb00396..718e08da 100644 --- a/source/mergeWith.js +++ b/source/mergeWith.js @@ -1,6 +1,6 @@ import { curry } from './curry.js' -function mergeWithFn( +export function mergeWithFn( mergeFn, aInput, bInput ){ const a = aInput ?? {} @@ -8,21 +8,15 @@ function mergeWithFn( const willReturn = {} Object.keys(a).forEach(key => { - if (b[ key ] === undefined){ - willReturn[ key ] = a[ key ] - } else { - willReturn[ key ] = mergeFn(a[ key ], b[ 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 - if (a[ key ] === undefined){ - willReturn[ key ] = b[ key ] - } else { - willReturn[ key ] = mergeFn(a[ key ], b[ key ]) - } + if (a[ key ] === undefined) willReturn[ key ] = b[ key ] + else willReturn[ key ] = mergeFn(a[ key ], b[ key ]) }) return willReturn diff --git a/source/mergeWith.spec.js b/source/mergeWith.spec.js index f0970050..9efc64f5 100644 --- a/source/mergeWith.spec.js +++ b/source/mergeWith.spec.js @@ -1,8 +1,8 @@ import { concat } from './concat.js' -import { mergeWith } from './mergeWith.js' +import { mergeWithFn } from './mergeWith.js' test('happy', () => { - const result = mergeWith( + const result = mergeWithFn( concat, { a : true, @@ -15,8 +15,8 @@ test('happy', () => { ) const expected = { a : true, - values : [ 10, 20, 15, 35 ], b : true, + values : [ 10, 20, 15, 35 ], } expect(result).toEqual(expected) }) @@ -24,31 +24,31 @@ test('happy', () => { // 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(mergeWith( + expect(mergeWithFn( concat, undefined, {} )).toEqual({}) }) it('if the first object is empty and the second nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, {}, null )).toEqual({}) }) it('if both objects are nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, undefined, null )).toEqual({}) }) it('if the first object is not empty and the second is nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, { a : 'a' }, null )).toEqual({ a : 'a' }) }) it('if the first object is nil and the second is not empty', () => { - expect(mergeWith( + expect(mergeWithFn( concat, undefined, { a : 'a' } )).toEqual({ a : 'a' }) }) diff --git a/source/partial-spec.ts b/source/partial-spec.ts index 728e56ec..02a4c786 100644 --- a/source/partial-spec.ts +++ b/source/partial-spec.ts @@ -8,41 +8,19 @@ describe('R.partial', () => { aBoolean: boolean, aNull: null ) { - return { aString, aNumber, aBoolean, aNull } + return {aString, aNumber, aBoolean, aNull} } // @ts-expect-error - partial(fn, 1); - - const fn1 = partial(fn, 'a') - - // @ts-expect-error - partial(fn1, 'b'); - - const fn2 = partial(fn1, 2) - const result = fn2(true, null) - result // $ExpectType { aString: string; aNumber: number; aBoolean: boolean; aNull: null; } - }) - - it('ramda', () => { - function fn( - aString: string, - aNumber: number, - aBoolean: boolean, - aNull: null - ) { - return { aString, aNumber, aBoolean, aNull } - } - - // @ts-expect-error - partial(fn, 1); + partial(fn, 1) const fn1 = partial(fn, ['a']) // @ts-expect-error - partial(fn1, ['b']); + partial(fn1, ['b']) const fn2 = partial(fn1, [2]) const result = fn2(true, null) result // $ExpectType { aString: string; aNumber: number; aBoolean: boolean; aNull: null; } - })}) + }) +}) From 8be721b03a523d461323ca61bc287c25d010fc6e Mon Sep 17 00:00:00 2001 From: Deyan Totev Date: Mon, 2 Oct 2023 10:06:10 +0300 Subject: [PATCH 3/4] fix --- source/partial-spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/partial-spec.ts b/source/partial-spec.ts index 02a4c786..1107194f 100644 --- a/source/partial-spec.ts +++ b/source/partial-spec.ts @@ -15,8 +15,6 @@ describe('R.partial', () => { partial(fn, 1) const fn1 = partial(fn, ['a']) - - // @ts-expect-error partial(fn1, ['b']) const fn2 = partial(fn1, [2]) From 253ca794ec62efa6f0357c1c6b03eb3051660a99 Mon Sep 17 00:00:00 2001 From: Deyan Totev Date: Mon, 2 Oct 2023 10:28:47 +0300 Subject: [PATCH 4/4] feat@before --- .github/README.md | 219 ++++++++++++++++++++----------------------- CHANGELOG.md | 14 +++ README.md | 219 ++++++++++++++++++++----------------------- dist/rambda.js | 93 ++++++++++++------ dist/rambda.umd.js | 2 +- docs/README.md | 219 ++++++++++++++++++++----------------------- immutable.d.ts | 60 +++++++----- index.d.ts | 60 +++++++----- rambda.js | 9 +- src/dropRepeatsBy.js | 21 +++++ src/empty.js | 15 +++ src/eqBy.js | 10 ++ src/forEach.js | 50 ++++++---- src/mergeWith.js | 16 +--- 14 files changed, 547 insertions(+), 460 deletions(-) create mode 100644 src/dropRepeatsBy.js create mode 100644 src/empty.js create mode 100644 src/eqBy.js diff --git a/.github/README.md b/.github/README.md index 8e057209..40ad5e39 100644 --- a/.github/README.md +++ b/.github/README.md @@ -830,7 +830,7 @@ describe('R.any', () => { ```typescript -anyPass(predicates: { [K in keyof U]: (x: T) => x is U[K] +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`. @@ -855,7 +855,6 @@ const result = fn(input) All TypeScript definitions ```typescript -anyPass(predicates: { [K in keyof U]: (x: T) => x is U[K]; }): (input: T) => input is U[number]; anyPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean; anyPass(predicates: ((...inputs: T[]) => boolean)[]): (...inputs: T[]) => boolean; ``` @@ -984,18 +983,18 @@ describe('anyPass', () => { filtered2 // $ExpectType number[] }) it('functions as a type guard', () => { - const isString = (x: unknown): x is string => typeof x === 'string'; - const isNumber = (x: unknown): x is number => typeof x === 'number'; - const isBoolean = (x: unknown): x is boolean => typeof x === 'boolean'; - - const isStringNumberOrBoolean = anyPass([isString, isNumber, isBoolean]); + const isString = (x: unknown): x is string => typeof x === 'string' + const isNumber = (x: unknown): x is number => typeof x === 'number' + const isBoolean = (x: unknown): x is boolean => typeof x === 'boolean' + + const isStringNumberOrBoolean = anyPass([isString, isNumber, isBoolean]) - isStringNumberOrBoolean // $ExpectType (input: unknown) => input is string | number | boolean + isStringNumberOrBoolean // $ExpectType (input: unknown) => boolean - const aValue: unknown = 1; + const aValue: unknown = 1 if (isStringNumberOrBoolean(aValue)) { - aValue // $ExpectType string | number | boolean + aValue // $ExpectType unknown } }) }) @@ -3609,6 +3608,10 @@ describe('R.dropRepeats', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeats) +### dropRepeatsBy + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeatsBy) + ### dropRepeatsWith Try this R.dropRepeatsWith example in Rambda REPL @@ -3787,6 +3790,10 @@ describe('R.either', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#either) +### empty + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#empty) + ### endsWith ```typescript @@ -3959,6 +3966,10 @@ describe('R.endsWith - string', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#endsWith) +### eqBy + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#eqBy) + ### eqProps It returns `true` if property `prop` in `obj1` is equal to property `prop` in `obj2` according to `R.equals`. @@ -5721,36 +5732,46 @@ forEach(fn: ObjectIterator): (list: Dictionary) => Dictionary< import { isArray } from './_internals/isArray.js' import { keys } from './_internals/keys.js' -export function forEach(fn, list){ - if (arguments.length === 1) return _list => forEach(fn, _list) +export function forEachObjIndexedFn(fn, obj){ + let index = 0 + const listKeys = keys(obj) + const len = listKeys.length - if (list === undefined){ - return + while (index < len){ + const key = listKeys[ index ] + fn( + obj[ key ], key, obj + ) + index++ } - if (isArray(list)){ - let index = 0 - const len = list.length + return obj +} - while (index < len){ - fn(list[ index ]) - index++ - } - } else { +export function forEachObjIndexed(fn, list){ + if (arguments.length === 1) return _list => forEachObjIndexed(fn, _list) + + if (list === undefined) return + + return forEachObjIndexedFn(fn, list) +} + +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 listKeys = keys(list) - const len = listKeys.length + const len = iterable.length while (index < len){ - const key = listKeys[ index ] - fn( - list[ key ], key, list - ) + fn(iterable[ index ]) index++ } - } + } else return forEachObjIndexedFn(fn, iterable) - return list + return iterable } ``` @@ -5881,6 +5902,10 @@ describe('R.forEach with objects', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#forEach) +### forEachObjIndexed + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#forEachObjIndexed) + ### fromPairs It transforms a `listOfPairs` to an object. @@ -9329,7 +9354,7 @@ mergeWith(fn: (x: any, z: any) => any): (a: U, b: V) => Output; ```javascript import { curry } from './curry.js' -function mergeWithFn( +export function mergeWithFn( mergeFn, aInput, bInput ){ const a = aInput ?? {} @@ -9337,21 +9362,15 @@ function mergeWithFn( const willReturn = {} Object.keys(a).forEach(key => { - if (b[ key ] === undefined){ - willReturn[ key ] = a[ key ] - } else { - willReturn[ key ] = mergeFn(a[ key ], b[ 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 - if (a[ key ] === undefined){ - willReturn[ key ] = b[ key ] - } else { - willReturn[ key ] = mergeFn(a[ key ], b[ key ]) - } + if (a[ key ] === undefined) willReturn[ key ] = b[ key ] + else willReturn[ key ] = mergeFn(a[ key ], b[ key ]) }) return willReturn @@ -9368,10 +9387,10 @@ export const mergeWith = curry(mergeWithFn) ```javascript import { concat } from './concat.js' -import { mergeWith } from './mergeWith.js' +import { mergeWithFn } from './mergeWith.js' test('happy', () => { - const result = mergeWith( + const result = mergeWithFn( concat, { a : true, @@ -9384,8 +9403,8 @@ test('happy', () => { ) const expected = { a : true, - values : [ 10, 20, 15, 35 ], b : true, + values : [ 10, 20, 15, 35 ], } expect(result).toEqual(expected) }) @@ -9393,31 +9412,31 @@ test('happy', () => { // 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(mergeWith( + expect(mergeWithFn( concat, undefined, {} )).toEqual({}) }) it('if the first object is empty and the second nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, {}, null )).toEqual({}) }) it('if both objects are nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, undefined, null )).toEqual({}) }) it('if the first object is not empty and the second is nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, { a : 'a' }, null )).toEqual({ a : 'a' }) }) it('if the first object is nil and the second is not empty', () => { - expect(mergeWith( + expect(mergeWithFn( concat, undefined, { a : 'a' } )).toEqual({ a : 'a' }) }) @@ -10692,18 +10711,7 @@ test('index lens', () => { ```typescript -partial< - Args extends unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - ...args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never +partial(fn: (x0: V0, x1: V1) => T, args: [V0]): (x1: V1) => T ``` It is very similar to `R.curry`, but you can pass initial arguments when you create the curried function. @@ -10733,31 +10741,22 @@ finalFn('Bar') // => 'Hello, Foo Bar!' All TypeScript definitions ```typescript -partial< - Args extends unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - ...args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never; - -partial< - Args extends readonly unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never; +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; ``` @@ -10874,44 +10873,20 @@ describe('R.partial', () => { aBoolean: boolean, aNull: null ) { - return { aString, aNumber, aBoolean, aNull } + return {aString, aNumber, aBoolean, aNull} } // @ts-expect-error - partial(fn, 1); - - const fn1 = partial(fn, 'a') - - // @ts-expect-error - partial(fn1, 'b'); - - const fn2 = partial(fn1, 2) - const result = fn2(true, null) - result // $ExpectType { aString: string; aNumber: number; aBoolean: boolean; aNull: null; } - }) - - it('ramda', () => { - function fn( - aString: string, - aNumber: number, - aBoolean: boolean, - aNull: null - ) { - return { aString, aNumber, aBoolean, aNull } - } - - // @ts-expect-error - partial(fn, 1); + partial(fn, 1) const fn1 = partial(fn, ['a']) - - // @ts-expect-error - partial(fn1, ['b']); + partial(fn1, ['b']) const fn2 = partial(fn1, [2]) const result = fn2(true, null) result // $ExpectType { aString: string; aNumber: number; aBoolean: boolean; aNull: null; } - })}) + }) +}) ``` @@ -18738,6 +18713,20 @@ describe('R.zipWith', () => { ## ❯ CHANGELOG +8.5.0 + +- Revert changes in `R.anyPass` introduced in `8.4.0` release. The reason is that the change was breaking the library older than `5.2.0` TypeScript. + +- Wrong `R.partial` TS definition - [Issue #705](https://github.com/selfrefactor/rambda/issues/705) + +- Add `R.dropRepeatsBy` + +- Add `R.empty` + +- Add `R.eqBy` + +- Add `R.forEachObjIndexed` + 8.4.0 - Add `R.dissocPath` diff --git a/CHANGELOG.md b/CHANGELOG.md index c7b5d6d6..92843bb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +8.5.0 + +- Revert changes in `R.anyPass` introduced in `8.4.0` release. The reason is that the change was breaking the library older than `5.2.0` TypeScript. + +- Wrong `R.partial` TS definition - [Issue #705](https://github.com/selfrefactor/rambda/issues/705) + +- Add `R.dropRepeatsBy` + +- Add `R.empty` + +- Add `R.eqBy` + +- Add `R.forEachObjIndexed` + 8.4.0 - Add `R.dissocPath` diff --git a/README.md b/README.md index 6f1a90c6..59c81b8b 100644 --- a/README.md +++ b/README.md @@ -789,7 +789,7 @@ describe('R.any', () => { ```typescript -anyPass(predicates: { [K in keyof U]: (x: T) => x is U[K] +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`. @@ -801,7 +801,6 @@ It accepts list of `predicates` and returns a function. This function with its ` All TypeScript definitions ```typescript -anyPass(predicates: { [K in keyof U]: (x: T) => x is U[K]; }): (input: T) => input is U[number]; anyPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean; anyPass(predicates: ((...inputs: T[]) => boolean)[]): (...inputs: T[]) => boolean; ``` @@ -930,18 +929,18 @@ describe('anyPass', () => { filtered2 // $ExpectType number[] }) it('functions as a type guard', () => { - const isString = (x: unknown): x is string => typeof x === 'string'; - const isNumber = (x: unknown): x is number => typeof x === 'number'; - const isBoolean = (x: unknown): x is boolean => typeof x === 'boolean'; - - const isStringNumberOrBoolean = anyPass([isString, isNumber, isBoolean]); + const isString = (x: unknown): x is string => typeof x === 'string' + const isNumber = (x: unknown): x is number => typeof x === 'number' + const isBoolean = (x: unknown): x is boolean => typeof x === 'boolean' + + const isStringNumberOrBoolean = anyPass([isString, isNumber, isBoolean]) - isStringNumberOrBoolean // $ExpectType (input: unknown) => input is string | number | boolean + isStringNumberOrBoolean // $ExpectType (input: unknown) => boolean - const aValue: unknown = 1; + const aValue: unknown = 1 if (isStringNumberOrBoolean(aValue)) { - aValue // $ExpectType string | number | boolean + aValue // $ExpectType unknown } }) }) @@ -3438,6 +3437,10 @@ describe('R.dropRepeats', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeats) +### dropRepeatsBy + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeatsBy) + ### dropRepeatsWith Try this R.dropRepeatsWith example in Rambda REPL @@ -3603,6 +3606,10 @@ describe('R.either', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#either) +### empty + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#empty) + ### endsWith ```typescript @@ -3764,6 +3771,10 @@ describe('R.endsWith - string', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#endsWith) +### eqBy + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#eqBy) + ### eqProps It returns `true` if property `prop` in `obj1` is equal to property `prop` in `obj2` according to `R.equals`. @@ -5422,36 +5433,46 @@ forEach(fn: ObjectIterator): (list: Dictionary) => Dictionary< import { isArray } from './_internals/isArray.js' import { keys } from './_internals/keys.js' -export function forEach(fn, list){ - if (arguments.length === 1) return _list => forEach(fn, _list) +export function forEachObjIndexedFn(fn, obj){ + let index = 0 + const listKeys = keys(obj) + const len = listKeys.length - if (list === undefined){ - return + while (index < len){ + const key = listKeys[ index ] + fn( + obj[ key ], key, obj + ) + index++ } - if (isArray(list)){ - let index = 0 - const len = list.length + return obj +} - while (index < len){ - fn(list[ index ]) - index++ - } - } else { +export function forEachObjIndexed(fn, list){ + if (arguments.length === 1) return _list => forEachObjIndexed(fn, _list) + + if (list === undefined) return + + return forEachObjIndexedFn(fn, list) +} + +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 listKeys = keys(list) - const len = listKeys.length + const len = iterable.length while (index < len){ - const key = listKeys[ index ] - fn( - list[ key ], key, list - ) + fn(iterable[ index ]) index++ } - } + } else return forEachObjIndexedFn(fn, iterable) - return list + return iterable } ``` @@ -5582,6 +5603,10 @@ describe('R.forEach with objects', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#forEach) +### forEachObjIndexed + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#forEachObjIndexed) + ### fromPairs It transforms a `listOfPairs` to an object. @@ -8780,7 +8805,7 @@ mergeWith(fn: (x: any, z: any) => any): (a: U, b: V) => Output; ```javascript import { curry } from './curry.js' -function mergeWithFn( +export function mergeWithFn( mergeFn, aInput, bInput ){ const a = aInput ?? {} @@ -8788,21 +8813,15 @@ function mergeWithFn( const willReturn = {} Object.keys(a).forEach(key => { - if (b[ key ] === undefined){ - willReturn[ key ] = a[ key ] - } else { - willReturn[ key ] = mergeFn(a[ key ], b[ 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 - if (a[ key ] === undefined){ - willReturn[ key ] = b[ key ] - } else { - willReturn[ key ] = mergeFn(a[ key ], b[ key ]) - } + if (a[ key ] === undefined) willReturn[ key ] = b[ key ] + else willReturn[ key ] = mergeFn(a[ key ], b[ key ]) }) return willReturn @@ -8819,10 +8838,10 @@ export const mergeWith = curry(mergeWithFn) ```javascript import { concat } from './concat.js' -import { mergeWith } from './mergeWith.js' +import { mergeWithFn } from './mergeWith.js' test('happy', () => { - const result = mergeWith( + const result = mergeWithFn( concat, { a : true, @@ -8835,8 +8854,8 @@ test('happy', () => { ) const expected = { a : true, - values : [ 10, 20, 15, 35 ], b : true, + values : [ 10, 20, 15, 35 ], } expect(result).toEqual(expected) }) @@ -8844,31 +8863,31 @@ test('happy', () => { // 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(mergeWith( + expect(mergeWithFn( concat, undefined, {} )).toEqual({}) }) it('if the first object is empty and the second nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, {}, null )).toEqual({}) }) it('if both objects are nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, undefined, null )).toEqual({}) }) it('if the first object is not empty and the second is nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, { a : 'a' }, null )).toEqual({ a : 'a' }) }) it('if the first object is nil and the second is not empty', () => { - expect(mergeWith( + expect(mergeWithFn( concat, undefined, { a : 'a' } )).toEqual({ a : 'a' }) }) @@ -10073,18 +10092,7 @@ test('index lens', () => { ```typescript -partial< - Args extends unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - ...args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never +partial(fn: (x0: V0, x1: V1) => T, args: [V0]): (x1: V1) => T ``` It is very similar to `R.curry`, but you can pass initial arguments when you create the curried function. @@ -10099,31 +10107,22 @@ The name comes from the fact that you partially inject the inputs. All TypeScript definitions ```typescript -partial< - Args extends unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - ...args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never; - -partial< - Args extends readonly unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never; +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; ``` @@ -10240,44 +10239,20 @@ describe('R.partial', () => { aBoolean: boolean, aNull: null ) { - return { aString, aNumber, aBoolean, aNull } + return {aString, aNumber, aBoolean, aNull} } // @ts-expect-error - partial(fn, 1); - - const fn1 = partial(fn, 'a') - - // @ts-expect-error - partial(fn1, 'b'); - - const fn2 = partial(fn1, 2) - const result = fn2(true, null) - result // $ExpectType { aString: string; aNumber: number; aBoolean: boolean; aNull: null; } - }) - - it('ramda', () => { - function fn( - aString: string, - aNumber: number, - aBoolean: boolean, - aNull: null - ) { - return { aString, aNumber, aBoolean, aNull } - } - - // @ts-expect-error - partial(fn, 1); + partial(fn, 1) const fn1 = partial(fn, ['a']) - - // @ts-expect-error - partial(fn1, ['b']); + partial(fn1, ['b']) const fn2 = partial(fn1, [2]) const result = fn2(true, null) result // $ExpectType { aString: string; aNumber: number; aBoolean: boolean; aNull: null; } - })}) + }) +}) ``` @@ -17452,6 +17427,20 @@ describe('R.zipWith', () => { ## ❯ CHANGELOG +8.5.0 + +- Revert changes in `R.anyPass` introduced in `8.4.0` release. The reason is that the change was breaking the library older than `5.2.0` TypeScript. + +- Wrong `R.partial` TS definition - [Issue #705](https://github.com/selfrefactor/rambda/issues/705) + +- Add `R.dropRepeatsBy` + +- Add `R.empty` + +- Add `R.eqBy` + +- Add `R.forEachObjIndexed` + 8.4.0 - Add `R.dissocPath` diff --git a/dist/rambda.js b/dist/rambda.js index b282aca9..c8da1d74 100644 --- a/dist/rambda.js +++ b/dist/rambda.js @@ -1015,6 +1015,21 @@ function dropRepeats(list) { return toReturn; } +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; + }); +} + function dropRepeatsWith(predicate, list) { if (arguments.length === 1) { return _iterable => dropRepeatsWith(predicate, _iterable); @@ -1066,6 +1081,19 @@ function either(firstPredicate, secondPredicate) { return (...input) => Boolean(firstPredicate(...input) || secondPredicate(...input)); } +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 {}; +} + function endsWith(target, iterable) { if (arguments.length === 1) return _iterable => endsWith(target, _iterable); if (typeof iterable === 'string') { @@ -1083,6 +1111,11 @@ function endsWith(target, iterable) { return filtered.length === target.length; } +function eqByFn(fn, a, b) { + return equals(fn(a), fn(b)); +} +const eqBy = curry(eqByFn); + function prop(propToFind, obj) { if (arguments.length === 1) return _obj => prop(propToFind, _obj); if (!obj) return undefined; @@ -1246,29 +1279,34 @@ function flip(fn) { return flipFn(fn); } -function forEach(fn, list) { - if (arguments.length === 1) return _list => forEach(fn, _list); - if (list === undefined) { - return; +function forEachObjIndexedFn(fn, obj) { + let index = 0; + const listKeys = keys$1(obj); + const len = listKeys.length; + while (index < len) { + const key = listKeys[index]; + fn(obj[key], key, obj); + index++; } - if (isArray(list)) { - let index = 0; - const len = list.length; - while (index < len) { - fn(list[index]); - index++; - } - } else { + return obj; +} +function forEachObjIndexed(fn, list) { + if (arguments.length === 1) return _list => forEachObjIndexed(fn, _list); + if (list === undefined) return; + return forEachObjIndexedFn(fn, list); +} +function forEach(fn, iterable) { + if (arguments.length === 1) return _list => forEach(fn, _list); + if (iterable === undefined) return; + if (isArray(iterable)) { let index = 0; - const listKeys = keys$1(list); - const len = listKeys.length; + const len = iterable.length; while (index < len) { - const key = listKeys[index]; - fn(list[key], key, list); + fn(iterable[index]); index++; } - } - return list; + } else return forEachObjIndexedFn(fn, iterable); + return iterable; } function fromPairs(listOfPairs) { @@ -1592,19 +1630,11 @@ function mergeWithFn(mergeFn, aInput, bInput) { const b = bInput !== null && bInput !== void 0 ? bInput : {}; const willReturn = {}; Object.keys(a).forEach(key => { - if (b[key] === undefined) { - willReturn[key] = a[key]; - } else { - willReturn[key] = mergeFn(a[key], b[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; - if (a[key] === undefined) { - willReturn[key] = b[key]; - } else { - willReturn[key] = mergeFn(a[key], b[key]); - } + if (a[key] === undefined) willReturn[key] = b[key];else willReturn[key] = mergeFn(a[key], b[key]); }); return willReturn; } @@ -2381,10 +2411,14 @@ exports.drop = drop; exports.dropLast = dropLast; exports.dropLastWhile = dropLastWhile; exports.dropRepeats = dropRepeats; +exports.dropRepeatsBy = dropRepeatsBy; exports.dropRepeatsWith = dropRepeatsWith; exports.dropWhile = dropWhile; exports.either = either; +exports.empty = empty; exports.endsWith = endsWith; +exports.eqBy = eqBy; +exports.eqByFn = eqByFn; exports.eqProps = eqProps; exports.equals = equals; exports.evolve = evolve; @@ -2400,6 +2434,8 @@ exports.findLastIndex = findLastIndex; exports.flatten = flatten; exports.flip = flip; exports.forEach = forEach; +exports.forEachObjIndexed = forEachObjIndexed; +exports.forEachObjIndexedFn = forEachObjIndexedFn; exports.fromPairs = fromPairs; exports.groupBy = groupBy; exports.groupWith = groupWith; @@ -2446,6 +2482,7 @@ exports.mergeDeepRight = mergeDeepRight; exports.mergeLeft = mergeLeft; exports.mergeRight = mergeRight; exports.mergeWith = mergeWith; +exports.mergeWithFn = mergeWithFn; exports.min = min; exports.minBy = minBy; exports.minByFn = minByFn; diff --git a/dist/rambda.umd.js b/dist/rambda.umd.js index 84f673d6..22e4c025 100644 --- a/dist/rambda.umd.js +++ b/dist/rambda.umd.js @@ -1 +1 @@ -!function(n,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((n="undefined"!=typeof globalThis?globalThis:n||self).R={})}(this,function(n){"use strict";function a(n,l){switch(n){case 0:return function(){return l.apply(this,arguments)};case 1:return function(n){return l.apply(this,arguments)};case 2:return function(n,r){return l.apply(this,arguments)};case 3:return function(n,r,t){return l.apply(this,arguments)};case 4:return function(n,r,t,e){return l.apply(this,arguments)};case 5:return function(n,r,t,e,u){return l.apply(this,arguments)};case 6:return function(n,r,t,e,u,i){return l.apply(this,arguments)};case 7:return function(n,r,t,e,u,i,o){return l.apply(this,arguments)};case 8:return function(n,r,t,e,u,i,o,f){return l.apply(this,arguments)};case 9:return function(n,r,t,e,u,i,o,f,c){return l.apply(this,arguments)};default:return function(n,r,t,e,u,i,o,f,c,a){return l.apply(this,arguments)}}}function t(r,n){if(1===arguments.length)return function(n){return t(r,n)};if(10>>0,r>>>=0,Array(u));++en(r)?t:r}var Bn=c(qn);function Cn(n){return n.reduce(function(n,r){return n+r},0)}function Un(n){return Cn(n)/n.length}function W(r,n){return 1===arguments.length?function(n){return W(r,n)}:Object.assign({},r||{},n||{})}function R(r,t){var e;return 1===arguments.length?function(n){return R(r,n)}:(e=Q(r),Object.keys(t).forEach(function(n){"Object"===x(t[n])&&"Object"===x(r[n])?e[n]=R(r[n],t[n]):e[n]=t[n]}),e)}var Dn=c(function(r,n,t){var e=null!=n?n:{},u=null!=t?t:{},i={};return Object.keys(e).forEach(function(n){i[n]=void 0===u[n]?e[n]:r(e[n],u[n])}),Object.keys(u).forEach(function(n){void 0===i[n]&&(i[n]=void 0===e[n]?u[n]:r(e[n],u[n]))}),i});function Ln(n,r,t){return n(t) 4")};var e},n.forEach=function r(t,n){if(1===arguments.length)return function(n){return r(t,n)};if(void 0!==n){if(p(n))for(var e=0,u=n.length;e>>0,r>>>=0,Array(u));++en(r)?t:r}var Dn=c(Un);function Ln(n){return n.reduce(function(n,r){return n+r},0)}function zn(n){return Ln(n)/n.length}function W(r,n){return 1===arguments.length?function(n){return W(r,n)}:Object.assign({},r||{},n||{})}function q(r,t){var e;return 1===arguments.length?function(n){return q(r,n)}:(e=Q(r),Object.keys(t).forEach(function(n){"Object"===N(t[n])&&"Object"===N(r[n])?e[n]=q(r[n],t[n]):e[n]=t[n]}),e)}function _n(r,n,t){var e=null!=n?n:{},u=null!=t?t:{},i={};return Object.keys(e).forEach(function(n){i[n]=void 0===u[n]?e[n]:r(e[n],u[n])}),Object.keys(u).forEach(function(n){void 0===i[n]&&(i[n]=void 0===e[n]?u[n]:r(e[n],u[n]))}),i}var Mn=c(_n);function Hn(n,r,t){return n(t) 4")};var e},n.forEach=function r(t,n){if(1===arguments.length)return function(n){return r(t,n)};if(void 0!==n){if(!p(n))return In(t,n);for(var e=0,u=n.length;e { ```typescript -anyPass(predicates: { [K in keyof U]: (x: T) => x is U[K] +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`. @@ -801,7 +801,6 @@ It accepts list of `predicates` and returns a function. This function with its ` All TypeScript definitions ```typescript -anyPass(predicates: { [K in keyof U]: (x: T) => x is U[K]; }): (input: T) => input is U[number]; anyPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean; anyPass(predicates: ((...inputs: T[]) => boolean)[]): (...inputs: T[]) => boolean; ``` @@ -930,18 +929,18 @@ describe('anyPass', () => { filtered2 // $ExpectType number[] }) it('functions as a type guard', () => { - const isString = (x: unknown): x is string => typeof x === 'string'; - const isNumber = (x: unknown): x is number => typeof x === 'number'; - const isBoolean = (x: unknown): x is boolean => typeof x === 'boolean'; - - const isStringNumberOrBoolean = anyPass([isString, isNumber, isBoolean]); + const isString = (x: unknown): x is string => typeof x === 'string' + const isNumber = (x: unknown): x is number => typeof x === 'number' + const isBoolean = (x: unknown): x is boolean => typeof x === 'boolean' + + const isStringNumberOrBoolean = anyPass([isString, isNumber, isBoolean]) - isStringNumberOrBoolean // $ExpectType (input: unknown) => input is string | number | boolean + isStringNumberOrBoolean // $ExpectType (input: unknown) => boolean - const aValue: unknown = 1; + const aValue: unknown = 1 if (isStringNumberOrBoolean(aValue)) { - aValue // $ExpectType string | number | boolean + aValue // $ExpectType unknown } }) }) @@ -3438,6 +3437,10 @@ describe('R.dropRepeats', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeats) +### dropRepeatsBy + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#dropRepeatsBy) + ### dropRepeatsWith Try this R.dropRepeatsWith example in Rambda REPL @@ -3603,6 +3606,10 @@ describe('R.either', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#either) +### empty + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#empty) + ### endsWith ```typescript @@ -3764,6 +3771,10 @@ describe('R.endsWith - string', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#endsWith) +### eqBy + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#eqBy) + ### eqProps It returns `true` if property `prop` in `obj1` is equal to property `prop` in `obj2` according to `R.equals`. @@ -5422,36 +5433,46 @@ forEach(fn: ObjectIterator): (list: Dictionary) => Dictionary< import { isArray } from './_internals/isArray.js' import { keys } from './_internals/keys.js' -export function forEach(fn, list){ - if (arguments.length === 1) return _list => forEach(fn, _list) +export function forEachObjIndexedFn(fn, obj){ + let index = 0 + const listKeys = keys(obj) + const len = listKeys.length - if (list === undefined){ - return + while (index < len){ + const key = listKeys[ index ] + fn( + obj[ key ], key, obj + ) + index++ } - if (isArray(list)){ - let index = 0 - const len = list.length + return obj +} - while (index < len){ - fn(list[ index ]) - index++ - } - } else { +export function forEachObjIndexed(fn, list){ + if (arguments.length === 1) return _list => forEachObjIndexed(fn, _list) + + if (list === undefined) return + + return forEachObjIndexedFn(fn, list) +} + +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 listKeys = keys(list) - const len = listKeys.length + const len = iterable.length while (index < len){ - const key = listKeys[ index ] - fn( - list[ key ], key, list - ) + fn(iterable[ index ]) index++ } - } + } else return forEachObjIndexedFn(fn, iterable) - return list + return iterable } ``` @@ -5582,6 +5603,10 @@ describe('R.forEach with objects', () => { [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#forEach) +### forEachObjIndexed + +[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#forEachObjIndexed) + ### fromPairs It transforms a `listOfPairs` to an object. @@ -8780,7 +8805,7 @@ mergeWith(fn: (x: any, z: any) => any): (a: U, b: V) => Output; ```javascript import { curry } from './curry.js' -function mergeWithFn( +export function mergeWithFn( mergeFn, aInput, bInput ){ const a = aInput ?? {} @@ -8788,21 +8813,15 @@ function mergeWithFn( const willReturn = {} Object.keys(a).forEach(key => { - if (b[ key ] === undefined){ - willReturn[ key ] = a[ key ] - } else { - willReturn[ key ] = mergeFn(a[ key ], b[ 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 - if (a[ key ] === undefined){ - willReturn[ key ] = b[ key ] - } else { - willReturn[ key ] = mergeFn(a[ key ], b[ key ]) - } + if (a[ key ] === undefined) willReturn[ key ] = b[ key ] + else willReturn[ key ] = mergeFn(a[ key ], b[ key ]) }) return willReturn @@ -8819,10 +8838,10 @@ export const mergeWith = curry(mergeWithFn) ```javascript import { concat } from './concat.js' -import { mergeWith } from './mergeWith.js' +import { mergeWithFn } from './mergeWith.js' test('happy', () => { - const result = mergeWith( + const result = mergeWithFn( concat, { a : true, @@ -8835,8 +8854,8 @@ test('happy', () => { ) const expected = { a : true, - values : [ 10, 20, 15, 35 ], b : true, + values : [ 10, 20, 15, 35 ], } expect(result).toEqual(expected) }) @@ -8844,31 +8863,31 @@ test('happy', () => { // 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(mergeWith( + expect(mergeWithFn( concat, undefined, {} )).toEqual({}) }) it('if the first object is empty and the second nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, {}, null )).toEqual({}) }) it('if both objects are nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, undefined, null )).toEqual({}) }) it('if the first object is not empty and the second is nil', () => { - expect(mergeWith( + expect(mergeWithFn( concat, { a : 'a' }, null )).toEqual({ a : 'a' }) }) it('if the first object is nil and the second is not empty', () => { - expect(mergeWith( + expect(mergeWithFn( concat, undefined, { a : 'a' } )).toEqual({ a : 'a' }) }) @@ -10073,18 +10092,7 @@ test('index lens', () => { ```typescript -partial< - Args extends unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - ...args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never +partial(fn: (x0: V0, x1: V1) => T, args: [V0]): (x1: V1) => T ``` It is very similar to `R.curry`, but you can pass initial arguments when you create the curried function. @@ -10099,31 +10107,22 @@ The name comes from the fact that you partially inject the inputs. All TypeScript definitions ```typescript -partial< - Args extends unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - ...args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never; - -partial< - Args extends readonly unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never; +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; ``` @@ -10240,44 +10239,20 @@ describe('R.partial', () => { aBoolean: boolean, aNull: null ) { - return { aString, aNumber, aBoolean, aNull } + return {aString, aNumber, aBoolean, aNull} } // @ts-expect-error - partial(fn, 1); - - const fn1 = partial(fn, 'a') - - // @ts-expect-error - partial(fn1, 'b'); - - const fn2 = partial(fn1, 2) - const result = fn2(true, null) - result // $ExpectType { aString: string; aNumber: number; aBoolean: boolean; aNull: null; } - }) - - it('ramda', () => { - function fn( - aString: string, - aNumber: number, - aBoolean: boolean, - aNull: null - ) { - return { aString, aNumber, aBoolean, aNull } - } - - // @ts-expect-error - partial(fn, 1); + partial(fn, 1) const fn1 = partial(fn, ['a']) - - // @ts-expect-error - partial(fn1, ['b']); + partial(fn1, ['b']) const fn2 = partial(fn1, [2]) const result = fn2(true, null) result // $ExpectType { aString: string; aNumber: number; aBoolean: boolean; aNull: null; } - })}) + }) +}) ``` @@ -17452,6 +17427,20 @@ describe('R.zipWith', () => { ## ❯ CHANGELOG +8.5.0 + +- Revert changes in `R.anyPass` introduced in `8.4.0` release. The reason is that the change was breaking the library older than `5.2.0` TypeScript. + +- Wrong `R.partial` TS definition - [Issue #705](https://github.com/selfrefactor/rambda/issues/705) + +- Add `R.dropRepeatsBy` + +- Add `R.empty` + +- Add `R.eqBy` + +- Add `R.forEachObjIndexed` + 8.4.0 - Add `R.dissocPath` diff --git a/immutable.d.ts b/immutable.d.ts index 71f0718a..fbc2a69b 100644 --- a/immutable.d.ts +++ b/immutable.d.ts @@ -231,7 +231,6 @@ export function any(predicate: (x: T) => boolean): (list: readonly T[]) => bo /** * 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`. */ -export function anyPass(predicates: { readonly [K in keyof U]: (x: T) => x is U[K]; }): (input: T) => input is U[number]; export function anyPass(predicates: readonly ((x: T) => boolean)[]): (input: T) => boolean; export function anyPass(predicates: readonly ((...inputs: readonly T[]) => boolean)[]): (...inputs: readonly T[]) => boolean; @@ -541,6 +540,12 @@ export function dropLastWhile(predicate: (x: T) => boolean): (iterable: re */ export function dropRepeats(list: readonly T[]): readonly T[]; +export function dropRepeatsBy(fn: (a: T) => U, list: readonly T[]): readonly T[]; +export function dropRepeatsBy( + fn: (a: T) => U +): (list: readonly T[]) => readonly T[]; +export function dropRepeatsBy(fn: any): (list: readonly T[]) => readonly T[]; + export function dropRepeatsWith(predicate: (x: T, y: T) => boolean, list: readonly T[]): readonly T[]; export function dropRepeatsWith(predicate: (x: T, y: T) => boolean): (list: readonly T[]) => readonly T[]; @@ -559,6 +564,8 @@ export function either(firstPredicate: Predicate, secondPredicate: Predica export function either(firstPredicate: Predicate): (secondPredicate: Predicate) => Predicate; export function either(firstPredicate: Pred): (secondPredicate: Pred) => Pred; +export function empty(x: T): T; + /** * 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. @@ -568,6 +575,13 @@ export function endsWith(question: T): (str: string) => boolea export function endsWith(question: readonly T[], list: readonly T[]): boolean; export function endsWith(question: readonly T[]): (list: readonly T[]) => boolean; +export function eqBy(fn: (a: T) => unknown, a: T, b: T): boolean; +export function eqBy(fn: (a: T) => unknown, a: T): (b: T) => boolean; +export function eqBy(fn: (a: T) => unknown): { + (a: T, b: T): boolean; + (a: T): (b: T) => boolean; +}; + /** * It returns `true` if property `prop` in `obj1` is equal to property `prop` in `obj2` according to `R.equals`. */ @@ -647,6 +661,9 @@ export function forEach(fn: Iterator): (list: readonly T[]) => reado export function forEach(fn: ObjectIterator, list: Dictionary): Dictionary; export function forEach(fn: ObjectIterator): (list: Dictionary) => Dictionary; +export function forEachObjIndexed(fn: (value: T[keyof T], key: keyof T, obj: T) => void, obj: T): T; +export function forEachObjIndexed(fn: (value: T[keyof T], key: keyof T, obj: T) => void): (obj: T) => T; + /** * It transforms a `listOfPairs` to an object. */ @@ -1096,31 +1113,22 @@ export function over(lens: Lens): (fn: Arity1Fn, value: readonly T[]) => read * `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. */ -export function partial< - Args extends readonly unknown[], - ArgsGiven extends readonly [...Partial], - R ->( - fn: (...args: Args) => R, - ...args: ArgsGiven -): Args extends readonly [...{ readonly[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends readonly [] - ? R - : (...args: ArgsRemaining) => R - : never; - -export function partial< - Args extends readonly unknown[], - ArgsGiven extends readonly [...Partial], - R ->( - fn: (...args: Args) => R, - args: ArgsGiven -): Args extends readonly [...{ readonly[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends readonly [] - ? R - : (...args: ArgsRemaining) => R - : never; +export function partial(fn: (x0: V0, x1: V1) => T, args: readonly [V0]): (x1: V1) => T; +export function partial(fn: (x0: V0, x1: V1, x2: V2) => T, args: readonly [V0, V1]): (x2: V2) => T; +export function partial(fn: (x0: V0, x1: V1, x2: V2) => T, args: readonly [V0]): (x1: V1, x2: V2) => T; +export function partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: readonly [V0, V1, V2], +): (x2: V3) => T; +export function partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: readonly [V0, V1], +): (x2: V2, x3: V3) => T; +export function partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: readonly [V0], +): (x1: V1, x2: V2, x3: V3) => T; +export function partial(fn: (...a: readonly any[]) => T, args: readonly any[]): (...a: readonly any[]) => T; /** * `R.partialObject` is a curry helper designed specifically for functions accepting object as a single argument. diff --git a/index.d.ts b/index.d.ts index 1278a0dc..4c755c6d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -231,7 +231,6 @@ export function any(predicate: (x: T) => boolean): (list: 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`. */ -export function anyPass(predicates: { [K in keyof U]: (x: T) => x is U[K]; }): (input: T) => input is U[number]; export function anyPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean; export function anyPass(predicates: ((...inputs: T[]) => boolean)[]): (...inputs: T[]) => boolean; @@ -541,6 +540,12 @@ export function dropLastWhile(predicate: (x: T) => boolean): (iterable: T[ */ export function dropRepeats(list: T[]): T[]; +export function dropRepeatsBy(fn: (a: T) => U, list: T[]): T[]; +export function dropRepeatsBy( + fn: (a: T) => U +): (list: T[]) => T[]; +export function dropRepeatsBy(fn: any): (list: T[]) => T[]; + export function dropRepeatsWith(predicate: (x: T, y: T) => boolean, list: T[]): T[]; export function dropRepeatsWith(predicate: (x: T, y: T) => boolean): (list: T[]) => T[]; @@ -559,6 +564,8 @@ export function either(firstPredicate: Predicate, secondPredicate: Predica export function either(firstPredicate: Predicate): (secondPredicate: Predicate) => Predicate; export function either(firstPredicate: Pred): (secondPredicate: Pred) => Pred; +export function empty(x: T): T; + /** * 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. @@ -568,6 +575,13 @@ export function endsWith(question: T): (str: string) => boolea export function endsWith(question: T[], list: T[]): boolean; export function endsWith(question: T[]): (list: T[]) => boolean; +export function eqBy(fn: (a: T) => unknown, a: T, b: T): boolean; +export function eqBy(fn: (a: T) => unknown, a: T): (b: T) => boolean; +export function eqBy(fn: (a: T) => unknown): { + (a: T, b: T): boolean; + (a: T): (b: T) => boolean; +}; + /** * It returns `true` if property `prop` in `obj1` is equal to property `prop` in `obj2` according to `R.equals`. */ @@ -647,6 +661,9 @@ export function forEach(fn: Iterator): (list: T[]) => T[]; export function forEach(fn: ObjectIterator, list: Dictionary): Dictionary; export function forEach(fn: ObjectIterator): (list: Dictionary) => Dictionary; +export function forEachObjIndexed(fn: (value: T[keyof T], key: keyof T, obj: T) => void, obj: T): T; +export function forEachObjIndexed(fn: (value: T[keyof T], key: keyof T, obj: T) => void): (obj: T) => T; + /** * It transforms a `listOfPairs` to an object. */ @@ -1096,31 +1113,22 @@ export function over(lens: Lens): (fn: Arity1Fn, value: T[]) => T[]; * `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. */ -export function partial< - Args extends unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - ...args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never; - -export function partial< - Args extends readonly unknown[], - ArgsGiven extends [...Partial], - R ->( - fn: (...args: Args) => R, - args: ArgsGiven -): Args extends [...{[K in keyof ArgsGiven]: Args[K]}, ...infer ArgsRemaining] - ? ArgsRemaining extends [] - ? R - : (...args: ArgsRemaining) => R - : never; +export function partial(fn: (x0: V0, x1: V1) => T, args: [V0]): (x1: V1) => T; +export function partial(fn: (x0: V0, x1: V1, x2: V2) => T, args: [V0, V1]): (x2: V2) => T; +export function partial(fn: (x0: V0, x1: V1, x2: V2) => T, args: [V0]): (x1: V1, x2: V2) => T; +export function partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: [V0, V1, V2], +): (x2: V3) => T; +export function partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: [V0, V1], +): (x2: V2, x3: V3) => T; +export function partial( + fn: (x0: V0, x1: V1, x2: V2, x3: V3) => T, + args: [V0], +): (x1: V1, x2: V2, x3: V3) => T; +export function partial(fn: (...a: any[]) => T, args: any[]): (...a: any[]) => T; /** * `R.partialObject` is a curry helper designed specifically for functions accepting object as a single argument. diff --git a/rambda.js b/rambda.js index 89058e36..0634725b 100644 --- a/rambda.js +++ b/rambda.js @@ -1,4 +1,6 @@ /// +export * from './src/F.js' +export * from './src/T.js' export * from './src/add.js' export * from './src/addIndex.js' export * from './src/addIndexRight.js' @@ -49,14 +51,16 @@ export * from './src/drop.js' export * from './src/dropLast.js' export * from './src/dropLastWhile.js' export * from './src/dropRepeats.js' +export * from './src/dropRepeatsBy.js' export * from './src/dropRepeatsWith.js' export * from './src/dropWhile.js' export * from './src/either.js' +export * from './src/empty.js' export * from './src/endsWith.js' +export * from './src/eqBy.js' export * from './src/eqProps.js' export * from './src/equals.js' export * from './src/evolve.js' -export * from './src/F.js' export * from './src/filter.js' export * from './src/find.js' export * from './src/findIndex.js' @@ -142,8 +146,8 @@ export * from './src/prop.js' export * from './src/propEq.js' export * from './src/propIs.js' export * from './src/propOr.js' -export * from './src/props.js' export * from './src/propSatisfies.js' +export * from './src/props.js' export * from './src/range.js' export * from './src/reduce.js' export * from './src/reject.js' @@ -163,7 +167,6 @@ export * from './src/startsWith.js' export * from './src/subtract.js' export * from './src/sum.js' export * from './src/symmetricDifference.js' -export * from './src/T.js' export * from './src/tail.js' export * from './src/take.js' export * from './src/takeLast.js' diff --git a/src/dropRepeatsBy.js b/src/dropRepeatsBy.js new file mode 100644 index 00000000..285918ea --- /dev/null +++ b/src/dropRepeatsBy.js @@ -0,0 +1,21 @@ +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 + }) +} diff --git a/src/empty.js b/src/empty.js new file mode 100644 index 00000000..7d7cdd9a --- /dev/null +++ b/src/empty.js @@ -0,0 +1,15 @@ +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 {} +} diff --git a/src/eqBy.js b/src/eqBy.js new file mode 100644 index 00000000..b20a6342 --- /dev/null +++ b/src/eqBy.js @@ -0,0 +1,10 @@ +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) diff --git a/src/forEach.js b/src/forEach.js index 4f7e6d3d..a320b28f 100644 --- a/src/forEach.js +++ b/src/forEach.js @@ -1,34 +1,44 @@ import { isArray } from './_internals/isArray.js' import { keys } from './_internals/keys.js' -export function forEach(fn, list){ - if (arguments.length === 1) return _list => forEach(fn, _list) +export function forEachObjIndexedFn(fn, obj){ + let index = 0 + const listKeys = keys(obj) + const len = listKeys.length - if (list === undefined){ - return + while (index < len){ + const key = listKeys[ index ] + fn( + obj[ key ], key, obj + ) + index++ } - if (isArray(list)){ - let index = 0 - const len = list.length + return obj +} - while (index < len){ - fn(list[ index ]) - index++ - } - } else { +export function forEachObjIndexed(fn, list){ + if (arguments.length === 1) return _list => forEachObjIndexed(fn, _list) + + if (list === undefined) return + + return forEachObjIndexedFn(fn, list) +} + +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 listKeys = keys(list) - const len = listKeys.length + const len = iterable.length while (index < len){ - const key = listKeys[ index ] - fn( - list[ key ], key, list - ) + fn(iterable[ index ]) index++ } - } + } else return forEachObjIndexedFn(fn, iterable) - return list + return iterable } diff --git a/src/mergeWith.js b/src/mergeWith.js index 4eb00396..718e08da 100644 --- a/src/mergeWith.js +++ b/src/mergeWith.js @@ -1,6 +1,6 @@ import { curry } from './curry.js' -function mergeWithFn( +export function mergeWithFn( mergeFn, aInput, bInput ){ const a = aInput ?? {} @@ -8,21 +8,15 @@ function mergeWithFn( const willReturn = {} Object.keys(a).forEach(key => { - if (b[ key ] === undefined){ - willReturn[ key ] = a[ key ] - } else { - willReturn[ key ] = mergeFn(a[ key ], b[ 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 - if (a[ key ] === undefined){ - willReturn[ key ] = b[ key ] - } else { - willReturn[ key ] = mergeFn(a[ key ], b[ key ]) - } + if (a[ key ] === undefined) willReturn[ key ] = b[ key ] + else willReturn[ key ] = mergeFn(a[ key ], b[ key ]) }) return willReturn