import type { Constructor, TypedArray } from "https://lib.deno.dev/x/ayonli_jsext@latest/index.ts"; import { isDictLike, isArrayLike, isBufferLike } from "https://lib.deno.dev/x/is_like@latest/index.js"; import isVoid from "./isVoid.ts"; type OmitChildrenNodes = Pick | Function | Constructor | Map | Set | Promise | TypedArray) ? K : T[K] extends object ? never : K; }[keyof T]>; type OmitChildrenElements = Pick | Map | Set | Promise | TypedArray) ? K : T[K] extends (object | any[] | ArrayLike) ? never : K; }[keyof T]>; /** * Create an object with flatted properties of the original object, the children * nodes' properties will be transformed to a string-represented path. * NOTE: this function also flat array/array-like nodes (except for TypedArray). * @param depth Default value: `1`. * @example * flatObject({ foo: { bar: "Hello, World!" } }) === { "foo.bar": "Hello, World!" } */ export default function flatObject( obj: T, depth?: number ): OmitChildrenNodes & Record; export default function flatObject( obj: T, depth: number, flatArray: true ): OmitChildrenElements & Record; export default function flatObject(obj: any, depth = 1, flatArray = false) { return flatDeep({}, obj, "", 0, depth, flatArray); } function flatDeep( carrier: any, source: any, field: string, depth: number, maxDepth: number, flatArray: boolean ) { let isArr: boolean | undefined; let isDict: boolean | undefined; let isContent = !isVoid(field) && field !== ""; if (depth === maxDepth || ( !(isArr = isArrayLike(source, true) && !isBufferLike(source)) && !(isDict = isDictLike(source)) )) { carrier[field] = source; } else if (isDict) { Reflect.ownKeys(source).forEach(key => { let value = (source)[key]; if (typeof key === "symbol") { if (depth === 0) { // only allow top-level symbol properties carrier[key] = value; } } else { flatDeep( carrier, value, isContent ? `${field}.${key}` : key, isContent ? depth + 1 : depth, maxDepth, flatArray ); } }); } else if (isArr) { if (flatArray) { for (let i = 0, len = (source).length; i < len; ++i) { flatDeep( carrier, (source)[i], isContent ? `${field}.${i}` : String(i), isContent ? depth + 1 : depth, maxDepth, flatArray ); } } else { carrier[field] = source; } } return carrier; }