Skip to content

Instantly share code, notes, and snippets.

@Toshinaki
Created November 24, 2023 13:38
Show Gist options
  • Save Toshinaki/4aa225075be06677d5970550ed647567 to your computer and use it in GitHub Desktop.
Save Toshinaki/4aa225075be06677d5970550ed647567 to your computer and use it in GitHub Desktop.
lodash custom mixin
import __ from 'lodash';
/**
* You can extend Lodash with mixins
* And use it as below
* import _ from '@lodash'
*
* copy the file and remove functions that not needed
*/
const _ = __.runInContext();
interface LoDashMixins extends _.LoDashStatic {
setIn<T>(state: T, name: string, value: unknown): T;
// difference(origin: any, updated: any): any;
deepDifference<T extends object>(object1: T, object2: T): Partial<T>;
replaceArray(origin: unknown, updated: Array<unknown>): Array<unknown> | undefined;
asArary<T>(
value: T
): T extends undefined ? Array<never> : T extends Array<unknown> ? T : Array<T>;
filterRecursive<T extends RecursiveParent<T>>(
data: T[] | null,
predicate: (item: T) => boolean
): T[] | null;
pickByRecursive<T extends object>(obj: T, predict: (value: unknown) => boolean): T;
filterDeep<T extends RecursiveParent<T>>(
data: T[],
predicate: (item: T) => boolean | undefined
): T[];
}
/**
* Deeply compares two nested objects and returns the difference between them.
* @param object1 - The first object to compare.
* @param object2 - The second object to compare.
* @returns The difference between the two objects.
*/
const deepDifference = <T extends object>(object1: T, object2: T): Partial<T> => {
return _.transform(object2, (result: Partial<T>, value: unknown, key: keyof T) => {
if (!_.isEqual(value, object1[key])) {
result[key] = (
_.isObject(value) && _.isObject(object1[key])
? deepDifference(object1[key] as T[keyof T], value as T[keyof T])
: value
) as T[keyof T] | undefined;
}
});
};
const replaceArray = (origin: unknown, updated: Array<unknown>) => {
if (_.isArray(origin)) {
return updated;
}
};
const asArary = <T>(value: T) => (_.isNil(value) ? [] : _.castArray(value));
/**
* Recursively filters a nested data structure based on a given predicate.
*
* @template T - The type of the parent object in the data structure.
* @param {T[] | null} data - The nested data structure to filter.
* @param {(item: T) => boolean} predicate - The predicate function used to filter each item.
* @returns {T[] | null} The filtered result, or `null` if the input data is `null`.
*/
function filterRecursive<T extends RecursiveParent<T>>(
data: T[] | null,
predicate: (item: T) => boolean
): T[] | null {
// if no data is sent in, return null, otherwise transform the data
return !data
? null
: data.reduce((list: T[], entry: T) => {
let clone: T | null = null;
if (predicate(entry)) {
// if the object matches the filter, clone it as it is
clone = { ...entry };
}
if (entry.children != null) {
// if the object has childrens, filter the list of children
const children = filterRecursive(entry.children, predicate);
if (children !== null && children.length > 0) {
// if any of the children matches, clone the parent object, overwrite
// the children list with the filtered list
clone = { ...entry, children };
}
}
// if there's a cloned object, push it to the output list
if (clone) {
list.push(clone);
}
return list;
}, []);
}
export function pickByRecursive<T extends object>(obj: T, predict: (value: unknown) => boolean): T {
return _.transform(obj, (result, value, key) => {
if (_.isObject(value) && !_.isEmpty(value)) {
result[key] = pickByRecursive(value, predict); // Recursively check nested objects
} else if (predict(value)) {
result[key] = value;
}
});
}
function filterDeep<T extends RecursiveParent<T>>(
data: T[],
predicate: (item: T) => boolean | undefined
): T[] {
return data
.filter(predicate)
.map((item) => {
if (item.children) {
return { ...item, children: filterDeep(item.children, predicate) };
}
return _.clone(item);
})
.filter((item) => (item.children ? item.children.length > 0 : true));
}
_.mixin({
// Immutable Set for setting state
setIn: (state: object, name: string, value: unknown) => {
return _.setWith(_.clone(state), name, value, _.clone);
},
// difference,
deepDifference,
replaceArray,
asArary,
filterRecursive,
pickByRecursive,
filterDeep,
});
export default _ as LoDashMixins;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment