thi-ng/umbrella

View on GitHub
packages/object-utils/src/rename-keys.ts

Summary

Maintainability
A
55 mins
Test Coverage
import type { Fn2, Nullable } from "@thi.ng/api";
import { isArray } from "@thi.ng/checks/is-array";
import { empty } from "./empty.js";

/**
 * Renames keys in `src` using mapping provided by key map `km`. Does
 * support key swapping / swizzling. Does not modify original.
 *
 * @remarks
 * Also see {@link renameKeysObj} for related functionality & example.
 *
 * @param src - source map
 * @param km - key mappings
 * @param out - result map
 */
export const renameKeysMap = <K, V>(
    src: Map<K, V>,
    km: Map<K, K>,
    out?: Map<K, V>
) => {
    out = out || empty(src, Map);
    for (let [k, v] of src) {
        out!.set(km.has(k) ? km.get(k)! : k, v);
    }
    return out;
};

/**
 * Renames keys in `src` using mapping provided by key map `km`. Does
 * support key swapping / swizzling. Does not modify original.
 *
 * ```ts tangle:../export/rename-keys.ts
 * import { renameKeysObj } from "@thi.ng/object-utils";
 *
 * // swap a & b, rename c
 * console.log(
 *   renameKeysObj({a: 1, b: 2, c: 3}, {a: "b", b: "a", c: "cc"})
 * );
 * // {b: 1, a: 2, cc: 3}
 * ```
 *
 * @param src - source object
 * @param km - key mappings
 * @param out - result object
 */
export const renameKeysObj = <T>(
    src: T,
    km: { [id in keyof T]?: PropertyKey },
    out: any = {}
) => {
    for (let k in src) {
        out[km.hasOwnProperty(k) ? km[k] : k] = src[k];
    }
    return out;
};

/**
 * Similar to (combination of) {@link renameKeysObj} and
 * {@link selectDefinedKeysObj}. Takes a `src` object and `keys`, an object of
 * mappings to rename given keys and (optionally) transform their values.
 * Returns new object. If `src` is nullish itself, returns an empty object.
 *
 * @remarks
 * Only keys with non-nullish values (in `src`) are being processed. The `keys`
 * object uses the original key names as keys and the new keys as their values
 * (like {@link renameKeysObj}). If a transformation of a key's value is
 * desired, the format is `{ oldname: [newname, xform] }`, where `xform` is a
 * 2-arg function, receiving the original value of `oldname` and the entire
 * `src` object as 2nd arg. The return value of that function will be used as
 * the value of `newname`.
 *
 * @example
 * ```ts tangle:../export/rename-transformed.ts
 * import { renameTransformedKeys } from "@thi.ng/object-utils";
 *
 * console.log(
 *   renameTransformedKeys(
 *     // source object
 *     { a: 1, b: 2, c: null },
 *     // mappings
 *     {
 *       // rename a => aa
 *       a: "aa",
 *       // rename & transform
 *       b: ["bb", (x, src) => x * 10 + src.a]
 *       // ignored, since original c is null
 *       c: "cc"
 *     }
 *   )
 * );
 * // { aa: 1, bb: 21 }
 * ```
 *
 * @param src -
 * @param keys -
 */
export const renameTransformedKeys = <T extends object, K extends keyof T>(
    src: Nullable<T>,
    keys: Record<K, PropertyKey | [PropertyKey, Fn2<any, T, any>]>
) => {
    if (!src) return {};
    const res: any = {};
    for (let $k in keys) {
        const spec = keys[$k];
        const [k, fn] = isArray(spec) ? spec : [spec];
        const val = src[$k];
        if (val != null) res[k] = fn ? fn(val, src) : val;
    }
    return res;
};