/ packages / utils / reflection.ts
reflection.ts
 1  import { equal } from "@std/assert";
 2  import type { CodecKey } from "./codec.ts";
 3  
 4  /**
 5   * Get a value from an array or map given a `key`, which can be a string, number, or object.
 6   * If the keys is an object then a deep equality check is performed while iterating through a map.
 7   * TODO: Iterateing through the map to find a key/value is suboptimal, consider using a more efficient data structure.
 8   */
 9  export function get(
10    obj: unknown,
11    key: CodecKey,
12  ) {
13    if (obj instanceof Map) {
14      if (
15        typeof key === "object" && key !== null
16      ) {
17        // find the key in the map if the key is an array
18        const f = Array.from(obj.entries()).find(([k]) => equal(k, key));
19        if (f) return f[1];
20      } else {
21        return obj.get(key);
22      }
23    } else if (
24      typeof obj === "object" && obj !== null &&
25      (typeof key === "number" ||
26        typeof key === "string")
27    ) {
28      return Reflect.get(obj, key);
29    } else {
30      // obj was a number / bool / null / undefined / etc
31      return undefined;
32    }
33  }
34  
35  /**
36   * Set a value in an array or map given a `key`, which can be a string, number, or object.
37   */
38  export function set(
39    obj: unknown,
40    key: CodecKey,
41    value: unknown,
42  ): void {
43    if (obj instanceof Map) {
44      const f = Array.from(obj.entries()).find(([k]) => equal(k, key)) ?? [key];
45      obj.set(f[0], value);
46    } else if (
47      typeof obj === "object" && obj !== null &&
48      (typeof key === "number" ||
49        typeof key === "string")
50    ) {
51      Reflect.set(obj, key, value);
52    } else {
53      throw new Error(`Cannot set key ${key} on ${obj}`);
54    }
55  }
56  
57  /**
58   * Remove a value from an array or map given a `key`, which can be a string, number, or object.
59   */
60  export function remove(
61    obj: unknown,
62    key: CodecKey,
63  ): void {
64    if (obj instanceof Map) {
65      const f = Array.from(obj.entries()).find(([k]) => equal(k, key));
66      if (f) obj.delete(f[0]);
67    } else if (
68      obj instanceof Array && typeof key === "number"
69    ) {
70      obj.splice(key, 1);
71    } else {
72      throw new Error(`Cannot remove key ${key} from ${obj}`);
73    }
74  }