import { isPlainObject } from './Object/is-plain-object'
import type { DefinedValue, ObjectType, Value } from './Type/type-helpers'
export const isEmptyObject = (object: ObjectType): boolean =>
object &&
Object.keys(object).length === 0 &&
Object.getPrototypeOf(object) === Object.prototype
const mergeSourceIntoTarget = <Obj extends ObjectType>(
target: Obj,
source: Obj,
): void => {
for (const key of Object.keys(source)) {
if (isPlainObject(source[key])) {
if (target[key] == null) {
Object.assign(target, { [key]: {} })
}
mergeObjects(target[key], source[key])
} else {
Object.assign(target, { [key]: source[key] })
}
}
}
export const mergeObjects = <T extends ObjectType>(
target: T,
...sources: T[]
): T => {
while (sources.length > 0) {
const source = sources.shift()
if (
source !== undefined &&
isPlainObject(target) &&
isPlainObject(source)
) {
mergeSourceIntoTarget(target, source)
}
}
return target
}
export const removeNullValues = <Type>(arr: (Type | null)[]): Type[] =>
arr.filter((val: Type | null): val is Type => val !== null)
export const removeNullishValues = <Type>(
arr: (Type | undefined | null)[],
): Type[] =>
arr.filter((val: Type | undefined | null): val is Type => val != null)
export const removeFalsyValues = <Type>(arr: (Type | Value)[]): Type[] =>
arr.filter((val): val is Type =>
typeof val === 'object' && val != null ? isEmptyObject(val) : val != null,
)
export const shallowRemoveObjNullishValues = (object: ObjectType): ObjectType =>
Object.fromEntries(Object.entries(object).filter(([_, v]) => v != null))
export const addOrRemoveFromList =
<T extends DefinedValue>(listOfThings: T[], aThing: T) =>
(add: boolean): T[] =>
add
? [...listOfThings, aThing]
: listOfThings.filter(thing => thing !== aThing)
export const addWhenAbsentOtherwiseRemove = <T extends DefinedValue>(
listOfThings: T[],
aThing: T,
): T[] =>
addOrRemoveFromList(listOfThings, aThing)(!listOfThings.includes(aThing))
export const deduplicateObjects = <
Obj extends object = ObjectType,
Key extends keyof Obj = keyof Obj,
>(
array: Obj[],
key: Key,
): Obj[] => [...new Map(array.map(object => [object[key], object])).values()]
export const deduplicateObjectsByAllKeys = <Obj extends object = ObjectType>(
array: Obj[],
): Obj[] => [
...new Map(array.map(object => [JSON.stringify(object), object])).values(),
]
export const updateObjectInArray = <Obj extends ObjectType = ObjectType>(
array: Obj[],
key: keyof Obj,
newData: Partial<Obj>,
): Obj[] => {
if (newData[key] === undefined)
throw new Error(`The key ${key.toString()} does not exist in newData`)
return array.map(object =>
object[key] === newData[key] ? { ...object, ...newData } : object,
)
}