diff --git a/.github/README.md b/.github/README.md index 20e286f8..40032351 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1224,28 +1224,28 @@ test('with strings', () => { import {append, prepend} from 'rambda' const listOfNumbers = [1, 2, 3] -const listOfNumbersAndStrings = [1, "b", 3] +const listOfNumbersAndStrings = [1, 'b', 3] describe('R.append/R.prepend', () => { - describe('with the same primitive type as the array\'s elements', () => { + describe("with the same primitive type as the array's elements", () => { it('uncurried', () => { // @ts-expect-error - append("d", listOfNumbers) + append('d', listOfNumbers) // @ts-expect-error - prepend("d", listOfNumbers) + prepend('d', listOfNumbers) append(4, listOfNumbers) // $ExpectType number[] prepend(4, listOfNumbers) // $ExpectType number[] }) it('curried', () => { // @ts-expect-error - append("d")(listOfNumbers) + append('d')(listOfNumbers) append(4)(listOfNumbers) // $ExpectType number[] prepend(4)(listOfNumbers) // $ExpectType number[] }) - }); + }) - describe('with a subtype of the array\'s elements', () => { + describe("with a subtype of the array's elements", () => { it('uncurried', () => { // @ts-expect-error append(true, listOfNumbersAndStrings) @@ -1259,25 +1259,25 @@ describe('R.append/R.prepend', () => { append(4)(listOfNumbersAndStrings) // $ExpectType (string | number)[] prepend(4)(listOfNumbersAndStrings) // $ExpectType (string | number)[] }) - }); + }) - describe('expanding the type of the array\'s elements', () => { + describe("expanding the type of the array's elements", () => { it('uncurried', () => { // @ts-expect-error - append("d", listOfNumbers) - append("d", listOfNumbers) // $ExpectType (string | number)[] - prepend("d", listOfNumbers) // $ExpectType (string | number)[] + append('d', listOfNumbers) + append('d', listOfNumbers) // $ExpectType (string | number)[] + prepend('d', listOfNumbers) // $ExpectType (string | number)[] }) it('curried', () => { // @ts-expect-error - append("d")(listOfNumbers) - const appendD = append("d"); + append('d')(listOfNumbers) + const appendD = append('d') appendD(listOfNumbers) // $ExpectType (string | number)[] - const prependD = prepend("d"); + const prependD = prepend('d') prependD(listOfNumbers) // $ExpectType (string | number)[] }) - }); + }) }) ``` @@ -1896,14 +1896,14 @@ assocPath(path: Path): (newValue: any) => (obj: object) => Output; R.assocPath source ```javascript -import { createPath } from '../src/_internals/createPath.js' import { cloneList } from './_internals/cloneList.js' +import { createPath } from './_internals/createPath.js' import { isArray } from './_internals/isArray.js' -import { isInteger } from './_internals/isInteger.js' -import { assoc } from './assoc.js' +import { isIndexInteger } from './_internals/isInteger.js' +import { assocFn } from './assoc.js' import { curry } from './curry.js' -function assocPathFn( +export function assocPathFn( path, newValue, input ){ const pathArrValue = createPath(path) @@ -1917,7 +1917,7 @@ function assocPathFn( !input.hasOwnProperty(index) const nextInput = condition ? - isInteger(pathArrValue[ 1 ]) ? + isIndexInteger(pathArrValue[ 1 ]) ? [] : {} : input[ index ] @@ -1929,14 +1929,14 @@ function assocPathFn( ) } - if (isInteger(index) && isArray(input)){ + if (isIndexInteger(index) && isArray(input)){ const arr = cloneList(input) arr[ index ] = newValue return arr } - return assoc( + return assocFn( index, newValue, input ) } @@ -1951,21 +1951,45 @@ export const assocPath = curry(assocPathFn) Tests ```javascript -import { assocPath } from './assocPath.js' +import { assocPathFn } from './assocPath.js' + +test.only('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 result = assocPath( + 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(result).toEqual(expected) + expect(result1).toEqual(expected) + expect(result2).toEqual(expected) }) test('difference with ramda - doesn\'t overwrite primitive values with keys in the path', () => { @@ -10258,22 +10282,20 @@ omit(propsToOmit: string): (obj: object) => T; ```javascript import { createPath } from './_internals/createPath.js' +import { includes } from './_internals/includes.js' 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 propsToOmitValue = createPath(propsToOmit, ',') const willReturn = {} - for (const key in obj){ - if (!propsToOmitValue.includes(key)){ + for (const key in obj) + if (!includes(key, propsToOmitValue)) willReturn[ key ] = obj[ key ] - } - } return willReturn } @@ -10286,8 +10308,6 @@ export function omit(propsToOmit, obj){ Tests ```javascript -import { omitRamda } from 'ramda' - import { omit } from './omit.js' test('with string as condition', () => { @@ -10304,7 +10324,7 @@ test('with string as condition', () => { expect(resultCurry).toEqual(expectedResult) }) -test('with number as property to omit', () => { +test.only('with number as property to omit', () => { const obj = { 1 : 1, b : 2, @@ -12161,7 +12181,11 @@ test('works with list as input and number as props - props to pick is a string', test('with symbol', () => { const symbolProp = Symbol('s') - expect(pick([ symbolProp ], { [ symbolProp ] : 'a' })).toMatchInlineSnapshot('{}') + expect(pick([ symbolProp ], { [ symbolProp ] : 'a' })).toMatchInlineSnapshot(` +{ + Symbol(s): "a", +} +`) }) ``` diff --git a/.vscode/launch.json b/.vscode/launch.json index 1d91774a..e22c64c1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "--verbose", "-i", "--no-cache", - "${file}" + "${fileBasenameNoExtension}" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" diff --git a/NEXT_VERSION_CHECKLIST.md b/NEXT_VERSION_CHECKLIST.md index db04211e..4b38c752 100644 --- a/NEXT_VERSION_CHECKLIST.md +++ b/NEXT_VERSION_CHECKLIST.md @@ -6,6 +6,10 @@ in js project like niketa theme, go to source lead to readable code, is ramda th fix https://github.com/selfrefactor/rambdax/issues/93 release X + +no need to test curried methods such as replace + +latest changes might have effect in benchmarks --- group TS test for similar methods diff --git a/README.md b/README.md index 8672fb2e..26281f1f 100644 --- a/README.md +++ b/README.md @@ -1163,28 +1163,28 @@ test('with strings', () => { import {append, prepend} from 'rambda' const listOfNumbers = [1, 2, 3] -const listOfNumbersAndStrings = [1, "b", 3] +const listOfNumbersAndStrings = [1, 'b', 3] describe('R.append/R.prepend', () => { - describe('with the same primitive type as the array\'s elements', () => { + describe("with the same primitive type as the array's elements", () => { it('uncurried', () => { // @ts-expect-error - append("d", listOfNumbers) + append('d', listOfNumbers) // @ts-expect-error - prepend("d", listOfNumbers) + prepend('d', listOfNumbers) append(4, listOfNumbers) // $ExpectType number[] prepend(4, listOfNumbers) // $ExpectType number[] }) it('curried', () => { // @ts-expect-error - append("d")(listOfNumbers) + append('d')(listOfNumbers) append(4)(listOfNumbers) // $ExpectType number[] prepend(4)(listOfNumbers) // $ExpectType number[] }) - }); + }) - describe('with a subtype of the array\'s elements', () => { + describe("with a subtype of the array's elements", () => { it('uncurried', () => { // @ts-expect-error append(true, listOfNumbersAndStrings) @@ -1198,25 +1198,25 @@ describe('R.append/R.prepend', () => { append(4)(listOfNumbersAndStrings) // $ExpectType (string | number)[] prepend(4)(listOfNumbersAndStrings) // $ExpectType (string | number)[] }) - }); + }) - describe('expanding the type of the array\'s elements', () => { + describe("expanding the type of the array's elements", () => { it('uncurried', () => { // @ts-expect-error - append("d", listOfNumbers) - append("d", listOfNumbers) // $ExpectType (string | number)[] - prepend("d", listOfNumbers) // $ExpectType (string | number)[] + append('d', listOfNumbers) + append('d', listOfNumbers) // $ExpectType (string | number)[] + prepend('d', listOfNumbers) // $ExpectType (string | number)[] }) it('curried', () => { // @ts-expect-error - append("d")(listOfNumbers) - const appendD = append("d"); + append('d')(listOfNumbers) + const appendD = append('d') appendD(listOfNumbers) // $ExpectType (string | number)[] - const prependD = prepend("d"); + const prependD = prepend('d') prependD(listOfNumbers) // $ExpectType (string | number)[] }) - }); + }) }) ``` @@ -1806,14 +1806,14 @@ assocPath(path: Path): (newValue: any) => (obj: object) => Output; R.assocPath source ```javascript -import { createPath } from '../src/_internals/createPath.js' import { cloneList } from './_internals/cloneList.js' +import { createPath } from './_internals/createPath.js' import { isArray } from './_internals/isArray.js' -import { isInteger } from './_internals/isInteger.js' -import { assoc } from './assoc.js' +import { isIndexInteger } from './_internals/isInteger.js' +import { assocFn } from './assoc.js' import { curry } from './curry.js' -function assocPathFn( +export function assocPathFn( path, newValue, input ){ const pathArrValue = createPath(path) @@ -1827,7 +1827,7 @@ function assocPathFn( !input.hasOwnProperty(index) const nextInput = condition ? - isInteger(pathArrValue[ 1 ]) ? + isIndexInteger(pathArrValue[ 1 ]) ? [] : {} : input[ index ] @@ -1839,14 +1839,14 @@ function assocPathFn( ) } - if (isInteger(index) && isArray(input)){ + if (isIndexInteger(index) && isArray(input)){ const arr = cloneList(input) arr[ index ] = newValue return arr } - return assoc( + return assocFn( index, newValue, input ) } @@ -1861,21 +1861,45 @@ export const assocPath = curry(assocPathFn) Tests ```javascript -import { assocPath } from './assocPath.js' +import { assocPathFn } from './assocPath.js' + +test.only('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 result = assocPath( + 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(result).toEqual(expected) + expect(result1).toEqual(expected) + expect(result2).toEqual(expected) }) test('difference with ramda - doesn\'t overwrite primitive values with keys in the path', () => { @@ -9654,22 +9678,20 @@ omit(propsToOmit: string): (obj: object) => T; ```javascript import { createPath } from './_internals/createPath.js' +import { includes } from './_internals/includes.js' 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 propsToOmitValue = createPath(propsToOmit, ',') const willReturn = {} - for (const key in obj){ - if (!propsToOmitValue.includes(key)){ + for (const key in obj) + if (!includes(key, propsToOmitValue)) willReturn[ key ] = obj[ key ] - } - } return willReturn } @@ -9682,8 +9704,6 @@ export function omit(propsToOmit, obj){ Tests ```javascript -import { omitRamda } from 'ramda' - import { omit } from './omit.js' test('with string as condition', () => { @@ -9700,7 +9720,7 @@ test('with string as condition', () => { expect(resultCurry).toEqual(expectedResult) }) -test('with number as property to omit', () => { +test.only('with number as property to omit', () => { const obj = { 1 : 1, b : 2, @@ -11402,7 +11422,11 @@ test('works with list as input and number as props - props to pick is a string', test('with symbol', () => { const symbolProp = Symbol('s') - expect(pick([ symbolProp ], { [ symbolProp ] : 'a' })).toMatchInlineSnapshot('{}') + expect(pick([ symbolProp ], { [ symbolProp ] : 'a' })).toMatchInlineSnapshot(` +{ + Symbol(s): "a", +} +`) }) ``` diff --git a/dist/rambda.js b/dist/rambda.js index 9c43dd89..9b96bcc0 100644 --- a/dist/rambda.js +++ b/dist/rambda.js @@ -332,14 +332,15 @@ function assocFn(prop, newValue, obj) { } const assoc = curry(assocFn); -function createPath(path, delimiter = '.') { - return typeof path === 'string' ? path.split(delimiter) : path.map(String); -} - function _isInteger(n) { return n << 0 === n; } const isInteger = Number.isInteger || _isInteger; +const isIndexInteger = index => Number.isInteger(Number(index)); + +function createPath(path, delimiter = '.') { + return typeof path === 'string' ? path.split(delimiter).map(x => isInteger(x) ? Number(x) : x) : path; +} function assocPathFn(path, newValue, input) { const pathArrValue = createPath(path); @@ -347,15 +348,15 @@ function assocPathFn(path, newValue, input) { const index = pathArrValue[0]; if (pathArrValue.length > 1) { const condition = typeof input !== 'object' || input === null || !input.hasOwnProperty(index); - const nextInput = condition ? isInteger(pathArrValue[1]) ? [] : {} : input[index]; + const nextInput = condition ? isIndexInteger(pathArrValue[1]) ? [] : {} : input[index]; newValue = assocPathFn(Array.prototype.slice.call(pathArrValue, 1), newValue, nextInput); } - if (isInteger(index) && isArray(input)) { + if (isIndexInteger(index) && isArray(input)) { const arr = cloneList(input); arr[index] = newValue; return arr; } - return assoc(index, newValue, input); + return assocFn(index, newValue, input); } const assocPath = curry(assocPathFn); @@ -786,8 +787,8 @@ function equals(a, b) { return false; } -function includes(valueToFind, iterable) { - if (arguments.length === 1) return _iterable => includes(valueToFind, _iterable); +function includes$1(valueToFind, iterable) { + if (arguments.length === 1) return _iterable => includes$1(valueToFind, _iterable); if (typeof iterable === 'string') { return iterable.includes(valueToFind); } @@ -842,7 +843,7 @@ function uniq(list) { function difference(a, b) { if (arguments.length === 1) return _b => difference(a, _b); - return uniq(a).filter(aInstance => !includes(aInstance, b)); + return uniq(a).filter(aInstance => !includes$1(aInstance, b)); } function differenceWithFn(fn, a, b) { @@ -919,18 +920,25 @@ function _toPropertyKey(arg) { return typeof key === "symbol" ? key : String(key); } +function compare(a, b) { + return String(a) === String(b); +} + +function includes(a, list) { + let index = -1; + const { + length + } = list; + while (++index < length) if (compare(list[index], a)) return true; + return false; +} + function omit(propsToOmit, obj) { if (arguments.length === 1) return _obj => omit(propsToOmit, _obj); - if (obj === null || obj === undefined) { - return undefined; - } + if (obj === null || obj === undefined) return undefined; const propsToOmitValue = createPath(propsToOmit, ','); const willReturn = {}; - for (const key in obj) { - if (!propsToOmitValue.includes(key)) { - willReturn[key] = obj[key]; - } - } + for (const key in obj) if (!includes(key, propsToOmitValue)) willReturn[key] = obj[key]; return willReturn; } @@ -979,7 +987,7 @@ function dissocPath(pathInput, input) { const index = pathArrValue[0]; const condition = typeof input !== 'object' || input === null || !input.hasOwnProperty(index); if (pathArrValue.length > 1) { - const nextInput = condition ? isInteger(pathArrValue[1]) ? [] : {} : input[index]; + 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); @@ -1444,7 +1452,7 @@ function init(listOrString) { function intersection(listA, listB) { if (arguments.length === 1) return _list => intersection(listA, _list); - return filter(x => includes(x, listA), listB); + return filter(x => includes$1(x, listA), listB); } function intersperse(separator, list) { @@ -2073,7 +2081,7 @@ function symmetricDifference(x, y) { if (arguments.length === 1) { return _y => symmetricDifference(x, _y); } - return concat(filter(value => !includes(value, y), x), filter(value => !includes(value, x), y)); + return concat(filter(value => !includes$1(value, y), x), filter(value => !includes$1(value, x), y)); } function takeLast(howMany, listOrString) { @@ -2197,7 +2205,7 @@ function union(x, y) { if (arguments.length === 1) return _y => union(x, _y); const toReturn = cloneList(x); y.forEach(yInstance => { - if (!includes(yInstance, x)) toReturn.push(yInstance); + if (!includes$1(yInstance, x)) toReturn.push(yInstance); }); return toReturn; } @@ -2374,7 +2382,9 @@ exports.applySpec = applySpec; exports.applyTo = applyTo; exports.ascend = ascend; exports.assoc = assoc; +exports.assocFn = assocFn; exports.assocPath = assocPath; +exports.assocPathFn = assocPathFn; exports.binary = binary; exports.bind = bind; exports.both = both; @@ -2437,7 +2447,7 @@ exports.identical = identical; exports.identity = identity; exports.ifElse = ifElse; exports.inc = inc; -exports.includes = includes; +exports.includes = includes$1; exports.indexBy = indexBy; exports.indexOf = indexOf; exports.init = init; diff --git a/dist/rambda.umd.js b/dist/rambda.umd.js index 30bff287..4e929485 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 Rn=c(Wn);function qn(n){return n.reduce(function(n,r){return n+r},0)}function Bn(n){return qn(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=b(r),Object.keys(t).forEach(function(n){"Object"===N(t[n])&&"Object"===N(r[n])?e[n]=R(r[n],t[n]):e[n]=t[n]}),e)}var Cn=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 Un(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 Cn=c(Bn);function Un(n){return n.reduce(function(n,r){return n+r},0)}function _n(n){return Un(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 { import {append, prepend} from 'rambda' const listOfNumbers = [1, 2, 3] -const listOfNumbersAndStrings = [1, "b", 3] +const listOfNumbersAndStrings = [1, 'b', 3] describe('R.append/R.prepend', () => { - describe('with the same primitive type as the array\'s elements', () => { + describe("with the same primitive type as the array's elements", () => { it('uncurried', () => { // @ts-expect-error - append("d", listOfNumbers) + append('d', listOfNumbers) // @ts-expect-error - prepend("d", listOfNumbers) + prepend('d', listOfNumbers) append(4, listOfNumbers) // $ExpectType number[] prepend(4, listOfNumbers) // $ExpectType number[] }) it('curried', () => { // @ts-expect-error - append("d")(listOfNumbers) + append('d')(listOfNumbers) append(4)(listOfNumbers) // $ExpectType number[] prepend(4)(listOfNumbers) // $ExpectType number[] }) - }); + }) - describe('with a subtype of the array\'s elements', () => { + describe("with a subtype of the array's elements", () => { it('uncurried', () => { // @ts-expect-error append(true, listOfNumbersAndStrings) @@ -1198,25 +1198,25 @@ describe('R.append/R.prepend', () => { append(4)(listOfNumbersAndStrings) // $ExpectType (string | number)[] prepend(4)(listOfNumbersAndStrings) // $ExpectType (string | number)[] }) - }); + }) - describe('expanding the type of the array\'s elements', () => { + describe("expanding the type of the array's elements", () => { it('uncurried', () => { // @ts-expect-error - append("d", listOfNumbers) - append("d", listOfNumbers) // $ExpectType (string | number)[] - prepend("d", listOfNumbers) // $ExpectType (string | number)[] + append('d', listOfNumbers) + append('d', listOfNumbers) // $ExpectType (string | number)[] + prepend('d', listOfNumbers) // $ExpectType (string | number)[] }) it('curried', () => { // @ts-expect-error - append("d")(listOfNumbers) - const appendD = append("d"); + append('d')(listOfNumbers) + const appendD = append('d') appendD(listOfNumbers) // $ExpectType (string | number)[] - const prependD = prepend("d"); + const prependD = prepend('d') prependD(listOfNumbers) // $ExpectType (string | number)[] }) - }); + }) }) ``` @@ -1806,14 +1806,14 @@ assocPath(path: Path): (newValue: any) => (obj: object) => Output; R.assocPath source ```javascript -import { createPath } from '../src/_internals/createPath.js' import { cloneList } from './_internals/cloneList.js' +import { createPath } from './_internals/createPath.js' import { isArray } from './_internals/isArray.js' -import { isInteger } from './_internals/isInteger.js' -import { assoc } from './assoc.js' +import { isIndexInteger } from './_internals/isInteger.js' +import { assocFn } from './assoc.js' import { curry } from './curry.js' -function assocPathFn( +export function assocPathFn( path, newValue, input ){ const pathArrValue = createPath(path) @@ -1827,7 +1827,7 @@ function assocPathFn( !input.hasOwnProperty(index) const nextInput = condition ? - isInteger(pathArrValue[ 1 ]) ? + isIndexInteger(pathArrValue[ 1 ]) ? [] : {} : input[ index ] @@ -1839,14 +1839,14 @@ function assocPathFn( ) } - if (isInteger(index) && isArray(input)){ + if (isIndexInteger(index) && isArray(input)){ const arr = cloneList(input) arr[ index ] = newValue return arr } - return assoc( + return assocFn( index, newValue, input ) } @@ -1861,21 +1861,45 @@ export const assocPath = curry(assocPathFn) Tests ```javascript -import { assocPath } from './assocPath.js' +import { assocPathFn } from './assocPath.js' + +test.only('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 result = assocPath( + 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(result).toEqual(expected) + expect(result1).toEqual(expected) + expect(result2).toEqual(expected) }) test('difference with ramda - doesn\'t overwrite primitive values with keys in the path', () => { @@ -9654,22 +9678,20 @@ omit(propsToOmit: string): (obj: object) => T; ```javascript import { createPath } from './_internals/createPath.js' +import { includes } from './_internals/includes.js' 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 propsToOmitValue = createPath(propsToOmit, ',') const willReturn = {} - for (const key in obj){ - if (!propsToOmitValue.includes(key)){ + for (const key in obj) + if (!includes(key, propsToOmitValue)) willReturn[ key ] = obj[ key ] - } - } return willReturn } @@ -9682,8 +9704,6 @@ export function omit(propsToOmit, obj){ Tests ```javascript -import { omitRamda } from 'ramda' - import { omit } from './omit.js' test('with string as condition', () => { @@ -9700,7 +9720,7 @@ test('with string as condition', () => { expect(resultCurry).toEqual(expectedResult) }) -test('with number as property to omit', () => { +test.only('with number as property to omit', () => { const obj = { 1 : 1, b : 2, @@ -11402,7 +11422,11 @@ test('works with list as input and number as props - props to pick is a string', test('with symbol', () => { const symbolProp = Symbol('s') - expect(pick([ symbolProp ], { [ symbolProp ] : 'a' })).toMatchInlineSnapshot('{}') + expect(pick([ symbolProp ], { [ symbolProp ] : 'a' })).toMatchInlineSnapshot(` +{ + Symbol(s): "a", +} +`) }) ``` diff --git a/rambda.js b/rambda.js index 89058e36..2576136b 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' @@ -56,7 +58,6 @@ 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' @@ -142,8 +143,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 +164,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/source/_internals/compare.js b/source/_internals/compare.js new file mode 100644 index 00000000..a2952de4 --- /dev/null +++ b/source/_internals/compare.js @@ -0,0 +1,3 @@ +export function compare(a, b){ + return String(a) === String(b) +} diff --git a/source/_internals/createPath.js b/source/_internals/createPath.js index 278e80e1..3848fbce 100644 --- a/source/_internals/createPath.js +++ b/source/_internals/createPath.js @@ -1,3 +1,7 @@ +import { isInteger } from './isInteger.js' + export function createPath(path, delimiter = '.'){ - return typeof path === 'string' ? path.split(delimiter) : path.map(String) + return typeof path === 'string' ? + path.split(delimiter).map(x => isInteger(x) ? Number(x) : x) : + path } diff --git a/source/_internals/includes.js b/source/_internals/includes.js new file mode 100644 index 00000000..01b45b0a --- /dev/null +++ b/source/_internals/includes.js @@ -0,0 +1,12 @@ +import { compare } from './compare.js' + +export function includes(a, list){ + let index = -1 + const { length } = list + + while (++index < length) + if (compare(list[ index ], a)) + return true + + return false +} diff --git a/source/_internals/isInteger.js b/source/_internals/isInteger.js index bcee10a7..b478ae98 100644 --- a/source/_internals/isInteger.js +++ b/source/_internals/isInteger.js @@ -3,3 +3,8 @@ function _isInteger(n){ } export const isInteger = Number.isInteger || _isInteger + +/** + * Check if `index` is integer even if it is a string. + */ +export const isIndexInteger = index => Number.isInteger(Number(index)) diff --git a/source/applyDiff.js b/source/applyDiff.js index 384bcc4b..f7d5cd9b 100644 --- a/source/applyDiff.js +++ b/source/applyDiff.js @@ -1,5 +1,5 @@ import { createPath } from './_internals/createPath.js' -import { assocPath } from './assocPath.js' +import { assocPathFn } from './assocPath.js' import { path as pathModule } from './path.js' const ALLOWED_OPERATIONS = [ 'remove', 'add', 'update' ] @@ -13,23 +13,23 @@ export function removeAtPath(path, obj){ if (len === 3) return delete obj[ p[ 0 ] ][ p[ 1 ] ][ p[ 2 ] ] if (len === 4) return delete obj[ p[ 0 ] ][ p[ 1 ] ][ p[ 2 ] ][ p[ 3 ] ] if (len === 5) return delete obj[ p[ 0 ] ][ p[ 1 ] ][ p[ 2 ] ][ p[ 3 ] ][ p[ 4 ] ] - if (len === 6){ + if (len === 6) return delete obj[ p[ 0 ] ][ p[ 1 ] ][ p[ 2 ] ][ p[ 3 ] ][ p[ 4 ] ][ p[ 5 ] ] - } - if (len === 7){ + + if (len === 7) return delete obj[ p[ 0 ] ][ p[ 1 ] ][ p[ 2 ] ][ p[ 3 ] ][ p[ 4 ] ][ p[ 5 ] ][ p[ 6 ] ] - } - if (len === 8){ + + if (len === 8) return delete obj[ p[ 0 ] ][ p[ 1 ] ][ p[ 2 ] ][ p[ 3 ] ][ p[ 4 ] ][ p[ 5 ] ][ p[ 6 ] ][ p[ 7 ] ] - } - if (len === 9){ + + if (len === 9) return delete obj[ p[ 0 ] ][ p[ 1 ] ][ p[ 2 ] ][ p[ 3 ] ][ p[ 4 ] ][ p[ 5 ] ][ p[ 6 ] ][ p[ 7 ] ][ p[ 8 ] ] - } - if (len === 10){ + + if (len === 10) return delete obj[ p[ 0 ] ][ p[ 1 ] ][ p[ 2 ] ][ p[ 3 ] ][ p[ 4 ] ][ p[ 5 ] ][ p[ 6 ] ][ p[ 7 ] ][ p[ 8 ] ][ p[ 9 ] ] - } + } export function applyDiff(rules, obj){ @@ -42,22 +42,27 @@ export function applyDiff(rules, obj){ if (op === 'add' && path && value !== undefined){ if (pathModule(path, obj)) return - return clone = assocPath( + clone = assocPathFn( path, value, clone ) + + return } if (op === 'remove'){ if (pathModule(path, obj) === undefined) return - return removeAtPath(path, clone) + removeAtPath(path, clone) + + return } if (op === 'update' && path && value !== undefined){ if (pathModule(path, obj) === undefined) return - return clone = assocPath( + clone = assocPathFn( path, value, clone ) + } }) diff --git a/source/applyDiff.spec.js b/source/applyDiff.spec.js index 6666354d..7ce85e33 100644 --- a/source/applyDiff.spec.js +++ b/source/applyDiff.spec.js @@ -34,13 +34,12 @@ test('update operation', () => { value : 3, }, ] - const result = applyDiff(rules, { + expect(applyDiff(rules, { a : { b : 1, c : [ 1, 2 ], }, - }) - expect(result).toEqual({ + })).toEqual({ a : { b : 3, c : [ 1, 3 ], diff --git a/source/assoc.js b/source/assoc.js index ec095dcb..781434b8 100644 --- a/source/assoc.js +++ b/source/assoc.js @@ -1,6 +1,6 @@ import { curry } from './curry.js' -function assocFn( +export function assocFn( prop, newValue, obj ){ return Object.assign( diff --git a/source/assocPath.js b/source/assocPath.js index f6fd71e9..0ed62994 100644 --- a/source/assocPath.js +++ b/source/assocPath.js @@ -1,11 +1,11 @@ -import { createPath } from '../src/_internals/createPath.js' import { cloneList } from './_internals/cloneList.js' +import { createPath } from './_internals/createPath.js' import { isArray } from './_internals/isArray.js' -import { isInteger } from './_internals/isInteger.js' -import { assoc } from './assoc.js' +import { isIndexInteger } from './_internals/isInteger.js' +import { assocFn } from './assoc.js' import { curry } from './curry.js' -function assocPathFn( +export function assocPathFn( path, newValue, input ){ const pathArrValue = createPath(path) @@ -19,7 +19,7 @@ function assocPathFn( !input.hasOwnProperty(index) const nextInput = condition ? - isInteger(pathArrValue[ 1 ]) ? + isIndexInteger(pathArrValue[ 1 ]) ? [] : {} : input[ index ] @@ -31,14 +31,14 @@ function assocPathFn( ) } - if (isInteger(index) && isArray(input)){ + if (isIndexInteger(index) && isArray(input)){ const arr = cloneList(input) arr[ index ] = newValue return arr } - return assoc( + return assocFn( index, newValue, input ) } diff --git a/source/assocPath.spec.js b/source/assocPath.spec.js index 647bfdf8..6adecc67 100644 --- a/source/assocPath.spec.js +++ b/source/assocPath.spec.js @@ -1,18 +1,42 @@ -import { assocPath } from './assocPath.js' +import { assocPathFn } from './assocPath.js' + +test.only('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 result = assocPath( + 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(result).toEqual(expected) + expect(result1).toEqual(expected) + expect(result2).toEqual(expected) }) test('difference with ramda - doesn\'t overwrite primitive values with keys in the path', () => { diff --git a/source/dissocPath.js b/source/dissocPath.js index 80adb2aa..949c5a89 100644 --- a/source/dissocPath.js +++ b/source/dissocPath.js @@ -1,6 +1,6 @@ import { createPath } from '../src/_internals/createPath.js' import { isArray } from './_internals/isArray.js' -import { isInteger } from './_internals/isInteger.js' +import { isIndexInteger } from './_internals/isInteger.js' import { omit } from './omit.js' import { path } from './path.js' import { removeIndex } from './removeIndex.js' @@ -24,7 +24,7 @@ export function dissocPath(pathInput, input){ !input.hasOwnProperty(index) if (pathArrValue.length > 1){ const nextInput = condition ? - isInteger(pathArrValue[ 1 ]) ? + isIndexInteger(pathArrValue[ 1 ]) ? [] : {} : input[ index ] diff --git a/source/omit.js b/source/omit.js index ce64d60a..cc15772a 100644 --- a/source/omit.js +++ b/source/omit.js @@ -1,20 +1,18 @@ import { createPath } from './_internals/createPath.js' +import { includes } from './_internals/includes.js' 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 propsToOmitValue = createPath(propsToOmit, ',') const willReturn = {} - for (const key in obj){ - if (!propsToOmitValue.includes(key)){ + for (const key in obj) + if (!includes(key, propsToOmitValue)) willReturn[ key ] = obj[ key ] - } - } return willReturn } diff --git a/source/omit.spec.js b/source/omit.spec.js index 8fab8e5c..8d03d43a 100644 --- a/source/omit.spec.js +++ b/source/omit.spec.js @@ -14,7 +14,7 @@ test('with string as condition', () => { expect(resultCurry).toEqual(expectedResult) }) -test('with number as property to omit', () => { +test.only('with number as property to omit', () => { const obj = { 1 : 1, b : 2, diff --git a/source/pick.spec.js b/source/pick.spec.js index bb185a42..ca97d4a3 100644 --- a/source/pick.spec.js +++ b/source/pick.spec.js @@ -70,5 +70,9 @@ test('works with list as input and number as props - props to pick is a string', test('with symbol', () => { const symbolProp = Symbol('s') - expect(pick([ symbolProp ], { [ symbolProp ] : 'a' })).toMatchInlineSnapshot('{}') + expect(pick([ symbolProp ], { [ symbolProp ] : 'a' })).toMatchInlineSnapshot(` +{ + Symbol(s): "a", +} +`) }) diff --git a/src/_internals/compare.js b/src/_internals/compare.js new file mode 100644 index 00000000..a2952de4 --- /dev/null +++ b/src/_internals/compare.js @@ -0,0 +1,3 @@ +export function compare(a, b){ + return String(a) === String(b) +} diff --git a/src/_internals/createPath.js b/src/_internals/createPath.js index 278e80e1..3848fbce 100644 --- a/src/_internals/createPath.js +++ b/src/_internals/createPath.js @@ -1,3 +1,7 @@ +import { isInteger } from './isInteger.js' + export function createPath(path, delimiter = '.'){ - return typeof path === 'string' ? path.split(delimiter) : path.map(String) + return typeof path === 'string' ? + path.split(delimiter).map(x => isInteger(x) ? Number(x) : x) : + path } diff --git a/src/_internals/includes.js b/src/_internals/includes.js new file mode 100644 index 00000000..01b45b0a --- /dev/null +++ b/src/_internals/includes.js @@ -0,0 +1,12 @@ +import { compare } from './compare.js' + +export function includes(a, list){ + let index = -1 + const { length } = list + + while (++index < length) + if (compare(list[ index ], a)) + return true + + return false +} diff --git a/src/_internals/isInteger.js b/src/_internals/isInteger.js index bcee10a7..b478ae98 100644 --- a/src/_internals/isInteger.js +++ b/src/_internals/isInteger.js @@ -3,3 +3,8 @@ function _isInteger(n){ } export const isInteger = Number.isInteger || _isInteger + +/** + * Check if `index` is integer even if it is a string. + */ +export const isIndexInteger = index => Number.isInteger(Number(index)) diff --git a/src/assoc.js b/src/assoc.js index ec095dcb..781434b8 100644 --- a/src/assoc.js +++ b/src/assoc.js @@ -1,6 +1,6 @@ import { curry } from './curry.js' -function assocFn( +export function assocFn( prop, newValue, obj ){ return Object.assign( diff --git a/src/assocPath.js b/src/assocPath.js index f6fd71e9..0ed62994 100644 --- a/src/assocPath.js +++ b/src/assocPath.js @@ -1,11 +1,11 @@ -import { createPath } from '../src/_internals/createPath.js' import { cloneList } from './_internals/cloneList.js' +import { createPath } from './_internals/createPath.js' import { isArray } from './_internals/isArray.js' -import { isInteger } from './_internals/isInteger.js' -import { assoc } from './assoc.js' +import { isIndexInteger } from './_internals/isInteger.js' +import { assocFn } from './assoc.js' import { curry } from './curry.js' -function assocPathFn( +export function assocPathFn( path, newValue, input ){ const pathArrValue = createPath(path) @@ -19,7 +19,7 @@ function assocPathFn( !input.hasOwnProperty(index) const nextInput = condition ? - isInteger(pathArrValue[ 1 ]) ? + isIndexInteger(pathArrValue[ 1 ]) ? [] : {} : input[ index ] @@ -31,14 +31,14 @@ function assocPathFn( ) } - if (isInteger(index) && isArray(input)){ + if (isIndexInteger(index) && isArray(input)){ const arr = cloneList(input) arr[ index ] = newValue return arr } - return assoc( + return assocFn( index, newValue, input ) } diff --git a/src/dissocPath.js b/src/dissocPath.js index 80adb2aa..949c5a89 100644 --- a/src/dissocPath.js +++ b/src/dissocPath.js @@ -1,6 +1,6 @@ import { createPath } from '../src/_internals/createPath.js' import { isArray } from './_internals/isArray.js' -import { isInteger } from './_internals/isInteger.js' +import { isIndexInteger } from './_internals/isInteger.js' import { omit } from './omit.js' import { path } from './path.js' import { removeIndex } from './removeIndex.js' @@ -24,7 +24,7 @@ export function dissocPath(pathInput, input){ !input.hasOwnProperty(index) if (pathArrValue.length > 1){ const nextInput = condition ? - isInteger(pathArrValue[ 1 ]) ? + isIndexInteger(pathArrValue[ 1 ]) ? [] : {} : input[ index ] diff --git a/src/omit.js b/src/omit.js index ce64d60a..cc15772a 100644 --- a/src/omit.js +++ b/src/omit.js @@ -1,20 +1,18 @@ import { createPath } from './_internals/createPath.js' +import { includes } from './_internals/includes.js' 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 propsToOmitValue = createPath(propsToOmit, ',') const willReturn = {} - for (const key in obj){ - if (!propsToOmitValue.includes(key)){ + for (const key in obj) + if (!includes(key, propsToOmitValue)) willReturn[ key ] = obj[ key ] - } - } return willReturn }