patched-undo-peasy
Version:
undo/redo for easy peasy
85 lines (74 loc) • 2.13 kB
text/typescript
import _ from "lodash";
type ObjOrArray = object | Array<any>;
/** Return a copy of an object with some fields elided.
*
* @param fn copy will not include source properties for which this function returns true
*/
export function copyFiltered(
src: ObjOrArray,
fn: (value: any, key: string, path: string[]) => boolean
): ObjOrArray {
return filterCopyRecurse(src, []);
function filterCopyRecurse(obj: ObjOrArray, path: string[]): ObjOrArray {
if (_.isArray(obj)) {
return obj.map((elem) => filterCopyRecurse(elem, path));
}
if (_.isObject(obj)) {
const filtered = Object.entries(obj).filter(
([key, value]) => !fn(value, key, path)
);
const copies = filtered.map(([key, value]) => [
key,
filterCopyRecurse(value, path.concat([key])),
]);
return Object.fromEntries(copies);
}
return obj;
}
}
/** replace undefined fields with a default value */
export function replaceUndefined<T extends Partial<U>, U>(
obj: T,
defaults: U
): T & U {
const result = { ...defaults, ...removeUndefined(obj) };
return result;
}
/** @return a copy, eliding fields with undefined values */
export function removeUndefined<T>(obj: T): T {
const result = { ...obj };
for (const key in result) {
if (result[key] === undefined) {
delete result[key];
}
}
return result;
}
export interface AnyObject {
[key: string]: any;
}
/** @return the paths of to all getter properties nested in a source object */
export function findGetters(
src: AnyObject,
pathPrefix: string[] = []
): string[][] {
const result = Object.entries(src).flatMap(([key, value]) => {
if (isGetter(src, key)) {
const getter = [pathPrefix.concat([key])];
return getter;
} else if (_.isPlainObject(value)) {
const found = findGetters(value, pathPrefix.concat([key]));
return found;
} else {
return [];
}
});
return result;
}
function isGetter(src: {}, key: string): boolean {
const desc = Object.getOwnPropertyDescriptor(src, key);
if (desc && desc.get) {
return true;
}
return false;
}