import {isArrayOrPlainObject} from '../internal/is'; import {setValue} from '../internal/value/set'; import type {PlainObject, Simplify, UnionToIntersection} from '../models'; // #region Types /** * Thanks, type-fest! */ type KeysOfUnion = keyof UnionToIntersection< ObjectType extends unknown ? Record : never >; type OrderedKey = { order: number; value: string; }; /** * An unsmushed object, with all dot notation keys turned into nested keys */ export type Unsmushed = Simplify< Omit< { [UnionKey in KeysOfUnion]: Value[UnionKey]; }, `${string}.${string}` > >; // #endregion // #region Functions function getKeys(value: PlainObject): OrderedKey[] { const keys = Object.keys(value); const {length} = keys; const orderedKeys: OrderedKey[] = []; for (let index = 0; index < length; index += 1) { const key = keys[index]; orderedKeys.push({ order: key.split('.').length, value: key, }); } return orderedKeys.sort((first, second) => first.order - second.order); } /** * Unsmush a smushed object _(turning dot notation keys into nested keys)_ * * @param value Object to unsmush * @returns Unsmushed object with nested keys */ export function unsmush(value: Value): Unsmushed { if (typeof value !== 'object' || value === null) { return {} as never; } const keys = getKeys(value); const {length} = keys; const unsmushed: PlainObject = {}; for (let index = 0; index < length; index += 1) { const key = keys[index].value; const val = value[key]; let next = val; if (isArrayOrPlainObject(val)) { next = Array.isArray(val) ? val.slice() : {...val}; } setValue(unsmushed, key, next); } return unsmushed as never; } // #endregion