Skip to content

Commit

Permalink
feat(Object): add mutable set & unset variants
Browse files Browse the repository at this point in the history
  • Loading branch information
Thanga-Ganapathy committed Apr 29, 2024
1 parent 76eccea commit 0d1451e
Show file tree
Hide file tree
Showing 21 changed files with 450 additions and 50 deletions.
5 changes: 5 additions & 0 deletions .changeset/yellow-jobs-tell.md
@@ -0,0 +1,5 @@
---
"@opentf/std": minor
---

Added mutable set & unset variant functions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Expand Up @@ -6,7 +6,7 @@ on:
branches:
- main
paths-ignore:
- "apps/website/**"
- "apps/docs/**"

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

Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -338,7 +338,7 @@ isEql:

- The native util `deepStrictEqual` not available in browsers, does not check `Map` ordering & same invalid dates.
- The `fast-deep-equal/es6` does not support cyclic refs, Map ordering check, invalid dates, symbols, objects values in Set & TypedArrays.
- The lodash `isEqual` does not check `Map` ordering & object values in `Set`.
- The lodash `isEqual` having issues with Map & does not check `Map` ordering & object values in `Set`.
- The ramda `equals` does not check `Map` ordering & symbols.
- The dequal does not support cyclic refs, Map ordering, symbols & same invalid dates.
```
Expand Down
2 changes: 2 additions & 0 deletions apps/docs/pages/Object/_meta.json
Expand Up @@ -12,5 +12,7 @@
"shallowMergeAll": "shallowMergeAll",
"size": "size",
"toPath": "toPath",
"toSet": "toSet",
"toUnset": "toUnset",
"unset": "unset"
}
12 changes: 10 additions & 2 deletions apps/docs/pages/Object/set.mdx
Expand Up @@ -3,10 +3,18 @@ import REPL from "../../components/REPL";

> Sets the value to an object at the given path.
<Callout emoji="" type="info">
Immutable: This does not mutate the original object.
<Callout emoji="⚠️" type="info">
This mutates the original object.
</Callout>

### Related

- [get](/Object/get)
- [has](/Object/has)
- [unset](/Object/unset)
- [toSet](/Object/toSet)
- [toUnset](/Object/toUnset)

## Syntax

```ts
Expand Down
70 changes: 70 additions & 0 deletions apps/docs/pages/Object/toSet.mdx
@@ -0,0 +1,70 @@
import { Callout } from "nextra/components";
import REPL from "../../components/REPL";

> Sets the value to an object at the given path.
<Callout emoji="" type="info">
Immutable: This does not mutate the original object.
</Callout>

### Related

- [get](/Object/get)
- [set](/Object/set)
- [has](/Object/has)
- [unset](/Object/unset)
- [toUnset](/Object/toUnset)

## Syntax

```ts
import { toSet } from '@opentf/std';

toSet<T>(
obj: T,
path: string | unknown[],
value: unknown | ((val: unknown) => unknown)
): T
```
<Callout type="info">
The value param can be either any `value` or `callback` function.
</Callout>

<Callout type="info">
The `callback` fn can be called with the property path value if it exist.
</Callout>

## Examples

```ts
toSet({}, 'a', null) //=> { a: null }

toSet({}, 'a', 1) //=> { a: 1 }

toSet({}, 'a.b', 25) //=> { a: { b: 25 } }

toSet({}, 'user.email', 'user@example.com')
//=>
// {
// user: { email: 'user@example.com' }
// }

toSet({}, '0', 'Apple') //=> { '0': 'Apple' }

toSet({}, 'fruits[0]', 'Apple') //=> { fruits: ['Apple'] }

toSet({ a: 1 }, 'a', (val) => val + 1) //=> { a: 2 }

const fn = () => render('My Component')
toSet({ subscribeFns: [] }, 'subscribeFns[0]', () => fn)
//=> { subscribeFns: [fn] }
```

## Try

<REPL code={`const { toSet } = require('@opentf/std');
log(toSet({}, 'a.b', 25));
toSet({ a: 1 }, 'a', (val) => val + 1)
`} />
60 changes: 60 additions & 0 deletions apps/docs/pages/Object/toUnset.mdx
@@ -0,0 +1,60 @@
import { Callout } from "nextra/components";
import REPL from "../../components/REPL";

> Removes the property of the given object at the given path.
<Callout emoji="" type="info">
Immutable: This does not mutate the original object.
</Callout>

<Callout type="default">
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.
</Callout>

### Related

- [get](/Object/get)
- [has](/Object/has)
- [set](/Object/set)
- [unset](/Object/unset)
- [toSet](/Object/toSet)

## Syntax

```ts
import { toUnset } from '@opentf/std';

toUnset<T>(
obj: T,
path: string | unknown[],
): T
```

## Examples

```ts
const obj = { a: 1, b: 2 };
toUnset(obj, 'a') //=> { b: 2 }

toUnset(obj, 'b') //=> { a: 1 }

toUnset(obj, 'c') //=> { a: 1, b: 2 }

const arr = [1, 2, 3];
toUnset(arr, '0') //=> [2, 3]

toUnset(arr, '1') //=> [1, 3]

toUnset(arr, '3') //=> [1, 2, 3]

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

## Try

<REPL code={`const { toUnset } = require('@opentf/std');
const obj = { a: 1, b: 2 };
toUnset(obj, 'a');
`} />
32 changes: 19 additions & 13 deletions apps/docs/pages/Object/unset.mdx
Expand Up @@ -3,13 +3,18 @@ import REPL from "../../components/REPL";

> Removes the property of the given object at the given path.
<Callout emoji="" type="info">
Immutable: This does not mutate the original object.
<Callout emoji="⚠️" type="info">
This mutates the original object.
</Callout>

<Callout type="default">
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.
</Callout>
### Related

- [get](/Object/get)
- [has](/Object/has)
- [set](/Object/set)
- [toSet](/Object/toSet)
- [toUnset](/Object/toUnset)


## Syntax

Expand All @@ -25,19 +30,20 @@ unset<T>(
## Examples

```ts
const obj = { a: 1, b: 2 };
unset(obj, 'a') //=> { b: 2 }
unset({ a: 1, b: 2 }, 'a') //=> { b: 2 }

unset({ a: 1, b: 2 }, 'b') //=> { a: 1 }

unset({ a: 1, b: 2 }, 'c') //=> { a: 1, b: 2 }

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

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

const arr = [1, 2, 3];
unset(arr, '0') //=> [2, 3]
unset([1, 2, 3], '1') //=> [1, <1 Empty>, 3]

unset(arr, '1') //=> [1, 3]
unset([1, 2, 3], '2') //=> [1, 2, <1 Empty>]

unset(arr, '3') //=> [1, 2, 3]
unset([1, 2, 3], '3') //=> [1, 2, 3]

const nestedObj = { x: { y: { z: ['a', null, 'b'] } } };
unset(nestedObj, 'x.y.z') //=> { x: { y: {} } }
Expand Down
21 changes: 20 additions & 1 deletion apps/docs/pages/index.mdx
Expand Up @@ -53,6 +53,9 @@ Let’s explore some of the library’s capabilities:
import { isNum } from "@opentf/std";

isNum(NaN); //=> false
isNum('1'); //=> false
isNum('1', true); //=> true
isNum(1); //=> true
```

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

isEql({a: 1}, {a: 1}); //=> true

const mapA = new Map([["a", 1], ["b", 2]]);
const mapB = new Map([["b", 2], ["a", 1]]);
isEql(mapA, mapB); //=> false

// Compare Arrays ignoring order
isEqlArr([1, 2, 3], [2, 3, 1]); //=> true
isEqlArr([1, 2, 3], [1, 2, 5]); //=> false
```

6. Adding a Delay (1 second) with sleep:
Expand Down Expand Up @@ -117,6 +124,18 @@ compose(
); //=> 6
```

8. Pick & Omit Paths in an Object

```js
import { pick omit } from '@opentf/std';

const obj = { a: 1, b: 2, c: 3 };

pick(obj, 'a', 'c'); //=> { a: 1, c: 3 }

omit(obj, 'a', 'c'); //=> { b: 2 }
```

<Callout type="default">

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

- The native util `deepStrictEqual` not available in browsers, does not check `Map` ordering & same invalid dates.
- The `fast-deep-equal/es6` does not support cyclic refs, Map ordering check, invalid dates, symbols, objects values in Set & TypedArrays.
- The lodash `isEqual` does not check `Map` ordering & object values in `Set`.
- The lodash `isEqual` having issues with Map & does not check `Map` ordering & object values in `Set`.
- The ramda `equals` does not check `Map` ordering & symbols.
- The dequal does not support cyclic refs, Map ordering, symbols & same invalid dates.
```
Expand Down
4 changes: 2 additions & 2 deletions benchmark.js
Expand Up @@ -172,6 +172,6 @@ async function isEqlBench() {
`);
}

await cloneBench();
// await cloneBench();
// await sortByBench();
// await isEqlBench();
await isEqlBench();
2 changes: 1 addition & 1 deletion packages/std/README.md
Expand Up @@ -338,7 +338,7 @@ isEql:

- The native util `deepStrictEqual` not available in browsers, does not check `Map` ordering & same invalid dates.
- The `fast-deep-equal/es6` does not support cyclic refs, Map ordering check, invalid dates, symbols, objects values in Set & TypedArrays.
- The lodash `isEqual` does not check `Map` ordering & object values in `Set`.
- The lodash `isEqual` having issues with Map & does not check `Map` ordering & object values in `Set`.
- The ramda `equals` does not check `Map` ordering & symbols.
- The dequal does not support cyclic refs, Map ordering, symbols & same invalid dates.
```
Expand Down
55 changes: 55 additions & 0 deletions packages/std/__tests__/object/toSet.spec.ts
@@ -0,0 +1,55 @@
import { toSet } from '../../src';

describe('Object', () => {
test('toSet', () => {
expect(toSet({}, 'a', null)).toEqual({ a: null });

expect(toSet({}, 'a', 1)).toEqual({ a: 1 });

expect(toSet({}, 'a.b', 25)).toEqual({ a: { b: 25 } });

expect(toSet({}, 'user.email', 'user@example.com')).toEqual({
user: { email: 'user@example.com' },
});

const obj = { name: 'x' };
const newObj = toSet(obj, 'name', 'xxx');
expect(newObj.name).toBe('xxx');

expect(toSet({}, '0', 'Apple')).toEqual({
'0': 'Apple',
});

expect(toSet({}, 'fruits[0]', 'Apple')).toEqual({
fruits: ['Apple'],
});

expect(toSet({ fruits: ['Apple'] }, 'fruits[0]', 'Mango')).toEqual({
fruits: ['Mango'],
});

expect(toSet({ fruits: ['Apple'] }, 'fruits[1]', 'Mango')).toEqual({
fruits: ['Apple', 'Mango'],
});

expect(toSet({ a: [{ b: { c: 3 } }] }, 'a[0].b.c', 4)).toEqual({
a: [{ b: { c: 4 } }],
});
});

test('updating values', () => {
expect(toSet({}, 'a', () => 1)).toEqual({ a: 1 });
expect(toSet({ a: 1 }, 'a', (val) => val + 1)).toEqual({ a: 2 });
expect(
toSet({ a: 1, b: [2] }, 'b', (arr) => {
arr.unshift(1);
return arr;
})
).toEqual({
a: 1,
b: [1, 2],
});
const fn = (a, b) => a ** b;
expect(toSet({ a: 1 }, 'b', (val) => fn)).toEqual({ a: 1, b: fn });
});
});

0 comments on commit 0d1451e

Please sign in to comment.