Skip to content

Commit bda95c9

Browse files
committed
preparing to use immer
1 parent f230da2 commit bda95c9

File tree

6 files changed

+156
-0
lines changed

6 files changed

+156
-0
lines changed

dashi/package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dashi/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"preview": "vite preview"
1313
},
1414
"dependencies": {
15+
"immer": "^10.1.1",
1516
"plotly.js": "^2.35.2",
1617
"react": "^18.3.1",
1718
"react-dom": "^18.3.1",

dashi/src/utils/get.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { expect, test } from "vitest";
2+
import { get } from "./get.js";
3+
4+
test("get from flat object", () => {
5+
const obj = { a: 1, b: 2 };
6+
expect(get(obj, "a")).toBe(1);
7+
expect(get(obj, "b")).toBe(2);
8+
expect(get(obj, "c")).toBeUndefined();
9+
});
10+
11+
test("get from nested object", () => {
12+
const obj = { a: { b: 2, c: "X" } };
13+
expect(get(obj, "a.b")).toBe(2);
14+
expect(get(obj, "a.c")).toBe("X");
15+
expect(get(obj, "a.d")).toBeUndefined();
16+
});
17+
18+
test("get from array", () => {
19+
const arr = [1, 2];
20+
expect(get(arr, "0")).toBe(1);
21+
expect(get(arr, "1")).toBe(2);
22+
expect(get(arr, "2")).toBeUndefined();
23+
});
24+
25+
test("get from array of objects", () => {
26+
const arr = [{ a: 1, b: 2 }, 2];
27+
expect(get(arr, "0.a")).toBe(1);
28+
expect(get(arr, "0.b")).toBe(2);
29+
expect(get(arr, "0.c")).toBeUndefined();
30+
expect(get(arr, "1")).toBe(2);
31+
expect(get(arr, "2")).toBeUndefined();
32+
});
33+
34+
test("get from object with array of objects with array", () => {
35+
const arr = { a: [{ b: [{ x: 27 }] }, 38] };
36+
expect(get(arr, "a.0.b.0.x")).toBe(27);
37+
expect(get(arr, "a.1")).toBe(38);
38+
});

dashi/src/utils/get.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Get value from `obj` at `path`.
3+
*
4+
* For example
5+
* ```
6+
* > get({a: {b: [13, 14, 15]}}, "a.b.1")
7+
* < 14
8+
* ```
9+
*
10+
* @param obj and object or array
11+
* @param path a property path with dot `.` as separator.
12+
* @returns value from `obj` at `path`
13+
*/
14+
export function get<T extends object | unknown[]>(
15+
obj: T,
16+
path: string,
17+
): unknown {
18+
let result = obj;
19+
path.split(".").forEach((key) => {
20+
result = result[key];
21+
});
22+
return result;
23+
}

dashi/src/utils/set.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { expect, test } from "vitest";
2+
import { set } from "./set";
3+
4+
test("set flat object", () => {
5+
const obj = { a: 1, b: 2 };
6+
expect(set(obj, "a", 3)).toEqual({ a: 3, b: 2 });
7+
expect(set(obj, "b", 4)).toEqual({ a: 3, b: 4 });
8+
expect(set(obj, "c", 5)).toEqual({ a: 3, b: 4, c: 5 });
9+
});
10+
11+
test("set nested object", () => {
12+
const obj = { a: { b: 2, c: "X" } };
13+
expect(set(obj, "a.b", 3)).toEqual({ a: { b: 3, c: "X" } });
14+
expect(set(obj, "a.c", "Y")).toEqual({ a: { b: 3, c: "Y" } });
15+
expect(set(obj, "a.d", true)).toEqual({ a: { b: 3, c: "Y", d: true } });
16+
});
17+
18+
test("set array", () => {
19+
const arr = [1, 2];
20+
expect(set(arr, "0", 3)).toEqual([3, 2]);
21+
expect(set(arr, "1", 4)).toEqual([3, 4]);
22+
expect(set(arr, "2", 5)).toEqual([3, 4, 5]);
23+
});
24+
25+
test("set array of objects", () => {
26+
const arr = [{ a: 1, b: 2 }, 3];
27+
expect(set(arr, "0.a", 4)).toEqual([{ a: 4, b: 2 }, 3]);
28+
expect(set(arr, "0.b", 5)).toEqual([{ a: 4, b: 5 }, 3]);
29+
expect(set(arr, "1", 7)).toEqual([{ a: 4, b: 5 }, 7]);
30+
expect(set(arr, "2", 8)).toEqual([{ a: 4, b: 5 }, 7, 8]);
31+
});
32+
33+
test("set object with array of objects with array", () => {
34+
const arr = { a: [{ b: [{ x: 27, y: 28 }] }, 38] };
35+
expect(set(arr, "a.0.b.0.x", 28)).toEqual({
36+
a: [{ b: [{ x: 28, y: 28 }] }, 38],
37+
});
38+
expect(set(arr, "a.0.b.0.y", 29)).toEqual({
39+
a: [{ b: [{ x: 28, y: 29 }] }, 38],
40+
});
41+
});

dashi/src/utils/set.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Set value of `obj` at `path`.
3+
*
4+
* For example
5+
* ```
6+
* > set({a: {b: [13, 14, 15]}}, "a.b.1", -1)
7+
* < {a: {b: [13, -1, 15]}}
8+
* ```
9+
*
10+
* The function does not create new object instances,
11+
* but modifies the `obj` in place.
12+
* The intended use is with `immer`, pass the _draft_ object of
13+
* `produce`, i.e.,
14+
*
15+
* ```
16+
* obj = produce(draft => {
17+
* set(draft, ...)
18+
* })
19+
* ```
20+
*
21+
* @param obj and object or array.
22+
* @param path a property path with dot `.` as separator.
23+
* @param value a value to set.
24+
* @returns the `obj` to allow for nested `set()` calls.
25+
*/
26+
export function set<T extends object | unknown[]>(
27+
obj: T,
28+
path: string,
29+
value: unknown,
30+
): T {
31+
const keys = path.split(".");
32+
const lastIndex = keys.length - 1;
33+
let lastObj = obj;
34+
keys.forEach((key, index: number) => {
35+
if (index === lastIndex) {
36+
lastObj[key] = value;
37+
} else {
38+
lastObj = lastObj[key];
39+
}
40+
});
41+
return obj;
42+
}

0 commit comments

Comments
 (0)