Skip to content

Commit 0d1451e

Browse files
feat(Object): add mutable set & unset variants
1 parent 76eccea commit 0d1451e

File tree

21 files changed

+450
-50
lines changed

21 files changed

+450
-50
lines changed

.changeset/yellow-jobs-tell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opentf/std": minor
3+
---
4+
5+
Added mutable set & unset variant functions.

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
branches:
77
- main
88
paths-ignore:
9-
- "apps/website/**"
9+
- "apps/docs/**"
1010

1111
concurrency: ${{ github.workflow }}-${{ github.ref }}
1212

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ isEql:
338338

339339
- The native util `deepStrictEqual` not available in browsers, does not check `Map` ordering & same invalid dates.
340340
- The `fast-deep-equal/es6` does not support cyclic refs, Map ordering check, invalid dates, symbols, objects values in Set & TypedArrays.
341-
- The lodash `isEqual` does not check `Map` ordering & object values in `Set`.
341+
- The lodash `isEqual` having issues with Map & does not check `Map` ordering & object values in `Set`.
342342
- The ramda `equals` does not check `Map` ordering & symbols.
343343
- The dequal does not support cyclic refs, Map ordering, symbols & same invalid dates.
344344
```

apps/docs/pages/Object/_meta.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@
1212
"shallowMergeAll": "shallowMergeAll",
1313
"size": "size",
1414
"toPath": "toPath",
15+
"toSet": "toSet",
16+
"toUnset": "toUnset",
1517
"unset": "unset"
1618
}

apps/docs/pages/Object/set.mdx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@ import REPL from "../../components/REPL";
33

44
> Sets the value to an object at the given path.
55
6-
<Callout emoji="" type="info">
7-
Immutable: This does not mutate the original object.
6+
<Callout emoji="⚠️" type="info">
7+
This mutates the original object.
88
</Callout>
99

10+
### Related
11+
12+
- [get](/Object/get)
13+
- [has](/Object/has)
14+
- [unset](/Object/unset)
15+
- [toSet](/Object/toSet)
16+
- [toUnset](/Object/toUnset)
17+
1018
## Syntax
1119

1220
```ts

apps/docs/pages/Object/toSet.mdx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Callout } from "nextra/components";
2+
import REPL from "../../components/REPL";
3+
4+
> Sets the value to an object at the given path.
5+
6+
<Callout emoji="" type="info">
7+
Immutable: This does not mutate the original object.
8+
</Callout>
9+
10+
### Related
11+
12+
- [get](/Object/get)
13+
- [set](/Object/set)
14+
- [has](/Object/has)
15+
- [unset](/Object/unset)
16+
- [toUnset](/Object/toUnset)
17+
18+
## Syntax
19+
20+
```ts
21+
import { toSet } from '@opentf/std';
22+
23+
toSet<T>(
24+
obj: T,
25+
path: string | unknown[],
26+
value: unknown | ((val: unknown) => unknown)
27+
): T
28+
```
29+
<Callout type="info">
30+
The value param can be either any `value` or `callback` function.
31+
</Callout>
32+
33+
<Callout type="info">
34+
The `callback` fn can be called with the property path value if it exist.
35+
</Callout>
36+
37+
## Examples
38+
39+
```ts
40+
toSet({}, 'a', null) //=> { a: null }
41+
42+
toSet({}, 'a', 1) //=> { a: 1 }
43+
44+
toSet({}, 'a.b', 25) //=> { a: { b: 25 } }
45+
46+
toSet({}, 'user.email', '[email protected]')
47+
//=>
48+
// {
49+
// user: { email: '[email protected]' }
50+
// }
51+
52+
toSet({}, '0', 'Apple') //=> { '0': 'Apple' }
53+
54+
toSet({}, 'fruits[0]', 'Apple') //=> { fruits: ['Apple'] }
55+
56+
toSet({ a: 1 }, 'a', (val) => val + 1) //=> { a: 2 }
57+
58+
const fn = () => render('My Component')
59+
toSet({ subscribeFns: [] }, 'subscribeFns[0]', () => fn)
60+
//=> { subscribeFns: [fn] }
61+
```
62+
63+
## Try
64+
65+
<REPL code={`const { toSet } = require('@opentf/std');
66+
67+
log(toSet({}, 'a.b', 25));
68+
69+
toSet({ a: 1 }, 'a', (val) => val + 1)
70+
`} />

apps/docs/pages/Object/toUnset.mdx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Callout } from "nextra/components";
2+
import REPL from "../../components/REPL";
3+
4+
> Removes the property of the given object at the given path.
5+
6+
<Callout emoji="" type="info">
7+
Immutable: This does not mutate the original object.
8+
</Callout>
9+
10+
<Callout type="default">
11+
If an array value is removed, then it will not return a [sparsed](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#sparse_arrays) array.
12+
</Callout>
13+
14+
### Related
15+
16+
- [get](/Object/get)
17+
- [has](/Object/has)
18+
- [set](/Object/set)
19+
- [unset](/Object/unset)
20+
- [toSet](/Object/toSet)
21+
22+
## Syntax
23+
24+
```ts
25+
import { toUnset } from '@opentf/std';
26+
27+
toUnset<T>(
28+
obj: T,
29+
path: string | unknown[],
30+
): T
31+
```
32+
33+
## Examples
34+
35+
```ts
36+
const obj = { a: 1, b: 2 };
37+
toUnset(obj, 'a') //=> { b: 2 }
38+
39+
toUnset(obj, 'b') //=> { a: 1 }
40+
41+
toUnset(obj, 'c') //=> { a: 1, b: 2 }
42+
43+
const arr = [1, 2, 3];
44+
toUnset(arr, '0') //=> [2, 3]
45+
46+
toUnset(arr, '1') //=> [1, 3]
47+
48+
toUnset(arr, '3') //=> [1, 2, 3]
49+
50+
const nestedObj = { x: { y: { z: ['a', null, 'b'] } } };
51+
toUnset(nestedObj, 'x.y.z') //=> { x: { y: {} } }
52+
```
53+
54+
## Try
55+
56+
<REPL code={`const { toUnset } = require('@opentf/std');
57+
58+
const obj = { a: 1, b: 2 };
59+
toUnset(obj, 'a');
60+
`} />

apps/docs/pages/Object/unset.mdx

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@ import REPL from "../../components/REPL";
33

44
> Removes the property of the given object at the given path.
55
6-
<Callout emoji="" type="info">
7-
Immutable: This does not mutate the original object.
6+
<Callout emoji="⚠️" type="info">
7+
This mutates the original object.
88
</Callout>
99

10-
<Callout type="default">
11-
If an array value is removed, then it will not return a [sparsed](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#sparse_arrays) array.
12-
</Callout>
10+
### Related
11+
12+
- [get](/Object/get)
13+
- [has](/Object/has)
14+
- [set](/Object/set)
15+
- [toSet](/Object/toSet)
16+
- [toUnset](/Object/toUnset)
17+
1318

1419
## Syntax
1520

@@ -25,19 +30,20 @@ unset<T>(
2530
## Examples
2631

2732
```ts
28-
const obj = { a: 1, b: 2 };
29-
unset(obj, 'a') //=> { b: 2 }
33+
unset({ a: 1, b: 2 }, 'a') //=> { b: 2 }
34+
35+
unset({ a: 1, b: 2 }, 'b') //=> { a: 1 }
36+
37+
unset({ a: 1, b: 2 }, 'c') //=> { a: 1, b: 2 }
3038

31-
unset(obj, 'b') //=> { a: 1 }
3239

33-
unset(obj, 'c') //=> { a: 1, b: 2 }
40+
unset([1, 2, 3], '0') //=> [<1 Empty>, 2, 3]
3441

35-
const arr = [1, 2, 3];
36-
unset(arr, '0') //=> [2, 3]
42+
unset([1, 2, 3], '1') //=> [1, <1 Empty>, 3]
3743

38-
unset(arr, '1') //=> [1, 3]
44+
unset([1, 2, 3], '2') //=> [1, 2, <1 Empty>]
3945

40-
unset(arr, '3') //=> [1, 2, 3]
46+
unset([1, 2, 3], '3') //=> [1, 2, 3]
4147

4248
const nestedObj = { x: { y: { z: ['a', null, 'b'] } } };
4349
unset(nestedObj, 'x.y.z') //=> { x: { y: {} } }

apps/docs/pages/index.mdx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ Let’s explore some of the library’s capabilities:
5353
import { isNum } from "@opentf/std";
5454

5555
isNum(NaN); //=> false
56+
isNum('1'); //=> false
57+
isNum('1', true); //=> true
58+
isNum(1); //=> true
5659
```
5760

5861
2. Converting Strings to Pascal Case:
@@ -84,11 +87,15 @@ clone(obj); // Returns deeply cloned value
8487
```js
8588
import { isEql, isEqlArr } from "@opentf/std";
8689

90+
isEql({a: 1}, {a: 1}); //=> true
91+
8792
const mapA = new Map([["a", 1], ["b", 2]]);
8893
const mapB = new Map([["b", 2], ["a", 1]]);
8994
isEql(mapA, mapB); //=> false
9095

96+
// Compare Arrays ignoring order
9197
isEqlArr([1, 2, 3], [2, 3, 1]); //=> true
98+
isEqlArr([1, 2, 3], [1, 2, 5]); //=> false
9299
```
93100

94101
6. Adding a Delay (1 second) with sleep:
@@ -117,6 +124,18 @@ compose(
117124
); //=> 6
118125
```
119126

127+
8. Pick & Omit Paths in an Object
128+
129+
```js
130+
import { pick omit } from '@opentf/std';
131+
132+
const obj = { a: 1, b: 2, c: 3 };
133+
134+
pick(obj, 'a', 'c'); //=> { a: 1, c: 3 }
135+
136+
omit(obj, 'a', 'c'); //=> { b: 2 }
137+
```
138+
120139
<Callout type="default">
121140

122141
You can try out these examples on the [Playground](/playground).
@@ -183,7 +202,7 @@ isEql:
183202

184203
- The native util `deepStrictEqual` not available in browsers, does not check `Map` ordering & same invalid dates.
185204
- The `fast-deep-equal/es6` does not support cyclic refs, Map ordering check, invalid dates, symbols, objects values in Set & TypedArrays.
186-
- The lodash `isEqual` does not check `Map` ordering & object values in `Set`.
205+
- The lodash `isEqual` having issues with Map & does not check `Map` ordering & object values in `Set`.
187206
- The ramda `equals` does not check `Map` ordering & symbols.
188207
- The dequal does not support cyclic refs, Map ordering, symbols & same invalid dates.
189208
```

benchmark.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,6 @@ async function isEqlBench() {
172172
`);
173173
}
174174

175-
await cloneBench();
175+
// await cloneBench();
176176
// await sortByBench();
177-
// await isEqlBench();
177+
await isEqlBench();

packages/std/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ isEql:
338338

339339
- The native util `deepStrictEqual` not available in browsers, does not check `Map` ordering & same invalid dates.
340340
- The `fast-deep-equal/es6` does not support cyclic refs, Map ordering check, invalid dates, symbols, objects values in Set & TypedArrays.
341-
- The lodash `isEqual` does not check `Map` ordering & object values in `Set`.
341+
- The lodash `isEqual` having issues with Map & does not check `Map` ordering & object values in `Set`.
342342
- The ramda `equals` does not check `Map` ordering & symbols.
343343
- The dequal does not support cyclic refs, Map ordering, symbols & same invalid dates.
344344
```
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { toSet } from '../../src';
2+
3+
describe('Object', () => {
4+
test('toSet', () => {
5+
expect(toSet({}, 'a', null)).toEqual({ a: null });
6+
7+
expect(toSet({}, 'a', 1)).toEqual({ a: 1 });
8+
9+
expect(toSet({}, 'a.b', 25)).toEqual({ a: { b: 25 } });
10+
11+
expect(toSet({}, 'user.email', '[email protected]')).toEqual({
12+
user: { email: '[email protected]' },
13+
});
14+
15+
const obj = { name: 'x' };
16+
const newObj = toSet(obj, 'name', 'xxx');
17+
expect(newObj.name).toBe('xxx');
18+
19+
expect(toSet({}, '0', 'Apple')).toEqual({
20+
'0': 'Apple',
21+
});
22+
23+
expect(toSet({}, 'fruits[0]', 'Apple')).toEqual({
24+
fruits: ['Apple'],
25+
});
26+
27+
expect(toSet({ fruits: ['Apple'] }, 'fruits[0]', 'Mango')).toEqual({
28+
fruits: ['Mango'],
29+
});
30+
31+
expect(toSet({ fruits: ['Apple'] }, 'fruits[1]', 'Mango')).toEqual({
32+
fruits: ['Apple', 'Mango'],
33+
});
34+
35+
expect(toSet({ a: [{ b: { c: 3 } }] }, 'a[0].b.c', 4)).toEqual({
36+
a: [{ b: { c: 4 } }],
37+
});
38+
});
39+
40+
test('updating values', () => {
41+
expect(toSet({}, 'a', () => 1)).toEqual({ a: 1 });
42+
expect(toSet({ a: 1 }, 'a', (val) => val + 1)).toEqual({ a: 2 });
43+
expect(
44+
toSet({ a: 1, b: [2] }, 'b', (arr) => {
45+
arr.unshift(1);
46+
return arr;
47+
})
48+
).toEqual({
49+
a: 1,
50+
b: [1, 2],
51+
});
52+
const fn = (a, b) => a ** b;
53+
expect(toSet({ a: 1 }, 'b', (val) => fn)).toEqual({ a: 1, b: fn });
54+
});
55+
});

0 commit comments

Comments
 (0)