rambda 10.0.0-beta.1 → 10.0.0-beta.3

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
@@ -258,6 +258,134 @@ it('R.addProp', () => {
258
258
 
259
259
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#addProp)
260
260
 
261
+ ### addPropToObjects
262
+
263
+ ```typescript
264
+
265
+ addPropToObjects<
266
+ T extends object,
267
+ K extends string,
268
+ R
269
+ >(
270
+ property: K,
271
+ fn: (input: T) => R
272
+ ): (list: T[]) => MergeTypes<T & { [P in K]: R }>[]
273
+ ```
274
+
275
+ It receives list of objects and add new property to each item.
276
+
277
+ The value is based on result of `fn` function, which receives the current object as argument.
278
+
279
+ ```javascript
280
+ const result = R.pipe(
281
+ [
282
+ {a: 1, b: 2},
283
+ {a: 3, b: 4},
284
+ ],
285
+ R.addPropToObjects(
286
+ 'c',
287
+ (x) => String(x.a + x.b),
288
+ )
289
+ )
290
+ // => [{a: 1, b: 2, c: '3'}, {a: 3, b: 4, c: '7'}]
291
+ ```
292
+
293
+ <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>
294
+
295
+ <details>
296
+
297
+ <summary>All TypeScript definitions</summary>
298
+
299
+ ```typescript
300
+ addPropToObjects<
301
+ T extends object,
302
+ K extends string,
303
+ R
304
+ >(
305
+ property: K,
306
+ fn: (input: T) => R
307
+ ): (list: T[]) => MergeTypes<T & { [P in K]: R }>[];
308
+ ```
309
+
310
+ </details>
311
+
312
+ <details>
313
+
314
+ <summary><strong>R.addPropToObjects</strong> source</summary>
315
+
316
+ ```javascript
317
+ import { mapFn } from './map.js'
318
+
319
+ export function addPropToObjects (
320
+ property,
321
+ fn
322
+ ){
323
+ return listOfObjects => mapFn(
324
+ (obj) => ({
325
+ ...(obj),
326
+ [property]: fn(obj)
327
+ }),
328
+ listOfObjects
329
+ )
330
+ }
331
+ ```
332
+
333
+ </details>
334
+
335
+ <details>
336
+
337
+ <summary><strong>Tests</strong></summary>
338
+
339
+ ```javascript
340
+ import { pipe } from "./pipe.js"
341
+ import { addPropToObjects } from "./addPropToObjects.js"
342
+
343
+ test('R.addPropToObjects', () => {
344
+ let result = pipe(
345
+ [
346
+ {a: 1, b: 2},
347
+ {a: 3, b: 4},
348
+ ],
349
+ addPropToObjects(
350
+ 'c',
351
+ (x) => String(x.a + x.b),
352
+ )
353
+ )
354
+ expect(result).toEqual([
355
+ { a: 1, b: 2, c: '3' },
356
+ { a: 3, b: 4, c: '7' },
357
+ ])
358
+ })
359
+ ```
360
+
361
+ </details>
362
+
363
+ <details>
364
+
365
+ <summary><strong>TypeScript</strong> test</summary>
366
+
367
+ ```typescript
368
+ import { addPropToObjects, pipe } from 'rambda'
369
+
370
+ it('R.addPropToObjects', () => {
371
+ let result = pipe(
372
+ [
373
+ {a: 1, b: 2},
374
+ {a: 3, b: 4},
375
+ ],
376
+ addPropToObjects(
377
+ 'c',
378
+ (x) => String(x.a + x.b),
379
+ )
380
+ )
381
+ result // $ExpectType { a: number; b: number; c: string; }[]
382
+ })
383
+ ```
384
+
385
+ </details>
386
+
387
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#addPropToObjects)
388
+
261
389
  ### all
262
390
 
263
391
  ```typescript
@@ -1610,12 +1738,8 @@ function isFalsy(input) {
1610
1738
  return input === undefined || input === null || Number.isNaN(input) === true
1611
1739
  }
1612
1740
 
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
1741
+ export function defaultTo(defaultArgument) {
1742
+ return input => isFalsy(input) ? defaultArgument : input
1619
1743
  }
1620
1744
  ```
1621
1745
 
@@ -1641,15 +1765,15 @@ test('with NaN', () => {
1641
1765
  })
1642
1766
 
1643
1767
  test('with empty string', () => {
1644
- expect(defaultTo('foo', '')).toBe('')
1768
+ expect(defaultTo('foo')('')).toBe('')
1645
1769
  })
1646
1770
 
1647
1771
  test('with false', () => {
1648
- expect(defaultTo('foo', false)).toBeFalsy()
1772
+ expect(defaultTo('foo')(false)).toBeFalsy()
1649
1773
  })
1650
1774
 
1651
1775
  test('when inputArgument passes initial check', () => {
1652
- expect(defaultTo('foo', 'bar')).toBe('bar')
1776
+ expect(defaultTo('foo')('bar')).toBe('bar')
1653
1777
  })
1654
1778
  ```
1655
1779
 
@@ -1755,12 +1879,8 @@ drop<T>(howMany: number): (list: T[]) => T[];
1755
1879
  <summary><strong>R.drop</strong> source</summary>
1756
1880
 
1757
1881
  ```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)
1882
+ export function drop(howManyToDrop, ) {
1883
+ return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
1764
1884
  }
1765
1885
  ```
1766
1886
 
@@ -1771,31 +1891,18 @@ export function drop(howManyToDrop, listOrString) {
1771
1891
  <summary><strong>Tests</strong></summary>
1772
1892
 
1773
1893
  ```javascript
1774
- import assert from 'node:assert'
1775
-
1776
1894
  import { drop } from './drop.js'
1777
1895
 
1778
1896
  test('with array', () => {
1779
1897
  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')
1898
+ expect(drop(3)(['foo', 'bar', 'baz'])).toEqual([])
1899
+ expect(drop(4)(['foo', 'bar', 'baz'])).toEqual([])
1786
1900
  })
1787
1901
 
1788
1902
  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)
1903
+ expect(drop(0)([1, 2, 3])).toEqual([1, 2, 3])
1904
+ expect(drop(-1)([1, 2, 3])).toEqual([1, 2, 3])
1905
+ expect(drop(Number.NEGATIVE_INFINITY)([1, 2, 3])).toEqual([1, 2, 3])
1799
1906
  })
1800
1907
  ```
1801
1908
 
@@ -3907,6 +4014,229 @@ describe('flatten', () => {
3907
4014
 
3908
4015
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#flatten)
3909
4016
 
4017
+ ### flattenObject
4018
+
4019
+ ```typescript
4020
+
4021
+ flattenObject<T extends object>(obj: T): FlattenObject<T>
4022
+ ```
4023
+
4024
+ It transforms object to object where each value is represented with its path.
4025
+
4026
+ ```javascript
4027
+ const result = R.flattenObject(
4028
+ [1, 2, 3]
4029
+ )
4030
+ // => [3, 1, 2] or [2, 3, 1] or ...
4031
+ ```
4032
+
4033
+ <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>
4034
+
4035
+ <details>
4036
+
4037
+ <summary>All TypeScript definitions</summary>
4038
+
4039
+ ```typescript
4040
+ flattenObject<T extends object>(obj: T): FlattenObject<T>;
4041
+ ```
4042
+
4043
+ </details>
4044
+
4045
+ <details>
4046
+
4047
+ <summary><strong>R.flattenObject</strong> source</summary>
4048
+
4049
+ ```javascript
4050
+ import { type } from './type.js'
4051
+
4052
+ export function flattenObjectHelper(obj, accumulator = []){
4053
+ const willReturn = {}
4054
+ Object.keys(obj).forEach(key => {
4055
+ const typeIs = type(obj[ key ])
4056
+ if (typeIs === 'Object'){
4057
+ const [ flatResultValue, flatResultPath ] = flattenObjectHelper(obj[ key ],
4058
+ [ ...accumulator, key ])
4059
+ willReturn[ flatResultPath.join('.') ] = flatResultValue
4060
+
4061
+ return
4062
+ } else if (accumulator.length > 0){
4063
+ const finalKey = [ ...accumulator, key ].join('.')
4064
+ willReturn[ finalKey ] = obj[ key ]
4065
+
4066
+ return
4067
+ }
4068
+ willReturn[ key ] = obj[ key ]
4069
+ })
4070
+ if (accumulator.length > 0) return [ willReturn, accumulator ]
4071
+
4072
+ return willReturn
4073
+ }
4074
+
4075
+ export function transformFlatObject(obj){
4076
+ const willReturn = {}
4077
+
4078
+ const transformFlatObjectFn = objLocal => {
4079
+ const willReturnLocal = {}
4080
+ Object.keys(objLocal).forEach(key => {
4081
+ const typeIs = type(objLocal[ key ])
4082
+ if (typeIs === 'Object'){
4083
+ transformFlatObjectFn(objLocal[ key ])
4084
+
4085
+ return
4086
+ }
4087
+ willReturnLocal[ key ] = objLocal[ key ]
4088
+ willReturn[ key ] = objLocal[ key ]
4089
+ })
4090
+
4091
+ return willReturnLocal
4092
+ }
4093
+
4094
+ Object.keys(obj).forEach(key => {
4095
+ const typeIs = type(obj[ key ])
4096
+ if (typeIs === 'Object'){
4097
+ transformFlatObjectFn(obj[ key ], key)
4098
+
4099
+ return
4100
+ }
4101
+ willReturn[ key ] = obj[ key ]
4102
+ })
4103
+
4104
+ return willReturn
4105
+ }
4106
+
4107
+ export function flattenObject(obj){
4108
+ const willReturn = {}
4109
+
4110
+ Object.keys(obj).forEach(key => {
4111
+ const typeIs = type(obj[ key ])
4112
+ if (typeIs === 'Object'){
4113
+ const flatObject = flattenObjectHelper(obj[ key ])
4114
+ const transformed = transformFlatObject(flatObject)
4115
+
4116
+ Object.keys(transformed).forEach(keyTransformed => {
4117
+ willReturn[ `${ key }.${ keyTransformed }` ] = transformed[ keyTransformed ]
4118
+ })
4119
+ } else {
4120
+ willReturn[ key ] = obj[ key ]
4121
+ }
4122
+ })
4123
+
4124
+ return willReturn
4125
+ }
4126
+ ```
4127
+
4128
+ </details>
4129
+
4130
+ <details>
4131
+
4132
+ <summary><strong>Tests</strong></summary>
4133
+
4134
+ ```javascript
4135
+ import {
4136
+ flattenObject,
4137
+ flattenObjectHelper,
4138
+ transformFlatObject,
4139
+ } from './flattenObject.js'
4140
+
4141
+ test('happy', () => {
4142
+ const obj = {
4143
+ c : 3,
4144
+ d : {
4145
+ 'd.e' : [ 5, 6, 7 ],
4146
+ 'd.z' : 4,
4147
+ 'd.f' : { 'd.f.h' : 6 },
4148
+ },
4149
+ }
4150
+ const result = transformFlatObject(obj)
4151
+ expect(result).toEqual({
4152
+ 'c' : 3,
4153
+ 'd.e' : [ 5, 6, 7 ],
4154
+ 'd.z' : 4,
4155
+ 'd.f.h' : 6,
4156
+ })
4157
+ })
4158
+
4159
+ test('happy', () => {
4160
+ const result = flattenObject({
4161
+ a : 1,
4162
+ b : {
4163
+ c : 3,
4164
+ d : {
4165
+ e : 5,
4166
+ z : 4,
4167
+ f : {
4168
+ h : 6,
4169
+ i : 7,
4170
+ j : {
4171
+ k : 8,
4172
+ l : 9,
4173
+ },
4174
+ },
4175
+ },
4176
+ },
4177
+ })
4178
+ const expected = {
4179
+ 'a' : 1,
4180
+ 'b.c' : 3,
4181
+ 'b.d.e' : 5,
4182
+ 'b.d.z' : 4,
4183
+ 'b.d.f.h' : 6,
4184
+ 'b.d.f.i' : 7,
4185
+ 'b.d.f.j.k' : 8,
4186
+ 'b.d.f.j.l' : 9,
4187
+ }
4188
+ expect(result).toEqual(expected)
4189
+ })
4190
+
4191
+ test('flattenObjectHelper', () => {
4192
+ const result = flattenObjectHelper({
4193
+ a : 1,
4194
+ b : {
4195
+ c : 3,
4196
+ d : {
4197
+ e : 5,
4198
+ z : 4,
4199
+ f : { h : 6 },
4200
+ },
4201
+ },
4202
+ })
4203
+ const expected = {
4204
+ a : 1,
4205
+ b : {
4206
+ 'b.c' : 3,
4207
+ 'b.d' : {
4208
+ 'b.d.e' : 5,
4209
+ 'b.d.z' : 4,
4210
+ 'b.d.f' : { 'b.d.f.h' : 6 },
4211
+ },
4212
+ },
4213
+ }
4214
+ expect(result).toEqual(expected)
4215
+ })
4216
+ ```
4217
+
4218
+ </details>
4219
+
4220
+ <details>
4221
+
4222
+ <summary><strong>TypeScript</strong> test</summary>
4223
+
4224
+ ```typescript
4225
+ import { flattenObject, pipe } from 'rambda'
4226
+
4227
+ it('R.flattenObject', () => {
4228
+ const result = pipe({ a: { b: 1, c: 2 } }, flattenObject)
4229
+ result['a.b'] // $ExpectType number
4230
+ result['a.c'] // $ExpectType number
4231
+ // @ts-expect-error
4232
+ result['a.foo']
4233
+ })
4234
+ ```
4235
+
4236
+ </details>
4237
+
4238
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#flattenObject)
4239
+
3910
4240
  ### groupBy
3911
4241
 
3912
4242
  ```typescript
@@ -5196,16 +5526,20 @@ map<T extends IterableContainer, U>(
5196
5526
  <summary><strong>R.map</strong> source</summary>
5197
5527
 
5198
5528
  ```javascript
5529
+ export function mapFn(
5530
+ fn, list
5531
+ ){
5532
+ let index = 0
5533
+ const willReturn = Array(list.length)
5534
+ while (index < list.length) {
5535
+ willReturn[index] = fn(list[index], index)
5536
+ index++
5537
+ }
5538
+ return willReturn
5539
+ }
5540
+
5199
5541
  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
- }
5542
+ return list => mapFn(fn, list)
5209
5543
  }
5210
5544
  ```
5211
5545
 
@@ -6153,66 +6487,53 @@ test('happy', () => {
6153
6487
 
6154
6488
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#minBy)
6155
6489
 
6156
- ### modifyProp
6490
+ ### modifyItemAtIndex
6157
6491
 
6158
6492
  ```typescript
6159
6493
 
6160
- modifyProp<T, K extends keyof T>(
6161
- prop: K,
6162
- fn: (x: T[K]) => T[K],
6163
- ): (target: T) => T
6494
+ modifyItemAtIndex<T>(index: number, replaceFn: (x: T) => T): (list: T[]) => T[]
6164
6495
  ```
6165
6496
 
6166
- It changes a property with the result of transformer function.
6497
+ It replaces `index` in array `list` with the result of `replaceFn(list[i])`.
6167
6498
 
6168
6499
  ```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}
6500
+ const result = R.pipe(
6501
+ [1, 2, 3],
6502
+ R.modifyItemAtIndex(1, R.add(1))
6503
+ ) // => [1, 3, 3]
6175
6504
  ```
6176
6505
 
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>
6506
+ <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
6507
 
6179
6508
  <details>
6180
6509
 
6181
6510
  <summary>All TypeScript definitions</summary>
6182
6511
 
6183
6512
  ```typescript
6184
- modifyProp<T, K extends keyof T>(
6185
- prop: K,
6186
- fn: (x: T[K]) => T[K],
6187
- ): (target: T) => T;
6513
+ modifyItemAtIndex<T>(index: number, replaceFn: (x: T) => T): (list: T[]) => T[];
6188
6514
  ```
6189
6515
 
6190
6516
  </details>
6191
6517
 
6192
6518
  <details>
6193
6519
 
6194
- <summary><strong>R.modifyProp</strong> source</summary>
6520
+ <summary><strong>R.modifyItemAtIndex</strong> source</summary>
6195
6521
 
6196
6522
  ```javascript
6197
- import { isArray } from './_internals/isArray.js'
6198
- import { update } from './update.js'
6523
+ import { cloneList } from './_internals/cloneList.js'
6199
6524
 
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
- }
6525
+ export function modifyItemAtIndex(index, replaceFn) {
6526
+ return list => {
6527
+ const actualIndex = index < 0 ? list.length + index : index
6528
+ if (index >= list.length || actualIndex < 0) {
6529
+ return list
6530
+ }
6207
6531
 
6208
- return {
6209
- ...list,
6210
- [property]: fn(list[property]),
6211
- }
6212
- }
6532
+ const clone = cloneList(list)
6533
+ clone[actualIndex] = replaceFn(clone[actualIndex])
6213
6534
 
6214
- export function modifyProp(property, fn) {
6215
- return obj => modifyFn(property, fn, obj)
6535
+ return clone
6536
+ }
6216
6537
  }
6217
6538
  ```
6218
6539
 
@@ -6223,25 +6544,121 @@ export function modifyProp(property, fn) {
6223
6544
  <summary><strong>Tests</strong></summary>
6224
6545
 
6225
6546
  ```javascript
6226
- import { modifyProp } from './modifyProp.js'
6547
+ import { modifyItemAtIndex } from './modifyItemAtIndex.js'
6227
6548
 
6228
- const person = {
6229
- name: 'foo',
6230
- age: 20,
6231
- }
6549
+ const add10 = x => x + 10
6550
+
6551
+ const list = [0, 1, 2]
6552
+ const expected = [0, 11, 2]
6232
6553
 
6233
6554
  test('happy', () => {
6234
- expect(modifyProp('age', x => x + 1)(person)).toEqual({
6235
- name: 'foo',
6236
- age: 21,
6237
- })
6555
+ expect(modifyItemAtIndex(1, add10)(list)).toEqual(expected)
6238
6556
  })
6239
6557
 
6240
- test('property is missing', () => {
6241
- expect(modifyProp('foo', x => x + 1)(person)).toEqual(person)
6558
+ test('with negative index', () => {
6559
+ expect(modifyItemAtIndex(-2, add10)(list)).toEqual(expected)
6242
6560
  })
6243
6561
 
6244
- test('adjust if `array` at the given key with the `transformation` function', () => {
6562
+ test('when index is out of bounds', () => {
6563
+ const list = [0, 1, 2, 3]
6564
+ expect(modifyItemAtIndex(4, add10)(list)).toEqual(list)
6565
+ expect(modifyItemAtIndex(-5, add10)(list)).toEqual(list)
6566
+ })
6567
+ ```
6568
+
6569
+ </details>
6570
+
6571
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#modifyItemAtIndex)
6572
+
6573
+ ### modifyProp
6574
+
6575
+ ```typescript
6576
+
6577
+ modifyProp<T, K extends keyof T>(
6578
+ prop: K,
6579
+ fn: (x: T[K]) => T[K],
6580
+ ): (target: T) => T
6581
+ ```
6582
+
6583
+ It changes a property with the result of transformer function.
6584
+
6585
+ ```javascript
6586
+ const person = {
6587
+ name : 'foo',
6588
+ age : 20,
6589
+ }
6590
+ const result = R.modifyProp('age', x => x + 1)(person)
6591
+ // => {name: 'foo', age: 21}
6592
+ ```
6593
+
6594
+ <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>
6595
+
6596
+ <details>
6597
+
6598
+ <summary>All TypeScript definitions</summary>
6599
+
6600
+ ```typescript
6601
+ modifyProp<T, K extends keyof T>(
6602
+ prop: K,
6603
+ fn: (x: T[K]) => T[K],
6604
+ ): (target: T) => T;
6605
+ ```
6606
+
6607
+ </details>
6608
+
6609
+ <details>
6610
+
6611
+ <summary><strong>R.modifyProp</strong> source</summary>
6612
+
6613
+ ```javascript
6614
+ import { isArray } from './_internals/isArray.js'
6615
+ import { update } from './update.js'
6616
+
6617
+ function modifyFn(property, fn, list) {
6618
+ if (list[property] === undefined) {
6619
+ return list
6620
+ }
6621
+ if (isArray(list)) {
6622
+ return update(property, fn(list[property]))(list)
6623
+ }
6624
+
6625
+ return {
6626
+ ...list,
6627
+ [property]: fn(list[property]),
6628
+ }
6629
+ }
6630
+
6631
+ export function modifyProp(property, fn) {
6632
+ return obj => modifyFn(property, fn, obj)
6633
+ }
6634
+ ```
6635
+
6636
+ </details>
6637
+
6638
+ <details>
6639
+
6640
+ <summary><strong>Tests</strong></summary>
6641
+
6642
+ ```javascript
6643
+ import { modifyProp } from './modifyProp.js'
6644
+
6645
+ const person = {
6646
+ name: 'foo',
6647
+ age: 20,
6648
+ }
6649
+
6650
+ test('happy', () => {
6651
+ expect(modifyProp('age', x => x + 1)(person)).toEqual({
6652
+ name: 'foo',
6653
+ age: 21,
6654
+ })
6655
+ })
6656
+
6657
+ test('property is missing', () => {
6658
+ expect(modifyProp('foo', x => x + 1)(person)).toEqual(person)
6659
+ })
6660
+
6661
+ test('adjust if `array` at the given key with the `transformation` function', () => {
6245
6662
  expect(modifyProp(1, x => x + 1)([100, 1400])).toEqual([100, 1401])
6246
6663
  })
6247
6664
  ```
@@ -7048,32 +7465,30 @@ path<
7048
7465
  ```javascript
7049
7466
  import { createPath } from './_internals/createPath.js'
7050
7467
 
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
7468
+ export function path(pathInput) {
7469
+ return (obj) => {
7470
+ if (!obj) {
7471
+ return undefined
7472
+ }
7473
+ let willReturn = obj
7474
+ let counter = 0
7475
+
7476
+ const pathArrValue = createPath(pathInput)
7477
+
7478
+ while (counter < pathArrValue.length) {
7479
+ if (willReturn === null || willReturn === undefined) {
7480
+ return undefined
7481
+ }
7482
+ if (willReturn[pathArrValue[counter]] === null) {
7483
+ return undefined
7484
+ }
7485
+
7486
+ willReturn = willReturn[pathArrValue[counter]]
7487
+ counter++
7488
+ }
7489
+
7490
+ return willReturn
7491
+ }
7077
7492
  }
7078
7493
  ```
7079
7494
 
@@ -7089,14 +7504,14 @@ import { path } from './path.js'
7089
7504
  test('with array inside object', () => {
7090
7505
  const obj = { a: { b: [1, { c: 1 }] } }
7091
7506
 
7092
- expect(path('a.b.1.c', obj)).toBe(1)
7507
+ expect(path('a.b.1.c')(obj)).toBe(1)
7093
7508
  })
7094
7509
 
7095
7510
  test('works with undefined', () => {
7096
7511
  const obj = { a: { b: { c: 1 } } }
7097
7512
 
7098
- expect(path('a.b.c.d.f', obj)).toBeUndefined()
7099
- expect(path('foo.babaz', undefined)).toBeUndefined()
7513
+ expect(path('a.b.c.d.f')(obj)).toBeUndefined()
7514
+ expect(path('foo.babaz')(undefined)).toBeUndefined()
7100
7515
  expect(path('foo.babaz')(undefined)).toBeUndefined()
7101
7516
  })
7102
7517
 
@@ -7111,12 +7526,12 @@ test('path', () => {
7111
7526
  })
7112
7527
 
7113
7528
  test('with number string in between', () => {
7114
- expect(path(['a', '1', 'b'], { a: [{ b: 1 }, { b: 2 }] })).toBe(2)
7529
+ expect(path(['a', '1', 'b'])({ a: [{ b: 1 }, { b: 2 }] })).toBe(2)
7115
7530
  })
7116
7531
 
7117
7532
  test('null is not a valid path', () => {
7118
7533
  expect(
7119
- path('audio_tracks', {
7534
+ path('audio_tracks')({
7120
7535
  a: 1,
7121
7536
  audio_tracks: null,
7122
7537
  }),
@@ -7153,6 +7568,131 @@ describe('R.path with string as path', () => {
7153
7568
 
7154
7569
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#path)
7155
7570
 
7571
+ ### pathSatisfies
7572
+
7573
+ ```typescript
7574
+
7575
+ pathSatisfies<S, K0 extends string & keyof S>(
7576
+ predicate: (x: S[K0]) => boolean,
7577
+ path: [K0]
7578
+ ): (obj: S) => boolean
7579
+ ```
7580
+
7581
+ ```javascript
7582
+ const result = R.pathSatisfies(
7583
+ x => x > 0,
7584
+ ['a', 'b', 'c'],
7585
+ {a: {b: {c: 1}}}
7586
+ )
7587
+ // => true
7588
+ ```
7589
+
7590
+ <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>
7591
+
7592
+ <details>
7593
+
7594
+ <summary>All TypeScript definitions</summary>
7595
+
7596
+ ```typescript
7597
+ pathSatisfies<S, K0 extends string & keyof S>(
7598
+ predicate: (x: S[K0]) => boolean,
7599
+ path: [K0]
7600
+ ): (obj: S) => boolean;
7601
+ pathSatisfies<S, K0 extends string & keyof S>(
7602
+ predicate: (x: S[K0]) => boolean,
7603
+ path: `${K0}`
7604
+ ): (obj: S) => boolean;
7605
+ pathSatisfies<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
7606
+ predicate: (x: S[K0][K1]) => boolean,
7607
+ path: [K0, K1]
7608
+ ): (obj: S) => boolean;
7609
+ pathSatisfies<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
7610
+ predicate: (x: S[K0][K1]) => boolean,
7611
+ path: `${K0}.${K1}`
7612
+ ): (obj: S) => boolean;
7613
+ ...
7614
+ ...
7615
+ ```
7616
+
7617
+ </details>
7618
+
7619
+ <details>
7620
+
7621
+ <summary><strong>R.pathSatisfies</strong> source</summary>
7622
+
7623
+ ```javascript
7624
+ import { path } from './path.js'
7625
+
7626
+ export function pathSatisfies(fn, pathInput) {
7627
+ return obj => Boolean(fn(path(pathInput)(obj)))
7628
+ }
7629
+ ```
7630
+
7631
+ </details>
7632
+
7633
+ <details>
7634
+
7635
+ <summary><strong>Tests</strong></summary>
7636
+
7637
+ ```javascript
7638
+ import { pathSatisfies } from './pathSatisfies.js'
7639
+
7640
+ const isPositive = n => n > 0
7641
+
7642
+ it('returns true if the specified object path satisfies the given predicate', () => {
7643
+ expect(pathSatisfies(isPositive, ['x', 'y'])({ x: { y: 1 } })).toBe(true)
7644
+ })
7645
+
7646
+ it('returns false if the specified path does not exist', () => {
7647
+ expect(pathSatisfies(isPositive, ['x', 'y'])({ x: { z: 42 } })).toBe(false)
7648
+ expect(pathSatisfies(isPositive, 'x.y')({ x: { z: 42 } })).toBe(false)
7649
+ })
7650
+
7651
+ it('returns false otherwise', () => {
7652
+ expect(pathSatisfies(isPositive, ['x', 'y'])({ x: { y: 0 } })).toBe(false)
7653
+ })
7654
+ ```
7655
+
7656
+ </details>
7657
+
7658
+ <details>
7659
+
7660
+ <summary><strong>TypeScript</strong> test</summary>
7661
+
7662
+ ```typescript
7663
+ import { pathSatisfies, pipe } from 'rambda'
7664
+
7665
+ const input = { a: { b: { c: 'bar' } } }
7666
+
7667
+ describe('R.pathSatisfies', () => {
7668
+ it('happy', () => {
7669
+ const result = pipe(
7670
+ input,
7671
+ pathSatisfies(
7672
+ x => {
7673
+ x // $ExpectType string
7674
+ return x !== 'foo'
7675
+ },
7676
+ ['a', 'b', 'c'],
7677
+ ),
7678
+ )
7679
+ const resultStringInput = pipe(
7680
+ input,
7681
+ pathSatisfies(x => {
7682
+ x // $ExpectType string
7683
+ return x !== 'foo'
7684
+ }, 'a.b.c'),
7685
+ )
7686
+ result // $ExpectType boolean
7687
+ resultStringInput // $ExpectType boolean
7688
+ })
7689
+ })
7690
+ ```
7691
+
7692
+ </details>
7693
+
7694
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#pathSatisfies)
7695
+
7156
7696
  ### permutations
7157
7697
 
7158
7698
  ```typescript
@@ -8265,7 +8805,7 @@ export function propOr(defaultValue, property) {
8265
8805
  return defaultValue
8266
8806
  }
8267
8807
 
8268
- return defaultTo(defaultValue, obj[property])
8808
+ return defaultTo(defaultValue)(obj[property])
8269
8809
  }
8270
8810
  }
8271
8811
  ```
@@ -8409,14 +8949,15 @@ describe('R.propSatisfies', () => {
8409
8949
  range(startInclusive: number): (endExclusive: number) => number[]
8410
8950
  ```
8411
8951
 
8412
- It returns list of numbers between `startInclusive` to `endInclusive` markers.
8952
+ It returns list of numbers between `startInclusive` to `endExclusive` markers.
8953
+ If `start` is greater than `end`, then the result will be in descending order.
8413
8954
 
8414
8955
  ```javascript
8415
- R.range(0)(5)
8416
- // => [0, 1, 2, 3, 4, 5]
8956
+ [R.range(0)(5), R.range(5)(0)]
8957
+ // => [[0, 1, 2, 3, 4], [5, 4, 3, 2, 1]]
8417
8958
  ```
8418
8959
 
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>
8960
+ <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
8961
 
8421
8962
  <details>
8422
8963
 
@@ -8433,42 +8974,33 @@ range(startInclusive: number): (endExclusive: number) => number[];
8433
8974
  <summary><strong>R.range</strong> source</summary>
8434
8975
 
8435
8976
  ```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
- }
8441
-
8442
- if (end <= start) {
8443
- return []
8444
- }
8445
-
8446
- const len = end - start
8447
- const willReturn = Array(len)
8977
+ function rangeDescending(start, end) {
8978
+ const len = start - end
8979
+ const willReturn = Array(len)
8448
8980
 
8449
- for (let i = 0; i < len + 1; i++) {
8450
- willReturn[i] = start + i
8451
- }
8981
+ for (let i = 0; i < len; i++) {
8982
+ willReturn[i] = start - i
8983
+ }
8452
8984
 
8453
- return willReturn
8454
- }
8985
+ return willReturn
8455
8986
  }
8456
8987
 
8457
- export function rangeDescending(start) {
8988
+ export function range(start) {
8458
8989
  return end => {
8459
8990
  if (Number.isNaN(Number(start)) || Number.isNaN(Number(end))) {
8460
8991
  throw new TypeError('Both arguments to range must be numbers')
8461
8992
  }
8462
8993
 
8463
- if (end >= start) {
8994
+ if (end === start) {
8464
8995
  return []
8465
8996
  }
8997
+ if (end < start) return rangeDescending(start,end)
8466
8998
 
8467
- const len = start - end
8999
+ const len = end - start
8468
9000
  const willReturn = Array(len)
8469
9001
 
8470
- for (let i = 0; i < len + 1; i++) {
8471
- willReturn[i] = start - i
9002
+ for (let i = 0; i < len; i++) {
9003
+ willReturn[i] = start + i
8472
9004
  }
8473
9005
 
8474
9006
  return willReturn
@@ -8483,24 +9015,12 @@ export function rangeDescending(start) {
8483
9015
  <summary><strong>Tests</strong></summary>
8484
9016
 
8485
9017
  ```javascript
8486
- import { range, rangeDescending } from './range.js'
9018
+ import { range } from './range.js'
8487
9019
 
8488
9020
  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([])
9021
+ expect(range(0)(5)).toEqual([0, 1, 2, 3, 4])
9022
+ expect(range(7)(3)).toEqual([7, 6, 5, 4])
9023
+ expect(range(5)(5)).toEqual([])
8504
9024
  })
8505
9025
  ```
8506
9026
 
@@ -8526,34 +9046,6 @@ describe('R.range', () => {
8526
9046
 
8527
9047
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#range)
8528
9048
 
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
9049
  ### reduce
8558
9050
 
8559
9051
  ```typescript
@@ -9026,89 +9518,6 @@ describe('R.replace', () => {
9026
9518
 
9027
9519
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#replace)
9028
9520
 
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
9521
  ### shuffle
9113
9522
 
9114
9523
  ```typescript
@@ -9306,7 +9715,7 @@ const list = [
9306
9715
  ]
9307
9716
  const sortFn = x => x.a
9308
9717
 
9309
- const result = R.sortBy(sortFn, list)
9718
+ const result = R.sortBy(sortFn)(list)
9310
9719
  const expected = [
9311
9720
  {a: 1},
9312
9721
  {a: 2},
@@ -9315,7 +9724,7 @@ const expected = [
9315
9724
  // => `result` is equal to `expected`
9316
9725
  ```
9317
9726
 
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>
9727
+ <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
9728
 
9320
9729
  <details>
9321
9730
 
@@ -9334,21 +9743,30 @@ sortBy<T>(sortFn: (x: T) => Ord): (list: T[]) => T[];
9334
9743
  ```javascript
9335
9744
  import { cloneList } from './_internals/cloneList.js'
9336
9745
 
9337
- export function sortBy(sortFn) {
9338
- return list => {
9339
- const clone = cloneList(list)
9746
+ export function sortByFn (
9747
+ sortFn,
9748
+ list,
9749
+ descending
9750
+ ){
9751
+ const clone = cloneList(list)
9340
9752
 
9341
- return clone.sort((a, b) => {
9342
- const aSortResult = sortFn(a)
9343
- const bSortResult = sortFn(b)
9753
+ return clone.sort((a, b) => {
9754
+ const aSortResult = sortFn(a)
9755
+ const bSortResult = sortFn(b)
9344
9756
 
9345
- if (aSortResult === bSortResult) {
9346
- return 0
9347
- }
9757
+ if (aSortResult === bSortResult) {
9758
+ return 0
9759
+ }
9760
+ if(
9761
+ descending
9762
+ ) return aSortResult > bSortResult ? -1 : 1
9348
9763
 
9349
- return aSortResult < bSortResult ? -1 : 1
9350
- })
9351
- }
9764
+ return aSortResult < bSortResult ? -1 : 1
9765
+ })
9766
+ }
9767
+
9768
+ export function sortBy(sortFn) {
9769
+ return list => sortByFn(sortFn, list, false)
9352
9770
  }
9353
9771
  ```
9354
9772
 
@@ -9361,13 +9779,18 @@ export function sortBy(sortFn) {
9361
9779
  ```javascript
9362
9780
  import { sortBy } from './sortBy.js'
9363
9781
 
9782
+ const input = [{ a: 2 }, { a: 1 }, { a: 1 }, { a: 3 }]
9783
+
9364
9784
  test('happy', () => {
9365
- const input = [{ a: 2 }, { a: 1 }, { a: 1 }, { a: 3 }]
9366
9785
  const expected = [{ a: 1 }, { a: 1 }, { a: 2 }, { a: 3 }]
9367
9786
 
9368
9787
  const result = sortBy(x => x.a)(input)
9369
9788
  expect(result).toEqual(expected)
9370
9789
  })
9790
+
9791
+ test('with non-existing path', () => {
9792
+ expect(sortBy(x => x.b)(input)).toEqual(input)
9793
+ })
9371
9794
  ```
9372
9795
 
9373
9796
  </details>
@@ -9397,6 +9820,215 @@ describe('R.sortBy', () => {
9397
9820
 
9398
9821
  [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortBy)
9399
9822
 
9823
+ ### sortByDescending
9824
+
9825
+ ```typescript
9826
+
9827
+ sortByDescending<T>(sortFn: (a: T, b: T) => number): (list: T[]) => T[]
9828
+ ```
9829
+
9830
+ <details>
9831
+
9832
+ <summary>All TypeScript definitions</summary>
9833
+
9834
+ ```typescript
9835
+ sortByDescending<T>(sortFn: (a: T, b: T) => number): (list: T[]) => T[];
9836
+ ```
9837
+
9838
+ </details>
9839
+
9840
+ <details>
9841
+
9842
+ <summary><strong>R.sortByDescending</strong> source</summary>
9843
+
9844
+ ```javascript
9845
+ import { sortByFn } from "./sortBy.js";
9846
+
9847
+ export function sortByDescending(sortFn) {
9848
+ return list => sortByFn(sortFn, list, true)
9849
+ }
9850
+ ```
9851
+
9852
+ </details>
9853
+
9854
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortByDescending)
9855
+
9856
+ ### sortByPath
9857
+
9858
+ ```typescript
9859
+
9860
+ sortByPath<S, K0 extends string & keyof S>(
9861
+ path: [K0]
9862
+ ): (list: S[]) => S[]
9863
+ ```
9864
+
9865
+ It sorts `list` by the value of `path` property.
9866
+
9867
+ <details>
9868
+
9869
+ <summary>All TypeScript definitions</summary>
9870
+
9871
+ ```typescript
9872
+ sortByPath<S, K0 extends string & keyof S>(
9873
+ path: [K0]
9874
+ ): (list: S[]) => S[];
9875
+ sortByPath<S, K0 extends string & keyof S>(
9876
+ path: `${K0}`
9877
+ ): (list: S[]) => S[];
9878
+ sortByPath<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
9879
+ path: [K0, K1]
9880
+ ): (list: S[]) => S[];
9881
+ sortByPath<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
9882
+ path: `${K0}.${K1}`
9883
+ ): (list: S[]) => S[];
9884
+ ...
9885
+ ...
9886
+ ```
9887
+
9888
+ </details>
9889
+
9890
+ <details>
9891
+
9892
+ <summary><strong>R.sortByPath</strong> source</summary>
9893
+
9894
+ ```javascript
9895
+ import { path } from './path.js'
9896
+ import { sortBy } from './sortBy.js'
9897
+
9898
+ export function sortByPath(sortPath) {
9899
+ return list => sortBy(path(sortPath))(list)
9900
+ }
9901
+ ```
9902
+
9903
+ </details>
9904
+
9905
+ <details>
9906
+
9907
+ <summary><strong>Tests</strong></summary>
9908
+
9909
+ ```javascript
9910
+ import { sortByPath } from './sortByPath.js'
9911
+
9912
+ const list = [{ a: { b: 3 } }, { a: { b: 1 } }, { a: { b: 2 } }]
9913
+ const sorted = [{ a: { b: 1 } }, { a: { b: 2 } }, { a: { b: 3 } }]
9914
+
9915
+ test('with string as path', () => {
9916
+ expect(sortByPath('a.b')(list)).toEqual(sorted)
9917
+ })
9918
+
9919
+ test('with list of strings as path', () => {
9920
+ expect(sortByPath(['a', 'b'])(list)).toEqual(sorted)
9921
+ })
9922
+
9923
+ test('when path is not found in any item', () => {
9924
+ const list = [{ a: { b: 3 } }, { a: { b: 1 } }, { a: {} }]
9925
+ expect(sortByPath('a.b.c.d')(list)).toEqual(list)
9926
+ })
9927
+ ```
9928
+
9929
+ </details>
9930
+
9931
+ <details>
9932
+
9933
+ <summary><strong>TypeScript</strong> test</summary>
9934
+
9935
+ ```typescript
9936
+ import { pipe, sortByPath } from 'rambda'
9937
+
9938
+ const input= [{ a: { b: 2 } }, { a: { b: 1 } }]
9939
+
9940
+ describe('R.sortByPath', () => {
9941
+ it('with string as path', () => {
9942
+ const result = pipe(input, sortByPath('a.b'))
9943
+ result[0].a.b // $ExpectType number
9944
+ })
9945
+ it('with list of strings as path', () => {
9946
+ const result = pipe(input, sortByPath(['a', 'b']))
9947
+ result[0].a.b // $ExpectType number
9948
+ })
9949
+ it('with non-existent path', () => {
9950
+ // @ts-expect-error
9951
+ pipe(input, sortByPath(['a', 'c']))
9952
+ // @ts-expect-error
9953
+ pipe(input, sortByPath('a.c'))
9954
+ })
9955
+ })
9956
+ ```
9957
+
9958
+ </details>
9959
+
9960
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortByPath)
9961
+
9962
+ ### sortByPathDescending
9963
+
9964
+ ```typescript
9965
+
9966
+ sortByPathDescending<S, K0 extends string & keyof S>(
9967
+ path: [K0]
9968
+ ): (list: S[]) => S[]
9969
+ ```
9970
+
9971
+ <details>
9972
+
9973
+ <summary>All TypeScript definitions</summary>
9974
+
9975
+ ```typescript
9976
+ sortByPathDescending<S, K0 extends string & keyof S>(
9977
+ path: [K0]
9978
+ ): (list: S[]) => S[];
9979
+ sortByPathDescending<S, K0 extends string & keyof S>(
9980
+ path: `${K0}`
9981
+ ): (list: S[]) => S[];
9982
+ sortByPathDescending<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
9983
+ path: [K0, K1]
9984
+ ): (list: S[]) => S[];
9985
+ sortByPathDescending<S, K0 extends string & keyof S, K1 extends string & keyof S[K0]>(
9986
+ path: `${K0}.${K1}`
9987
+ ): (list: S[]) => S[];
9988
+ ...
9989
+ ...
9990
+ ```
9991
+
9992
+ </details>
9993
+
9994
+ <details>
9995
+
9996
+ <summary><strong>R.sortByPathDescending</strong> source</summary>
9997
+
9998
+ ```javascript
9999
+ import { path } from './path.js'
10000
+ import { sortByDescending } from './sortByDescending.js'
10001
+
10002
+ export function sortByPathDescending(sortPath) {
10003
+ return list => sortByDescending(path(sortPath))(list)
10004
+ }
10005
+ ```
10006
+
10007
+ </details>
10008
+
10009
+ <details>
10010
+
10011
+ <summary><strong>Tests</strong></summary>
10012
+
10013
+ ```javascript
10014
+ import { sortByPathDescending } from './sortByPathDescending.js'
10015
+
10016
+ const list = [{ a: { b: 3 } }, { a: { b: 1 } }, { a: { b: 2 } }]
10017
+ const sorted = [{ a: { b: 3 } }, { a: { b: 2 } }, { a: { b: 1 } }]
10018
+
10019
+ test('with string as path', () => {
10020
+ expect(sortByPathDescending('a.b')(list)).toEqual(sorted)
10021
+ })
10022
+
10023
+ test('with list of strings as path', () => {
10024
+ expect(sortByPathDescending(['a', 'b'])(list)).toEqual(sorted)
10025
+ })
10026
+ ```
10027
+
10028
+ </details>
10029
+
10030
+ [![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#sortByPathDescending)
10031
+
9400
10032
  ### sortObject
9401
10033
 
9402
10034
  ```typescript
@@ -11532,7 +12164,7 @@ describe('R.unless', () => {
11532
12164
 
11533
12165
  ```typescript
11534
12166
 
11535
- unwind<S extends string>(prop: S): <T>(obj: T) => Omit<T, S> & { [K in S]: T[S][number] }
12167
+ unwind<S extends string>(prop: S): <T>(obj: T) => MergeTypes<Omit<T, S> & { [K in S]: T[S][number] }>
11536
12168
  ```
11537
12169
 
11538
12170
  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 +12186,7 @@ const expected = [{a:1, b:2}, {a:1, b:3}]
11554
12186
  <summary>All TypeScript definitions</summary>
11555
12187
 
11556
12188
  ```typescript
11557
- unwind<S extends string>(prop: S): <T>(obj: T) => Omit<T, S> & { [K in S]: T[S][number] };
12189
+ unwind<S extends string>(prop: S): <T>(obj: T) => MergeTypes<Omit<T, S> & { [K in S]: T[S][number] }>;
11558
12190
  ```
11559
12191
 
11560
12192
  </details>
@@ -11732,7 +12364,7 @@ test('with negative index', () => {
11732
12364
 
11733
12365
  ```typescript
11734
12366
 
11735
- when<T>(predicate: (x: T) => boolean, whenTrueFn: (x: T) => T): (input: T) => T
12367
+ when<T, U extends T>(predicate: (x: T) => x is U, whenTrueFn: (x: U) => T): (input: T) => T
11736
12368
  ```
11737
12369
 
11738
12370
  It pass `input` to `predicate` function and if the result is `true`, it will return the result of `whenTrueFn(input)`.
@@ -11766,6 +12398,7 @@ const expected = [
11766
12398
  <summary>All TypeScript definitions</summary>
11767
12399
 
11768
12400
  ```typescript
12401
+ when<T, U extends T>(predicate: (x: T) => x is U, whenTrueFn: (x: U) => T): (input: T) => T;
11769
12402
  when<T>(predicate: (x: T) => boolean, whenTrueFn: (x: T) => T): (input: T) => T;
11770
12403
  when<T, U>(predicate: (x: T) => boolean, whenTrueFn: (x: T) => U): (input: T) => T | U;
11771
12404
  ```
@@ -11813,7 +12446,11 @@ test('happy', () => {
11813
12446
  <summary><strong>TypeScript</strong> test</summary>
11814
12447
 
11815
12448
  ```typescript
11816
- import { pipe, tap, when } from 'rambda'
12449
+ import { head, pipe, tap, when } from 'rambda'
12450
+
12451
+ function notNull<T>(a: T | null | undefined): a is T {
12452
+ return a != null
12453
+ }
11817
12454
 
11818
12455
  describe('R.when', () => {
11819
12456
  it('happy', () => {
@@ -11833,6 +12470,15 @@ describe('R.when', () => {
11833
12470
  )
11834
12471
 
11835
12472
  result // $ExpectType string | number
12473
+ })
12474
+
12475
+ it('with assertion of type', () => {
12476
+ const result = pipe(
12477
+ [1, null, 2, 3],
12478
+ head,
12479
+ when(notNull, x => x + 1),
12480
+ )
12481
+ result // $ExpectType number | null
11836
12482
  })
11837
12483
  })
11838
12484
  ```
@@ -12153,14 +12799,20 @@ This is major revamp of `Rambda` library:
12153
12799
  -- R.rejectObject
12154
12800
  -- R.findNth
12155
12801
  -- R.combinations
12156
- -- R.rangeDescending
12802
+ -- R.sortByPath
12803
+ -- R.sortByPathDescending
12804
+ -- R.sortByDescending
12805
+ -- R.flattenObject
12806
+ -- R.addPropToObjects
12157
12807
 
12158
12808
  - Rename following methods:
12159
12809
 
12160
- -- replaceItemAtIndex -> adjust
12810
+ -- modifyItemAtIndex -> adjust
12161
12811
  -- checkObjectWithSpec -> where
12162
12812
  -- objectIncludes -> whereEq
12163
12813
  -- modify -> modifyProp
12814
+ -- chain -> flatMap
12815
+ -- mapObjIndexed -> mapObject
12164
12816
 
12165
12817
  _ 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
12818
 
@@ -12188,7 +12840,7 @@ _ Regarding using object as input with TypeScript in methods such as `R.map/filt
12188
12840
 
12189
12841
  -- append/prepend
12190
12842
 
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.
12843
+ - Change `R.range` to work with descending order.
12192
12844
 
12193
12845
  - Sync with typing of `@types/ramda`:
12194
12846
 
@@ -12241,11 +12893,6 @@ _ Regarding using object as input with TypeScript in methods such as `R.map/filt
12241
12893
 
12242
12894
  - Revert adding stopper logic in `R.reduce` - https://github.com/selfrefactor/rambda/pull/630
12243
12895
 
12244
- - Renamed methods:
12245
-
12246
- -- `chain` to `flatMap`
12247
- -- `mapObjIndexed` to `mapObject`
12248
-
12249
12896
  9.4.2
12250
12897
 
12251
12898
  - Fix TS issue when `R.take` is used as part of `R.pipe`.