rambda 10.0.0-beta.1 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -101,14 +101,6 @@ Because of the focus on `R.pipe`, there is only one way to use each method. This
101
101
  - All methods that 2 inputs, will have to be called with `R.methodName(input1)(input2)`
102
102
  - All methods that 3 inputs, will have to be called with `R.methodName(input1, input2)(input3)`
103
103
 
104
- ### Immutable TS definitions
105
-
106
- You can use immutable version of Rambda definitions, which is linted with ESLint `functional/prefer-readonly-type` plugin.
107
-
108
- ```
109
- import {filter} from 'rambda/immutable'
110
- ```
111
-
112
104
  ### Deno support
113
105
 
114
106
  ```
@@ -258,6 +250,134 @@ it('R.addProp', () => {
258
250
 
259
251
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#addProp)
260
252
 
253
+ ### addPropToObjects
254
+
255
+ ```typescript
256
+
257
+ addPropToObjects<
258
+ T extends object,
259
+ K extends string,
260
+ R
261
+ >(
262
+ property: K,
263
+ fn: (input: T) => R
264
+ ): (list: T[]) => MergeTypes<T & { [P in K]: R }>[]
265
+ ```
266
+
267
+ It receives list of objects and add new property to each item.
268
+
269
+ The value is based on result of `fn` function, which receives the current object as argument.
270
+
271
+ ```javascript
272
+ const result = R.pipe(
273
+ [
274
+ {a: 1, b: 2},
275
+ {a: 3, b: 4},
276
+ ],
277
+ R.addPropToObjects(
278
+ 'c',
279
+ (x) => String(x.a + x.b),
280
+ )
281
+ )
282
+ // => [{a: 1, b: 2, c: '3'}, {a: 3, b: 4, c: '7'}]
283
+ ```
284
+
285
+ <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.pipe(%0A%09%5B%0A%09%09%7Ba%3A%201%2C%20b%3A%202%7D%2C%0A%09%09%7Ba%3A%203%2C%20b%3A%204%7D%2C%0A%09%5D%2C%0A%09R.addPropToObjects(%0A%09%09'c'%2C%0A%09%09(x)%20%3D%3E%20String(x.a%20%2B%20x.b)%2C%0A%09)%0A)%0A%2F%2F%20%3D%3E%20%5B%7Ba%3A%201%2C%20b%3A%202%2C%20c%3A%20'3'%7D%2C%20%7Ba%3A%203%2C%20b%3A%204%2C%20c%3A%20'7'%7D%5D">Try this <strong>R.addPropToObjects</strong> example in Rambda REPL</a>
286
+
287
+ <details>
288
+
289
+ <summary>All TypeScript definitions</summary>
290
+
291
+ ```typescript
292
+ addPropToObjects<
293
+ T extends object,
294
+ K extends string,
295
+ R
296
+ >(
297
+ property: K,
298
+ fn: (input: T) => R
299
+ ): (list: T[]) => MergeTypes<T & { [P in K]: R }>[];
300
+ ```
301
+
302
+ </details>
303
+
304
+ <details>
305
+
306
+ <summary><strong>R.addPropToObjects</strong> source</summary>
307
+
308
+ ```javascript
309
+ import { mapFn } from './map.js'
310
+
311
+ export function addPropToObjects (
312
+ property,
313
+ fn
314
+ ){
315
+ return listOfObjects => mapFn(
316
+ (obj) => ({
317
+ ...(obj),
318
+ [property]: fn(obj)
319
+ }),
320
+ listOfObjects
321
+ )
322
+ }
323
+ ```
324
+
325
+ </details>
326
+
327
+ <details>
328
+
329
+ <summary><strong>Tests</strong></summary>
330
+
331
+ ```javascript
332
+ import { pipe } from "./pipe.js"
333
+ import { addPropToObjects } from "./addPropToObjects.js"
334
+
335
+ test('R.addPropToObjects', () => {
336
+ let result = pipe(
337
+ [
338
+ {a: 1, b: 2},
339
+ {a: 3, b: 4},
340
+ ],
341
+ addPropToObjects(
342
+ 'c',
343
+ (x) => String(x.a + x.b),
344
+ )
345
+ )
346
+ expect(result).toEqual([
347
+ { a: 1, b: 2, c: '3' },
348
+ { a: 3, b: 4, c: '7' },
349
+ ])
350
+ })
351
+ ```
352
+
353
+ </details>
354
+
355
+ <details>
356
+
357
+ <summary><strong>TypeScript</strong> test</summary>
358
+
359
+ ```typescript
360
+ import { addPropToObjects, pipe } from 'rambda'
361
+
362
+ it('R.addPropToObjects', () => {
363
+ let result = pipe(
364
+ [
365
+ {a: 1, b: 2},
366
+ {a: 3, b: 4},
367
+ ],
368
+ addPropToObjects(
369
+ 'c',
370
+ (x) => String(x.a + x.b),
371
+ )
372
+ )
373
+ result // $ExpectType { a: number; b: number; c: string; }[]
374
+ })
375
+ ```
376
+
377
+ </details>
378
+
379
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#addPropToObjects)
380
+
261
381
  ### all
262
382
 
263
383
  ```typescript
@@ -1610,12 +1730,8 @@ function isFalsy(input) {
1610
1730
  return input === undefined || input === null || Number.isNaN(input) === true
1611
1731
  }
1612
1732
 
1613
- export function defaultTo(defaultArgument, input) {
1614
- if (arguments.length === 1) {
1615
- return _input => defaultTo(defaultArgument, _input)
1616
- }
1617
-
1618
- return isFalsy(input) ? defaultArgument : input
1733
+ export function defaultTo(defaultArgument) {
1734
+ return input => isFalsy(input) ? defaultArgument : input
1619
1735
  }
1620
1736
  ```
1621
1737
 
@@ -1641,15 +1757,15 @@ test('with NaN', () => {
1641
1757
  })
1642
1758
 
1643
1759
  test('with empty string', () => {
1644
- expect(defaultTo('foo', '')).toBe('')
1760
+ expect(defaultTo('foo')('')).toBe('')
1645
1761
  })
1646
1762
 
1647
1763
  test('with false', () => {
1648
- expect(defaultTo('foo', false)).toBeFalsy()
1764
+ expect(defaultTo('foo')(false)).toBeFalsy()
1649
1765
  })
1650
1766
 
1651
1767
  test('when inputArgument passes initial check', () => {
1652
- expect(defaultTo('foo', 'bar')).toBe('bar')
1768
+ expect(defaultTo('foo')('bar')).toBe('bar')
1653
1769
  })
1654
1770
  ```
1655
1771
 
@@ -1755,12 +1871,8 @@ drop<T>(howMany: number): (list: T[]) => T[];
1755
1871
  <summary><strong>R.drop</strong> source</summary>
1756
1872
 
1757
1873
  ```javascript
1758
- export function drop(howManyToDrop, listOrString) {
1759
- if (arguments.length === 1) {
1760
- return _list => drop(howManyToDrop, _list)
1761
- }
1762
-
1763
- return listOrString.slice(howManyToDrop > 0 ? howManyToDrop : 0)
1874
+ export function drop(howManyToDrop, ) {
1875
+ return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
1764
1876
  }
1765
1877
  ```
1766
1878
 
@@ -1771,31 +1883,18 @@ export function drop(howManyToDrop, listOrString) {
1771
1883
  <summary><strong>Tests</strong></summary>
1772
1884
 
1773
1885
  ```javascript
1774
- import assert from 'node:assert'
1775
-
1776
1886
  import { drop } from './drop.js'
1777
1887
 
1778
1888
  test('with array', () => {
1779
1889
  expect(drop(2)(['foo', 'bar', 'baz'])).toEqual(['baz'])
1780
- expect(drop(3, ['foo', 'bar', 'baz'])).toEqual([])
1781
- expect(drop(4, ['foo', 'bar', 'baz'])).toEqual([])
1782
- })
1783
-
1784
- test('with string', () => {
1785
- expect(drop(3, 'rambda')).toBe('bda')
1890
+ expect(drop(3)(['foo', 'bar', 'baz'])).toEqual([])
1891
+ expect(drop(4)(['foo', 'bar', 'baz'])).toEqual([])
1786
1892
  })
1787
1893
 
1788
1894
  test('with non-positive count', () => {
1789
- expect(drop(0, [1, 2, 3])).toEqual([1, 2, 3])
1790
- expect(drop(-1, [1, 2, 3])).toEqual([1, 2, 3])
1791
- expect(drop(Number.NEGATIVE_INFINITY, [1, 2, 3])).toEqual([1, 2, 3])
1792
- })
1793
-
1794
- test('should return copy', () => {
1795
- const xs = [1, 2, 3]
1796
-
1797
- assert.notStrictEqual(drop(0, xs), xs)
1798
- assert.notStrictEqual(drop(-1, xs), xs)
1895
+ expect(drop(0)([1, 2, 3])).toEqual([1, 2, 3])
1896
+ expect(drop(-1)([1, 2, 3])).toEqual([1, 2, 3])
1897
+ expect(drop(Number.NEGATIVE_INFINITY)([1, 2, 3])).toEqual([1, 2, 3])
1799
1898
  })
1800
1899
  ```
1801
1900
 
@@ -3907,6 +4006,229 @@ describe('flatten', () => {
3907
4006
 
3908
4007
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#flatten)
3909
4008
 
4009
+ ### flattenObject
4010
+
4011
+ ```typescript
4012
+
4013
+ flattenObject<T extends object>(obj: T): FlattenObject<T>
4014
+ ```
4015
+
4016
+ It transforms object to object where each value is represented with its path.
4017
+
4018
+ ```javascript
4019
+ const result = R.flattenObject(
4020
+ [1, 2, 3]
4021
+ )
4022
+ // => [3, 1, 2] or [2, 3, 1] or ...
4023
+ ```
4024
+
4025
+ <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.flattenObject(%0A%09%5B1%2C%202%2C%203%5D%0A)%0A%2F%2F%20%3D%3E%20%5B3%2C%201%2C%202%5D%20or%20%5B2%2C%203%2C%201%5D%20or%20...">Try this <strong>R.flattenObject</strong> example in Rambda REPL</a>
4026
+
4027
+ <details>
4028
+
4029
+ <summary>All TypeScript definitions</summary>
4030
+
4031
+ ```typescript
4032
+ flattenObject<T extends object>(obj: T): FlattenObject<T>;
4033
+ ```
4034
+
4035
+ </details>
4036
+
4037
+ <details>
4038
+
4039
+ <summary><strong>R.flattenObject</strong> source</summary>
4040
+
4041
+ ```javascript
4042
+ import { type } from './type.js'
4043
+
4044
+ export function flattenObjectHelper(obj, accumulator = []){
4045
+ const willReturn = {}
4046
+ Object.keys(obj).forEach(key => {
4047
+ const typeIs = type(obj[ key ])
4048
+ if (typeIs === 'Object'){
4049
+ const [ flatResultValue, flatResultPath ] = flattenObjectHelper(obj[ key ],
4050
+ [ ...accumulator, key ])
4051
+ willReturn[ flatResultPath.join('.') ] = flatResultValue
4052
+
4053
+ return
4054
+ } else if (accumulator.length > 0){
4055
+ const finalKey = [ ...accumulator, key ].join('.')
4056
+ willReturn[ finalKey ] = obj[ key ]
4057
+
4058
+ return
4059
+ }
4060
+ willReturn[ key ] = obj[ key ]
4061
+ })
4062
+ if (accumulator.length > 0) return [ willReturn, accumulator ]
4063
+
4064
+ return willReturn
4065
+ }
4066
+
4067
+ export function transformFlatObject(obj){
4068
+ const willReturn = {}
4069
+
4070
+ const transformFlatObjectFn = objLocal => {
4071
+ const willReturnLocal = {}
4072
+ Object.keys(objLocal).forEach(key => {
4073
+ const typeIs = type(objLocal[ key ])
4074
+ if (typeIs === 'Object'){
4075
+ transformFlatObjectFn(objLocal[ key ])
4076
+
4077
+ return
4078
+ }
4079
+ willReturnLocal[ key ] = objLocal[ key ]
4080
+ willReturn[ key ] = objLocal[ key ]
4081
+ })
4082
+
4083
+ return willReturnLocal
4084
+ }
4085
+
4086
+ Object.keys(obj).forEach(key => {
4087
+ const typeIs = type(obj[ key ])
4088
+ if (typeIs === 'Object'){
4089
+ transformFlatObjectFn(obj[ key ], key)
4090
+
4091
+ return
4092
+ }
4093
+ willReturn[ key ] = obj[ key ]
4094
+ })
4095
+
4096
+ return willReturn
4097
+ }
4098
+
4099
+ export function flattenObject(obj){
4100
+ const willReturn = {}
4101
+
4102
+ Object.keys(obj).forEach(key => {
4103
+ const typeIs = type(obj[ key ])
4104
+ if (typeIs === 'Object'){
4105
+ const flatObject = flattenObjectHelper(obj[ key ])
4106
+ const transformed = transformFlatObject(flatObject)
4107
+
4108
+ Object.keys(transformed).forEach(keyTransformed => {
4109
+ willReturn[ `${ key }.${ keyTransformed }` ] = transformed[ keyTransformed ]
4110
+ })
4111
+ } else {
4112
+ willReturn[ key ] = obj[ key ]
4113
+ }
4114
+ })
4115
+
4116
+ return willReturn
4117
+ }
4118
+ ```
4119
+
4120
+ </details>
4121
+
4122
+ <details>
4123
+
4124
+ <summary><strong>Tests</strong></summary>
4125
+
4126
+ ```javascript
4127
+ import {
4128
+ flattenObject,
4129
+ flattenObjectHelper,
4130
+ transformFlatObject,
4131
+ } from './flattenObject.js'
4132
+
4133
+ test('happy', () => {
4134
+ const obj = {
4135
+ c : 3,
4136
+ d : {
4137
+ 'd.e' : [ 5, 6, 7 ],
4138
+ 'd.z' : 4,
4139
+ 'd.f' : { 'd.f.h' : 6 },
4140
+ },
4141
+ }
4142
+ const result = transformFlatObject(obj)
4143
+ expect(result).toEqual({
4144
+ 'c' : 3,
4145
+ 'd.e' : [ 5, 6, 7 ],
4146
+ 'd.z' : 4,
4147
+ 'd.f.h' : 6,
4148
+ })
4149
+ })
4150
+
4151
+ test('happy', () => {
4152
+ const result = flattenObject({
4153
+ a : 1,
4154
+ b : {
4155
+ c : 3,
4156
+ d : {
4157
+ e : 5,
4158
+ z : 4,
4159
+ f : {
4160
+ h : 6,
4161
+ i : 7,
4162
+ j : {
4163
+ k : 8,
4164
+ l : 9,
4165
+ },
4166
+ },
4167
+ },
4168
+ },
4169
+ })
4170
+ const expected = {
4171
+ 'a' : 1,
4172
+ 'b.c' : 3,
4173
+ 'b.d.e' : 5,
4174
+ 'b.d.z' : 4,
4175
+ 'b.d.f.h' : 6,
4176
+ 'b.d.f.i' : 7,
4177
+ 'b.d.f.j.k' : 8,
4178
+ 'b.d.f.j.l' : 9,
4179
+ }
4180
+ expect(result).toEqual(expected)
4181
+ })
4182
+
4183
+ test('flattenObjectHelper', () => {
4184
+ const result = flattenObjectHelper({
4185
+ a : 1,
4186
+ b : {
4187
+ c : 3,
4188
+ d : {
4189
+ e : 5,
4190
+ z : 4,
4191
+ f : { h : 6 },
4192
+ },
4193
+ },
4194
+ })
4195
+ const expected = {
4196
+ a : 1,
4197
+ b : {
4198
+ 'b.c' : 3,
4199
+ 'b.d' : {
4200
+ 'b.d.e' : 5,
4201
+ 'b.d.z' : 4,
4202
+ 'b.d.f' : { 'b.d.f.h' : 6 },
4203
+ },
4204
+ },
4205
+ }
4206
+ expect(result).toEqual(expected)
4207
+ })
4208
+ ```
4209
+
4210
+ </details>
4211
+
4212
+ <details>
4213
+
4214
+ <summary><strong>TypeScript</strong> test</summary>
4215
+
4216
+ ```typescript
4217
+ import { flattenObject, pipe } from 'rambda'
4218
+
4219
+ it('R.flattenObject', () => {
4220
+ const result = pipe({ a: { b: 1, c: 2 } }, flattenObject)
4221
+ result['a.b'] // $ExpectType number
4222
+ result['a.c'] // $ExpectType number
4223
+ // @ts-expect-error
4224
+ result['a.foo']
4225
+ })
4226
+ ```
4227
+
4228
+ </details>
4229
+
4230
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#flattenObject)
4231
+
3910
4232
  ### groupBy
3911
4233
 
3912
4234
  ```typescript
@@ -4623,9 +4945,7 @@ const expected = 'foo is BAR even 1 more'
4623
4945
  interpolate(inputWithTags: string): (templateArguments: object) => string;
4624
4946
 
4625
4947
  // API_MARKER_END
4626
- // ============================================
4627
-
4628
- export as namespace R
4948
+ // ===========================================
4629
4949
  ```
4630
4950
 
4631
4951
  </details>
@@ -5196,16 +5516,20 @@ map<T extends IterableContainer, U>(
5196
5516
  <summary><strong>R.map</strong> source</summary>
5197
5517
 
5198
5518
  ```javascript
5519
+ export function mapFn(
5520
+ fn, list
5521
+ ){
5522
+ let index = 0
5523
+ const willReturn = Array(list.length)
5524
+ while (index < list.length) {
5525
+ willReturn[index] = fn(list[index], index)
5526
+ index++
5527
+ }
5528
+ return willReturn
5529
+ }
5530
+
5199
5531
  export function map(fn) {
5200
- return list => {
5201
- let index = 0
5202
- const willReturn = Array(list.length)
5203
- while (index < list.length) {
5204
- willReturn[index] = fn(list[index], index)
5205
- index++
5206
- }
5207
- return willReturn
5208
- }
5532
+ return list => mapFn(fn, list)
5209
5533
  }
5210
5534
  ```
5211
5535
 
@@ -6153,66 +6477,53 @@ test('happy', () => {
6153
6477
 
6154
6478
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#minBy)
6155
6479
 
6156
- ### modifyProp
6480
+ ### modifyItemAtIndex
6157
6481
 
6158
6482
  ```typescript
6159
6483
 
6160
- modifyProp<T, K extends keyof T>(
6161
- prop: K,
6162
- fn: (x: T[K]) => T[K],
6163
- ): (target: T) => T
6484
+ modifyItemAtIndex<T>(index: number, replaceFn: (x: T) => T): (list: T[]) => T[]
6164
6485
  ```
6165
6486
 
6166
- It changes a property with the result of transformer function.
6487
+ It replaces `index` in array `list` with the result of `replaceFn(list[i])`.
6167
6488
 
6168
6489
  ```javascript
6169
- const person = {
6170
- name : 'foo',
6171
- age : 20,
6172
- }
6173
- const result = R.modifyProp('age', x => x + 1)(person)
6174
- // => {name: 'foo', age: 21}
6490
+ const result = R.pipe(
6491
+ [1, 2, 3],
6492
+ R.modifyItemAtIndex(1, R.add(1))
6493
+ ) // => [1, 3, 3]
6175
6494
  ```
6176
6495
 
6177
- <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20person%20%3D%20%7B%0A%20%20name%20%3A%20'foo'%2C%0A%20%20age%20%20%3A%2020%2C%0A%7D%0Aconst%20result%20%3D%20R.modifyProp('age'%2C%20x%20%3D%3E%20x%20%2B%201)(person)%20%0A%2F%2F%20%3D%3E%20%7Bname%3A%20'foo'%2C%20age%3A%2021%7D">Try this <strong>R.modifyProp</strong> example in Rambda REPL</a>
6496
+ <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.pipe(%0A%09%5B1%2C%202%2C%203%5D%2C%0A%09R.modifyItemAtIndex(1%2C%20R.add(1))%0A)%20%2F%2F%20%3D%3E%20%5B1%2C%203%2C%203%5D">Try this <strong>R.modifyItemAtIndex</strong> example in Rambda REPL</a>
6178
6497
 
6179
6498
  <details>
6180
6499
 
6181
6500
  <summary>All TypeScript definitions</summary>
6182
6501
 
6183
6502
  ```typescript
6184
- modifyProp<T, K extends keyof T>(
6185
- prop: K,
6186
- fn: (x: T[K]) => T[K],
6187
- ): (target: T) => T;
6503
+ modifyItemAtIndex<T>(index: number, replaceFn: (x: T) => T): (list: T[]) => T[];
6188
6504
  ```
6189
6505
 
6190
6506
  </details>
6191
6507
 
6192
6508
  <details>
6193
6509
 
6194
- <summary><strong>R.modifyProp</strong> source</summary>
6510
+ <summary><strong>R.modifyItemAtIndex</strong> source</summary>
6195
6511
 
6196
6512
  ```javascript
6197
- import { isArray } from './_internals/isArray.js'
6198
- import { update } from './update.js'
6513
+ import { cloneList } from './_internals/cloneList.js'
6199
6514
 
6200
- function modifyFn(property, fn, list) {
6201
- if (list[property] === undefined) {
6202
- return list
6203
- }
6204
- if (isArray(list)) {
6205
- return update(property, fn(list[property]))(list)
6206
- }
6515
+ export function modifyItemAtIndex(index, replaceFn) {
6516
+ return list => {
6517
+ const actualIndex = index < 0 ? list.length + index : index
6518
+ if (index >= list.length || actualIndex < 0) {
6519
+ return list
6520
+ }
6207
6521
 
6208
- return {
6209
- ...list,
6210
- [property]: fn(list[property]),
6211
- }
6212
- }
6522
+ const clone = cloneList(list)
6523
+ clone[actualIndex] = replaceFn(clone[actualIndex])
6213
6524
 
6214
- export function modifyProp(property, fn) {
6215
- return obj => modifyFn(property, fn, obj)
6525
+ return clone
6526
+ }
6216
6527
  }
6217
6528
  ```
6218
6529
 
@@ -6223,16 +6534,112 @@ export function modifyProp(property, fn) {
6223
6534
  <summary><strong>Tests</strong></summary>
6224
6535
 
6225
6536
  ```javascript
6226
- import { modifyProp } from './modifyProp.js'
6537
+ import { modifyItemAtIndex } from './modifyItemAtIndex.js'
6227
6538
 
6228
- const person = {
6229
- name: 'foo',
6230
- age: 20,
6231
- }
6539
+ const add10 = x => x + 10
6540
+
6541
+ const list = [0, 1, 2]
6542
+ const expected = [0, 11, 2]
6232
6543
 
6233
6544
  test('happy', () => {
6234
- expect(modifyProp('age', x => x + 1)(person)).toEqual({
6235
- name: 'foo',
6545
+ expect(modifyItemAtIndex(1, add10)(list)).toEqual(expected)
6546
+ })
6547
+
6548
+ test('with negative index', () => {
6549
+ expect(modifyItemAtIndex(-2, add10)(list)).toEqual(expected)
6550
+ })
6551
+
6552
+ test('when index is out of bounds', () => {
6553
+ const list = [0, 1, 2, 3]
6554
+ expect(modifyItemAtIndex(4, add10)(list)).toEqual(list)
6555
+ expect(modifyItemAtIndex(-5, add10)(list)).toEqual(list)
6556
+ })
6557
+ ```
6558
+
6559
+ </details>
6560
+
6561
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#modifyItemAtIndex)
6562
+
6563
+ ### modifyProp
6564
+
6565
+ ```typescript
6566
+
6567
+ modifyProp<T, K extends keyof T>(
6568
+ prop: K,
6569
+ fn: (x: T[K]) => T[K],
6570
+ ): (target: T) => T
6571
+ ```
6572
+
6573
+ It changes a property with the result of transformer function.
6574
+
6575
+ ```javascript
6576
+ const person = {
6577
+ name : 'foo',
6578
+ age : 20,
6579
+ }
6580
+ const result = R.modifyProp('age', x => x + 1)(person)
6581
+ // => {name: 'foo', age: 21}
6582
+ ```
6583
+
6584
+ <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20person%20%3D%20%7B%0A%20%20name%20%3A%20'foo'%2C%0A%20%20age%20%20%3A%2020%2C%0A%7D%0Aconst%20result%20%3D%20R.modifyProp('age'%2C%20x%20%3D%3E%20x%20%2B%201)(person)%20%0A%2F%2F%20%3D%3E%20%7Bname%3A%20'foo'%2C%20age%3A%2021%7D">Try this <strong>R.modifyProp</strong> example in Rambda REPL</a>
6585
+
6586
+ <details>
6587
+
6588
+ <summary>All TypeScript definitions</summary>
6589
+
6590
+ ```typescript
6591
+ modifyProp<T, K extends keyof T>(
6592
+ prop: K,
6593
+ fn: (x: T[K]) => T[K],
6594
+ ): (target: T) => T;
6595
+ ```
6596
+
6597
+ </details>
6598
+
6599
+ <details>
6600
+
6601
+ <summary><strong>R.modifyProp</strong> source</summary>
6602
+
6603
+ ```javascript
6604
+ import { isArray } from './_internals/isArray.js'
6605
+ import { update } from './update.js'
6606
+
6607
+ function modifyFn(property, fn, list) {
6608
+ if (list[property] === undefined) {
6609
+ return list
6610
+ }
6611
+ if (isArray(list)) {
6612
+ return update(property, fn(list[property]))(list)
6613
+ }
6614
+
6615
+ return {
6616
+ ...list,
6617
+ [property]: fn(list[property]),
6618
+ }
6619
+ }
6620
+
6621
+ export function modifyProp(property, fn) {
6622
+ return obj => modifyFn(property, fn, obj)
6623
+ }
6624
+ ```
6625
+
6626
+ </details>
6627
+
6628
+ <details>
6629
+
6630
+ <summary><strong>Tests</strong></summary>
6631
+
6632
+ ```javascript
6633
+ import { modifyProp } from './modifyProp.js'
6634
+
6635
+ const person = {
6636
+ name: 'foo',
6637
+ age: 20,
6638
+ }
6639
+
6640
+ test('happy', () => {
6641
+ expect(modifyProp('age', x => x + 1)(person)).toEqual({
6642
+ name: 'foo',
6236
6643
  age: 21,
6237
6644
  })
6238
6645
  })
@@ -7048,32 +7455,30 @@ path<
7048
7455
  ```javascript
7049
7456
  import { createPath } from './_internals/createPath.js'
7050
7457
 
7051
- export function path(pathInput, obj) {
7052
- if (arguments.length === 1) {
7053
- return _obj => path(pathInput, _obj)
7054
- }
7055
-
7056
- if (!obj) {
7057
- return undefined
7058
- }
7059
- let willReturn = obj
7060
- let counter = 0
7061
-
7062
- const pathArrValue = createPath(pathInput)
7063
-
7064
- while (counter < pathArrValue.length) {
7065
- if (willReturn === null || willReturn === undefined) {
7066
- return undefined
7067
- }
7068
- if (willReturn[pathArrValue[counter]] === null) {
7069
- return undefined
7070
- }
7071
-
7072
- willReturn = willReturn[pathArrValue[counter]]
7073
- counter++
7074
- }
7075
-
7076
- return willReturn
7458
+ export function path(pathInput) {
7459
+ return (obj) => {
7460
+ if (!obj) {
7461
+ return undefined
7462
+ }
7463
+ let willReturn = obj
7464
+ let counter = 0
7465
+
7466
+ const pathArrValue = createPath(pathInput)
7467
+
7468
+ while (counter < pathArrValue.length) {
7469
+ if (willReturn === null || willReturn === undefined) {
7470
+ return undefined
7471
+ }
7472
+ if (willReturn[pathArrValue[counter]] === null) {
7473
+ return undefined
7474
+ }
7475
+
7476
+ willReturn = willReturn[pathArrValue[counter]]
7477
+ counter++
7478
+ }
7479
+
7480
+ return willReturn
7481
+ }
7077
7482
  }
7078
7483
  ```
7079
7484
 
@@ -7089,14 +7494,14 @@ import { path } from './path.js'
7089
7494
  test('with array inside object', () => {
7090
7495
  const obj = { a: { b: [1, { c: 1 }] } }
7091
7496
 
7092
- expect(path('a.b.1.c', obj)).toBe(1)
7497
+ expect(path('a.b.1.c')(obj)).toBe(1)
7093
7498
  })
7094
7499
 
7095
7500
  test('works with undefined', () => {
7096
7501
  const obj = { a: { b: { c: 1 } } }
7097
7502
 
7098
- expect(path('a.b.c.d.f', obj)).toBeUndefined()
7099
- expect(path('foo.babaz', undefined)).toBeUndefined()
7503
+ expect(path('a.b.c.d.f')(obj)).toBeUndefined()
7504
+ expect(path('foo.babaz')(undefined)).toBeUndefined()
7100
7505
  expect(path('foo.babaz')(undefined)).toBeUndefined()
7101
7506
  })
7102
7507
 
@@ -7111,12 +7516,12 @@ test('path', () => {
7111
7516
  })
7112
7517
 
7113
7518
  test('with number string in between', () => {
7114
- expect(path(['a', '1', 'b'], { a: [{ b: 1 }, { b: 2 }] })).toBe(2)
7519
+ expect(path(['a', '1', 'b'])({ a: [{ b: 1 }, { b: 2 }] })).toBe(2)
7115
7520
  })
7116
7521
 
7117
7522
  test('null is not a valid path', () => {
7118
7523
  expect(
7119
- path('audio_tracks', {
7524
+ path('audio_tracks')({
7120
7525
  a: 1,
7121
7526
  audio_tracks: null,
7122
7527
  }),
@@ -7153,6 +7558,131 @@ describe('R.path with string as path', () => {
7153
7558
 
7154
7559
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#path)
7155
7560
 
7561
+ ### pathSatisfies
7562
+
7563
+ ```typescript
7564
+
7565
+ pathSatisfies<S, K0 extends string & keyof S>(
7566
+ predicate: (x: S[K0]) => boolean,
7567
+ path: [K0]
7568
+ ): (obj: S) => boolean
7569
+ ```
7570
+
7571
+ ```javascript
7572
+ const result = R.pathSatisfies(
7573
+ x => x > 0,
7574
+ ['a', 'b', 'c'],
7575
+ {a: {b: {c: 1}}}
7576
+ )
7577
+ // => true
7578
+ ```
7579
+
7580
+ <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.pathSatisfies(%0A%20%20x%20%3D%3E%20x%20%3E%200%2C%0A%20%20%5B'a'%2C%20'b'%2C%20'c'%5D%2C%0A%20%20%7Ba%3A%20%7Bb%3A%20%7Bc%3A%201%7D%7D%7D%0A)%0A%2F%2F%20%3D%3E%20true">Try this <strong>R.pathSatisfies</strong> example in Rambda REPL</a>
7581
+
7582
+ <details>
7583
+
7584
+ <summary>All TypeScript definitions</summary>
7585
+
7586
+ ```typescript
7587
+ pathSatisfies<S, K0 extends string & keyof S>(
7588
+ predicate: (x: S[K0]) => boolean,
7589
+ path: [K0]
7590
+ ): (obj: S) => boolean;
7591
+ pathSatisfies<S, K0 extends string & keyof S>(
7592
+ predicate: (x: S[K0]) => boolean,
7593
+ path: `${K0}`
7594
+ ): (obj: S) => boolean;
7595
+ pathSatisfies<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
7596
+ predicate: (x: S[K0][K1]) => boolean,
7597
+ path: [K0, K1]
7598
+ ): (obj: S) => boolean;
7599
+ pathSatisfies<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
7600
+ predicate: (x: S[K0][K1]) => boolean,
7601
+ path: `${K0}.${K1}`
7602
+ ): (obj: S) => boolean;
7603
+ ...
7604
+ ...
7605
+ ```
7606
+
7607
+ </details>
7608
+
7609
+ <details>
7610
+
7611
+ <summary><strong>R.pathSatisfies</strong> source</summary>
7612
+
7613
+ ```javascript
7614
+ import { path } from './path.js'
7615
+
7616
+ export function pathSatisfies(fn, pathInput) {
7617
+ return obj => Boolean(fn(path(pathInput)(obj)))
7618
+ }
7619
+ ```
7620
+
7621
+ </details>
7622
+
7623
+ <details>
7624
+
7625
+ <summary><strong>Tests</strong></summary>
7626
+
7627
+ ```javascript
7628
+ import { pathSatisfies } from './pathSatisfies.js'
7629
+
7630
+ const isPositive = n => n > 0
7631
+
7632
+ it('returns true if the specified object path satisfies the given predicate', () => {
7633
+ expect(pathSatisfies(isPositive, ['x', 'y'])({ x: { y: 1 } })).toBe(true)
7634
+ })
7635
+
7636
+ it('returns false if the specified path does not exist', () => {
7637
+ expect(pathSatisfies(isPositive, ['x', 'y'])({ x: { z: 42 } })).toBe(false)
7638
+ expect(pathSatisfies(isPositive, 'x.y')({ x: { z: 42 } })).toBe(false)
7639
+ })
7640
+
7641
+ it('returns false otherwise', () => {
7642
+ expect(pathSatisfies(isPositive, ['x', 'y'])({ x: { y: 0 } })).toBe(false)
7643
+ })
7644
+ ```
7645
+
7646
+ </details>
7647
+
7648
+ <details>
7649
+
7650
+ <summary><strong>TypeScript</strong> test</summary>
7651
+
7652
+ ```typescript
7653
+ import { pathSatisfies, pipe } from 'rambda'
7654
+
7655
+ const input = { a: { b: { c: 'bar' } } }
7656
+
7657
+ describe('R.pathSatisfies', () => {
7658
+ it('happy', () => {
7659
+ const result = pipe(
7660
+ input,
7661
+ pathSatisfies(
7662
+ x => {
7663
+ x // $ExpectType string
7664
+ return x !== 'foo'
7665
+ },
7666
+ ['a', 'b', 'c'],
7667
+ ),
7668
+ )
7669
+ const resultStringInput = pipe(
7670
+ input,
7671
+ pathSatisfies(x => {
7672
+ x // $ExpectType string
7673
+ return x !== 'foo'
7674
+ }, 'a.b.c'),
7675
+ )
7676
+ result // $ExpectType boolean
7677
+ resultStringInput // $ExpectType boolean
7678
+ })
7679
+ })
7680
+ ```
7681
+
7682
+ </details>
7683
+
7684
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pathSatisfies)
7685
+
7156
7686
  ### permutations
7157
7687
 
7158
7688
  ```typescript
@@ -8265,7 +8795,7 @@ export function propOr(defaultValue, property) {
8265
8795
  return defaultValue
8266
8796
  }
8267
8797
 
8268
- return defaultTo(defaultValue, obj[property])
8798
+ return defaultTo(defaultValue)(obj[property])
8269
8799
  }
8270
8800
  }
8271
8801
  ```
@@ -8409,14 +8939,15 @@ describe('R.propSatisfies', () => {
8409
8939
  range(startInclusive: number): (endExclusive: number) => number[]
8410
8940
  ```
8411
8941
 
8412
- It returns list of numbers between `startInclusive` to `endInclusive` markers.
8942
+ It returns list of numbers between `startInclusive` to `endExclusive` markers.
8943
+ If `start` is greater than `end`, then the result will be in descending order.
8413
8944
 
8414
8945
  ```javascript
8415
- R.range(0)(5)
8416
- // => [0, 1, 2, 3, 4, 5]
8946
+ [R.range(0)(5), R.range(5)(0)]
8947
+ // => [[0, 1, 2, 3, 4], [5, 4, 3, 2, 1]]
8417
8948
  ```
8418
8949
 
8419
- <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.range(0)(5)%0A%2F%2F%20%3D%3E%20%5B0%2C%201%2C%202%2C%203%2C%204%2C%205%5D">Try this <strong>R.range</strong> example in Rambda REPL</a>
8950
+ <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20%5BR.range(0)(5)%2C%20R.range(5)(0)%5D%0A%2F%2F%20%3D%3E%20%5B%5B0%2C%201%2C%202%2C%203%2C%204%5D%2C%20%5B5%2C%204%2C%203%2C%202%2C%201%5D%5D">Try this <strong>R.range</strong> example in Rambda REPL</a>
8420
8951
 
8421
8952
  <details>
8422
8953
 
@@ -8433,42 +8964,33 @@ range(startInclusive: number): (endExclusive: number) => number[];
8433
8964
  <summary><strong>R.range</strong> source</summary>
8434
8965
 
8435
8966
  ```javascript
8436
- export function range(start) {
8437
- return end => {
8438
- if (Number.isNaN(Number(start)) || Number.isNaN(Number(end))) {
8439
- throw new TypeError('Both arguments to range must be numbers')
8440
- }
8967
+ function rangeDescending(start, end) {
8968
+ const len = start - end
8969
+ const willReturn = Array(len)
8441
8970
 
8442
- if (end <= start) {
8443
- return []
8444
- }
8445
-
8446
- const len = end - start
8447
- const willReturn = Array(len)
8448
-
8449
- for (let i = 0; i < len + 1; i++) {
8450
- willReturn[i] = start + i
8451
- }
8971
+ for (let i = 0; i < len; i++) {
8972
+ willReturn[i] = start - i
8973
+ }
8452
8974
 
8453
- return willReturn
8454
- }
8975
+ return willReturn
8455
8976
  }
8456
8977
 
8457
- export function rangeDescending(start) {
8978
+ export function range(start) {
8458
8979
  return end => {
8459
8980
  if (Number.isNaN(Number(start)) || Number.isNaN(Number(end))) {
8460
8981
  throw new TypeError('Both arguments to range must be numbers')
8461
8982
  }
8462
8983
 
8463
- if (end >= start) {
8984
+ if (end === start) {
8464
8985
  return []
8465
8986
  }
8987
+ if (end < start) return rangeDescending(start,end)
8466
8988
 
8467
- const len = start - end
8989
+ const len = end - start
8468
8990
  const willReturn = Array(len)
8469
8991
 
8470
- for (let i = 0; i < len + 1; i++) {
8471
- willReturn[i] = start - i
8992
+ for (let i = 0; i < len; i++) {
8993
+ willReturn[i] = start + i
8472
8994
  }
8473
8995
 
8474
8996
  return willReturn
@@ -8483,24 +9005,12 @@ export function rangeDescending(start) {
8483
9005
  <summary><strong>Tests</strong></summary>
8484
9006
 
8485
9007
  ```javascript
8486
- import { range, rangeDescending } from './range.js'
9008
+ import { range } from './range.js'
8487
9009
 
8488
9010
  test('happy', () => {
8489
- expect(range(0)(10)).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
8490
- })
8491
-
8492
- test('end range is bigger than start range', () => {
8493
- expect(range(7)(3)).toEqual([])
8494
- expect(range(5)(5)).toEqual([])
8495
- })
8496
-
8497
- test('descending', () => {
8498
- expect(rangeDescending(5)(0)).toEqual([5, 4, 3, 2, 1, 0])
8499
- })
8500
-
8501
- test('descending end range is bigger than start range', () => {
8502
- expect(rangeDescending(3)(7)).toEqual([])
8503
- expect(rangeDescending(5)(5)).toEqual([])
9011
+ expect(range(0)(5)).toEqual([0, 1, 2, 3, 4])
9012
+ expect(range(7)(3)).toEqual([7, 6, 5, 4])
9013
+ expect(range(5)(5)).toEqual([])
8504
9014
  })
8505
9015
  ```
8506
9016
 
@@ -8526,34 +9036,6 @@ describe('R.range', () => {
8526
9036
 
8527
9037
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#range)
8528
9038
 
8529
- ### rangeDescending
8530
-
8531
- ```typescript
8532
-
8533
- rangeDescending(startInclusive: number): (endExclusive: number) => number[]
8534
- ```
8535
-
8536
- Same as `R.range` but in descending order.
8537
-
8538
- ```javascript
8539
- R.rangeDescending(5, 0)
8540
- // => [5, 4, 3, 2, 1, 0]
8541
- ```
8542
-
8543
- <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.rangeDescending(5%2C%200)%0A%2F%2F%20%3D%3E%20%5B5%2C%204%2C%203%2C%202%2C%201%2C%200%5D">Try this <strong>R.rangeDescending</strong> example in Rambda REPL</a>
8544
-
8545
- <details>
8546
-
8547
- <summary>All TypeScript definitions</summary>
8548
-
8549
- ```typescript
8550
- rangeDescending(startInclusive: number): (endExclusive: number) => number[];
8551
- ```
8552
-
8553
- </details>
8554
-
8555
- [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#rangeDescending)
8556
-
8557
9039
  ### reduce
8558
9040
 
8559
9041
  ```typescript
@@ -9026,89 +9508,6 @@ describe('R.replace', () => {
9026
9508
 
9027
9509
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#replace)
9028
9510
 
9029
- ### replaceItemAtIndex
9030
-
9031
- ```typescript
9032
-
9033
- replaceItemAtIndex<T>(index: number, replaceFn: (x: T) => T): (list: T[]) => T[]
9034
- ```
9035
-
9036
- It replaces `index` in array `list` with the result of `replaceFn(list[i])`.
9037
-
9038
- ```javascript
9039
- const result = R.pipe(
9040
- [1, 2, 3],
9041
- R.replaceItemAtIndex(1, R.add(1))
9042
- ) // => [1, 3, 3]
9043
- ```
9044
-
9045
- <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.pipe(%0A%09%5B1%2C%202%2C%203%5D%2C%0A%09R.replaceItemAtIndex(1%2C%20R.add(1))%0A)%20%2F%2F%20%3D%3E%20%5B1%2C%203%2C%203%5D">Try this <strong>R.replaceItemAtIndex</strong> example in Rambda REPL</a>
9046
-
9047
- <details>
9048
-
9049
- <summary>All TypeScript definitions</summary>
9050
-
9051
- ```typescript
9052
- replaceItemAtIndex<T>(index: number, replaceFn: (x: T) => T): (list: T[]) => T[];
9053
- ```
9054
-
9055
- </details>
9056
-
9057
- <details>
9058
-
9059
- <summary><strong>R.replaceItemAtIndex</strong> source</summary>
9060
-
9061
- ```javascript
9062
- import { cloneList } from './_internals/cloneList.js'
9063
-
9064
- export function replaceItemAtIndex(index, replaceFn) {
9065
- return list => {
9066
- const actualIndex = index < 0 ? list.length + index : index
9067
- if (index >= list.length || actualIndex < 0) {
9068
- return list
9069
- }
9070
-
9071
- const clone = cloneList(list)
9072
- clone[actualIndex] = replaceFn(clone[actualIndex])
9073
-
9074
- return clone
9075
- }
9076
- }
9077
- ```
9078
-
9079
- </details>
9080
-
9081
- <details>
9082
-
9083
- <summary><strong>Tests</strong></summary>
9084
-
9085
- ```javascript
9086
- import { replaceItemAtIndex } from './replaceItemAtIndex.js'
9087
-
9088
- const add10 = x => x + 10
9089
-
9090
- const list = [0, 1, 2]
9091
- const expected = [0, 11, 2]
9092
-
9093
- test('happy', () => {
9094
- expect(replaceItemAtIndex(1, add10)(list)).toEqual(expected)
9095
- })
9096
-
9097
- test('with negative index', () => {
9098
- expect(replaceItemAtIndex(-2, add10)(list)).toEqual(expected)
9099
- })
9100
-
9101
- test('when index is out of bounds', () => {
9102
- const list = [0, 1, 2, 3]
9103
- expect(replaceItemAtIndex(4, add10)(list)).toEqual(list)
9104
- expect(replaceItemAtIndex(-5, add10)(list)).toEqual(list)
9105
- })
9106
- ```
9107
-
9108
- </details>
9109
-
9110
- [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#replaceItemAtIndex)
9111
-
9112
9511
  ### shuffle
9113
9512
 
9114
9513
  ```typescript
@@ -9142,6 +9541,8 @@ shuffle<T>(list: T[]): T[];
9142
9541
  <summary><strong>R.shuffle</strong> source</summary>
9143
9542
 
9144
9543
  ```javascript
9544
+ import { cloneList } from './_internals/cloneList.js'
9545
+
9145
9546
  export function shuffle(listInput) {
9146
9547
  const list = cloneList(listInput)
9147
9548
  let counter = list.length
@@ -9306,7 +9707,7 @@ const list = [
9306
9707
  ]
9307
9708
  const sortFn = x => x.a
9308
9709
 
9309
- const result = R.sortBy(sortFn, list)
9710
+ const result = R.sortBy(sortFn)(list)
9310
9711
  const expected = [
9311
9712
  {a: 1},
9312
9713
  {a: 2},
@@ -9315,7 +9716,7 @@ const expected = [
9315
9716
  // => `result` is equal to `expected`
9316
9717
  ```
9317
9718
 
9318
- <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20list%20%3D%20%5B%0A%20%20%7Ba%3A%202%7D%2C%0A%20%20%7Ba%3A%203%7D%2C%0A%20%20%7Ba%3A%201%7D%0A%5D%0Aconst%20sortFn%20%3D%20x%20%3D%3E%20x.a%0A%0Aconst%20result%20%3D%20R.sortBy(sortFn%2C%20list)%0Aconst%20expected%20%3D%20%5B%0A%20%20%7Ba%3A%201%7D%2C%0A%20%20%7Ba%3A%202%7D%2C%0A%20%20%7Ba%3A%203%7D%0A%5D%0A%2F%2F%20%3D%3E%20%60result%60%20is%20equal%20to%20%60expected%60">Try this <strong>R.sortBy</strong> example in Rambda REPL</a>
9719
+ <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20list%20%3D%20%5B%0A%20%20%7Ba%3A%202%7D%2C%0A%20%20%7Ba%3A%203%7D%2C%0A%20%20%7Ba%3A%201%7D%0A%5D%0Aconst%20sortFn%20%3D%20x%20%3D%3E%20x.a%0A%0Aconst%20result%20%3D%20R.sortBy(sortFn)(list)%0Aconst%20expected%20%3D%20%5B%0A%20%20%7Ba%3A%201%7D%2C%0A%20%20%7Ba%3A%202%7D%2C%0A%20%20%7Ba%3A%203%7D%0A%5D%0A%2F%2F%20%3D%3E%20%60result%60%20is%20equal%20to%20%60expected%60">Try this <strong>R.sortBy</strong> example in Rambda REPL</a>
9319
9720
 
9320
9721
  <details>
9321
9722
 
@@ -9334,21 +9735,30 @@ sortBy<T>(sortFn: (x: T) => Ord): (list: T[]) => T[];
9334
9735
  ```javascript
9335
9736
  import { cloneList } from './_internals/cloneList.js'
9336
9737
 
9337
- export function sortBy(sortFn) {
9338
- return list => {
9339
- const clone = cloneList(list)
9738
+ export function sortByFn (
9739
+ sortFn,
9740
+ list,
9741
+ descending
9742
+ ){
9743
+ const clone = cloneList(list)
9340
9744
 
9341
- return clone.sort((a, b) => {
9342
- const aSortResult = sortFn(a)
9343
- const bSortResult = sortFn(b)
9745
+ return clone.sort((a, b) => {
9746
+ const aSortResult = sortFn(a)
9747
+ const bSortResult = sortFn(b)
9344
9748
 
9345
- if (aSortResult === bSortResult) {
9346
- return 0
9347
- }
9749
+ if (aSortResult === bSortResult) {
9750
+ return 0
9751
+ }
9752
+ if(
9753
+ descending
9754
+ ) return aSortResult > bSortResult ? -1 : 1
9348
9755
 
9349
- return aSortResult < bSortResult ? -1 : 1
9350
- })
9351
- }
9756
+ return aSortResult < bSortResult ? -1 : 1
9757
+ })
9758
+ }
9759
+
9760
+ export function sortBy(sortFn) {
9761
+ return list => sortByFn(sortFn, list, false)
9352
9762
  }
9353
9763
  ```
9354
9764
 
@@ -9361,13 +9771,18 @@ export function sortBy(sortFn) {
9361
9771
  ```javascript
9362
9772
  import { sortBy } from './sortBy.js'
9363
9773
 
9774
+ const input = [{ a: 2 }, { a: 1 }, { a: 1 }, { a: 3 }]
9775
+
9364
9776
  test('happy', () => {
9365
- const input = [{ a: 2 }, { a: 1 }, { a: 1 }, { a: 3 }]
9366
9777
  const expected = [{ a: 1 }, { a: 1 }, { a: 2 }, { a: 3 }]
9367
9778
 
9368
9779
  const result = sortBy(x => x.a)(input)
9369
9780
  expect(result).toEqual(expected)
9370
9781
  })
9782
+
9783
+ test('with non-existing path', () => {
9784
+ expect(sortBy(x => x.b)(input)).toEqual(input)
9785
+ })
9371
9786
  ```
9372
9787
 
9373
9788
  </details>
@@ -9397,6 +9812,215 @@ describe('R.sortBy', () => {
9397
9812
 
9398
9813
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortBy)
9399
9814
 
9815
+ ### sortByDescending
9816
+
9817
+ ```typescript
9818
+
9819
+ sortByDescending<T>(sortFn: (a: T, b: T) => number): (list: T[]) => T[]
9820
+ ```
9821
+
9822
+ <details>
9823
+
9824
+ <summary>All TypeScript definitions</summary>
9825
+
9826
+ ```typescript
9827
+ sortByDescending<T>(sortFn: (a: T, b: T) => number): (list: T[]) => T[];
9828
+ ```
9829
+
9830
+ </details>
9831
+
9832
+ <details>
9833
+
9834
+ <summary><strong>R.sortByDescending</strong> source</summary>
9835
+
9836
+ ```javascript
9837
+ import { sortByFn } from "./sortBy.js";
9838
+
9839
+ export function sortByDescending(sortFn) {
9840
+ return list => sortByFn(sortFn, list, true)
9841
+ }
9842
+ ```
9843
+
9844
+ </details>
9845
+
9846
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortByDescending)
9847
+
9848
+ ### sortByPath
9849
+
9850
+ ```typescript
9851
+
9852
+ sortByPath<S, K0 extends string & keyof S>(
9853
+ path: [K0]
9854
+ ): (list: S[]) => S[]
9855
+ ```
9856
+
9857
+ It sorts `list` by the value of `path` property.
9858
+
9859
+ <details>
9860
+
9861
+ <summary>All TypeScript definitions</summary>
9862
+
9863
+ ```typescript
9864
+ sortByPath<S, K0 extends string & keyof S>(
9865
+ path: [K0]
9866
+ ): (list: S[]) => S[];
9867
+ sortByPath<S, K0 extends string & keyof S>(
9868
+ path: `${K0}`
9869
+ ): (list: S[]) => S[];
9870
+ sortByPath<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
9871
+ path: [K0, K1]
9872
+ ): (list: S[]) => S[];
9873
+ sortByPath<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
9874
+ path: `${K0}.${K1}`
9875
+ ): (list: S[]) => S[];
9876
+ ...
9877
+ ...
9878
+ ```
9879
+
9880
+ </details>
9881
+
9882
+ <details>
9883
+
9884
+ <summary><strong>R.sortByPath</strong> source</summary>
9885
+
9886
+ ```javascript
9887
+ import { path } from './path.js'
9888
+ import { sortBy } from './sortBy.js'
9889
+
9890
+ export function sortByPath(sortPath) {
9891
+ return list => sortBy(path(sortPath))(list)
9892
+ }
9893
+ ```
9894
+
9895
+ </details>
9896
+
9897
+ <details>
9898
+
9899
+ <summary><strong>Tests</strong></summary>
9900
+
9901
+ ```javascript
9902
+ import { sortByPath } from './sortByPath.js'
9903
+
9904
+ const list = [{ a: { b: 3 } }, { a: { b: 1 } }, { a: { b: 2 } }]
9905
+ const sorted = [{ a: { b: 1 } }, { a: { b: 2 } }, { a: { b: 3 } }]
9906
+
9907
+ test('with string as path', () => {
9908
+ expect(sortByPath('a.b')(list)).toEqual(sorted)
9909
+ })
9910
+
9911
+ test('with list of strings as path', () => {
9912
+ expect(sortByPath(['a', 'b'])(list)).toEqual(sorted)
9913
+ })
9914
+
9915
+ test('when path is not found in any item', () => {
9916
+ const list = [{ a: { b: 3 } }, { a: { b: 1 } }, { a: {} }]
9917
+ expect(sortByPath('a.b.c.d')(list)).toEqual(list)
9918
+ })
9919
+ ```
9920
+
9921
+ </details>
9922
+
9923
+ <details>
9924
+
9925
+ <summary><strong>TypeScript</strong> test</summary>
9926
+
9927
+ ```typescript
9928
+ import { pipe, sortByPath } from 'rambda'
9929
+
9930
+ const input= [{ a: { b: 2 } }, { a: { b: 1 } }]
9931
+
9932
+ describe('R.sortByPath', () => {
9933
+ it('with string as path', () => {
9934
+ const result = pipe(input, sortByPath('a.b'))
9935
+ result[0].a.b // $ExpectType number
9936
+ })
9937
+ it('with list of strings as path', () => {
9938
+ const result = pipe(input, sortByPath(['a', 'b']))
9939
+ result[0].a.b // $ExpectType number
9940
+ })
9941
+ it('with non-existent path', () => {
9942
+ // @ts-expect-error
9943
+ pipe(input, sortByPath(['a', 'c']))
9944
+ // @ts-expect-error
9945
+ pipe(input, sortByPath('a.c'))
9946
+ })
9947
+ })
9948
+ ```
9949
+
9950
+ </details>
9951
+
9952
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortByPath)
9953
+
9954
+ ### sortByPathDescending
9955
+
9956
+ ```typescript
9957
+
9958
+ sortByPathDescending<S, K0 extends string & keyof S>(
9959
+ path: [K0]
9960
+ ): (list: S[]) => S[]
9961
+ ```
9962
+
9963
+ <details>
9964
+
9965
+ <summary>All TypeScript definitions</summary>
9966
+
9967
+ ```typescript
9968
+ sortByPathDescending<S, K0 extends string & keyof S>(
9969
+ path: [K0]
9970
+ ): (list: S[]) => S[];
9971
+ sortByPathDescending<S, K0 extends string & keyof S>(
9972
+ path: `${K0}`
9973
+ ): (list: S[]) => S[];
9974
+ sortByPathDescending<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
9975
+ path: [K0, K1]
9976
+ ): (list: S[]) => S[];
9977
+ sortByPathDescending<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
9978
+ path: `${K0}.${K1}`
9979
+ ): (list: S[]) => S[];
9980
+ ...
9981
+ ...
9982
+ ```
9983
+
9984
+ </details>
9985
+
9986
+ <details>
9987
+
9988
+ <summary><strong>R.sortByPathDescending</strong> source</summary>
9989
+
9990
+ ```javascript
9991
+ import { path } from './path.js'
9992
+ import { sortByDescending } from './sortByDescending.js'
9993
+
9994
+ export function sortByPathDescending(sortPath) {
9995
+ return list => sortByDescending(path(sortPath))(list)
9996
+ }
9997
+ ```
9998
+
9999
+ </details>
10000
+
10001
+ <details>
10002
+
10003
+ <summary><strong>Tests</strong></summary>
10004
+
10005
+ ```javascript
10006
+ import { sortByPathDescending } from './sortByPathDescending.js'
10007
+
10008
+ const list = [{ a: { b: 3 } }, { a: { b: 1 } }, { a: { b: 2 } }]
10009
+ const sorted = [{ a: { b: 3 } }, { a: { b: 2 } }, { a: { b: 1 } }]
10010
+
10011
+ test('with string as path', () => {
10012
+ expect(sortByPathDescending('a.b')(list)).toEqual(sorted)
10013
+ })
10014
+
10015
+ test('with list of strings as path', () => {
10016
+ expect(sortByPathDescending(['a', 'b'])(list)).toEqual(sorted)
10017
+ })
10018
+ ```
10019
+
10020
+ </details>
10021
+
10022
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortByPathDescending)
10023
+
9400
10024
  ### sortObject
9401
10025
 
9402
10026
  ```typescript
@@ -11532,7 +12156,7 @@ describe('R.unless', () => {
11532
12156
 
11533
12157
  ```typescript
11534
12158
 
11535
- unwind<S extends string>(prop: S): <T>(obj: T) => Omit<T, S> & { [K in S]: T[S][number] }
12159
+ unwind<S extends string>(prop: S): <T>(obj: T) => MergeTypes<Omit<T, S> & { [K in S]: T[S][number] }>
11536
12160
  ```
11537
12161
 
11538
12162
  It takes an object and a property name. The method will return a list of objects, where each object is a shallow copy of the input object, but with the property array unwound.
@@ -11554,7 +12178,7 @@ const expected = [{a:1, b:2}, {a:1, b:3}]
11554
12178
  <summary>All TypeScript definitions</summary>
11555
12179
 
11556
12180
  ```typescript
11557
- unwind<S extends string>(prop: S): <T>(obj: T) => Omit<T, S> & { [K in S]: T[S][number] };
12181
+ unwind<S extends string>(prop: S): <T>(obj: T) => MergeTypes<Omit<T, S> & { [K in S]: T[S][number] }>;
11558
12182
  ```
11559
12183
 
11560
12184
  </details>
@@ -11732,7 +12356,7 @@ test('with negative index', () => {
11732
12356
 
11733
12357
  ```typescript
11734
12358
 
11735
- when<T>(predicate: (x: T) => boolean, whenTrueFn: (x: T) => T): (input: T) => T
12359
+ when<T, U extends T>(predicate: (x: T) => x is U, whenTrueFn: (x: U) => T): (input: T) => T
11736
12360
  ```
11737
12361
 
11738
12362
  It pass `input` to `predicate` function and if the result is `true`, it will return the result of `whenTrueFn(input)`.
@@ -11766,6 +12390,7 @@ const expected = [
11766
12390
  <summary>All TypeScript definitions</summary>
11767
12391
 
11768
12392
  ```typescript
12393
+ when<T, U extends T>(predicate: (x: T) => x is U, whenTrueFn: (x: U) => T): (input: T) => T;
11769
12394
  when<T>(predicate: (x: T) => boolean, whenTrueFn: (x: T) => T): (input: T) => T;
11770
12395
  when<T, U>(predicate: (x: T) => boolean, whenTrueFn: (x: T) => U): (input: T) => T | U;
11771
12396
  ```
@@ -11813,7 +12438,11 @@ test('happy', () => {
11813
12438
  <summary><strong>TypeScript</strong> test</summary>
11814
12439
 
11815
12440
  ```typescript
11816
- import { pipe, tap, when } from 'rambda'
12441
+ import { head, pipe, tap, when } from 'rambda'
12442
+
12443
+ function notNull<T>(a: T | null | undefined): a is T {
12444
+ return a != null
12445
+ }
11817
12446
 
11818
12447
  describe('R.when', () => {
11819
12448
  it('happy', () => {
@@ -11833,6 +12462,15 @@ describe('R.when', () => {
11833
12462
  )
11834
12463
 
11835
12464
  result // $ExpectType string | number
12465
+ })
12466
+
12467
+ it('with assertion of type', () => {
12468
+ const result = pipe(
12469
+ [1, null, 2, 3],
12470
+ head,
12471
+ when(notNull, x => x + 1),
12472
+ )
12473
+ result // $ExpectType number | null
11836
12474
  })
11837
12475
  })
11838
12476
  ```
@@ -12153,14 +12791,20 @@ This is major revamp of `Rambda` library:
12153
12791
  -- R.rejectObject
12154
12792
  -- R.findNth
12155
12793
  -- R.combinations
12156
- -- R.rangeDescending
12794
+ -- R.sortByPath
12795
+ -- R.sortByPathDescending
12796
+ -- R.sortByDescending
12797
+ -- R.flattenObject
12798
+ -- R.addPropToObjects
12157
12799
 
12158
12800
  - Rename following methods:
12159
12801
 
12160
- -- replaceItemAtIndex -> adjust
12802
+ -- modifyItemAtIndex -> adjust
12161
12803
  -- checkObjectWithSpec -> where
12162
12804
  -- objectIncludes -> whereEq
12163
12805
  -- modify -> modifyProp
12806
+ -- chain -> flatMap
12807
+ -- mapObjIndexed -> mapObject
12164
12808
 
12165
12809
  _ Regarding using object as input with TypeScript in methods such as `R.map/filter` - this feature is no longer supported in TypeScript as it has multiple issues when using inside pipes. In JS, it still works as before. Following methods are affected:
12166
12810
 
@@ -12188,7 +12832,9 @@ _ Regarding using object as input with TypeScript in methods such as `R.map/filt
12188
12832
 
12189
12833
  -- append/prepend
12190
12834
 
12191
- - Change `R.range` to work with `endIndex` included instead of `endIndex` excluded, i.e. `R.range(0, 2)` will return `[0, 1, 2]` instead of `[0, 1]`. This is done because `R.rangeDescending` is added and users would wonder if end or start index is excluded.
12835
+ - Change `R.range` to work with descending order.
12836
+
12837
+ - Remove `rambda/immutable` as import option as it is hard to support in the new context.
12192
12838
 
12193
12839
  - Sync with typing of `@types/ramda`:
12194
12840
 
@@ -12241,11 +12887,6 @@ _ Regarding using object as input with TypeScript in methods such as `R.map/filt
12241
12887
 
12242
12888
  - Revert adding stopper logic in `R.reduce` - https://github.com/selfrefactor/rambda/pull/630
12243
12889
 
12244
- - Renamed methods:
12245
-
12246
- -- `chain` to `flatMap`
12247
- -- `mapObjIndexed` to `mapObject`
12248
-
12249
12890
  9.4.2
12250
12891
 
12251
12892
  - Fix TS issue when `R.take` is used as part of `R.pipe`.