diff --git a/src/deepmerge.ts b/src/deepmerge.ts index 35a7047a..24f42481 100644 --- a/src/deepmerge.ts +++ b/src/deepmerge.ts @@ -117,43 +117,55 @@ function mergeUnknowns< U extends DeepMergeMergeFunctionUtils, MF extends DeepMergeMergeFunctionsURIs >(values: Ts, utils: U): DeepMergeHKT { - const types = values.map(getObjectType); - const type = types[0]; + const type = getObjectType(values[0]); - if (types.every((value) => value === type)) { - if (type === ObjectType.RECORD) { + // eslint-disable-next-line functional/no-conditional-statement -- add an early escape for better performance. + if (type !== ObjectType.NOT && type !== ObjectType.OTHER) { + // eslint-disable-next-line functional/no-loop-statement, functional/no-let -- using a loop here is more performant than mapping every value and then testing every value. + for (let i = 1; i < values.length; i++) { + // eslint-disable-next-line functional/no-conditional-statement -- waiting on https://github.com/jonaskello/eslint-plugin-functional/issues/269 + if (getObjectType(values[i]) === type) { + continue; + } + + return utils.mergeFunctions.mergeOthers(values, utils) as DeepMergeHKT< + Ts, + MF + >; + } + } + + switch (type) { + case ObjectType.RECORD: return utils.mergeFunctions.mergeRecords( values as ReadonlyArray>>, utils ) as DeepMergeHKT; - } - if (type === ObjectType.ARRAY) { + case ObjectType.ARRAY: return utils.mergeFunctions.mergeArrays( values as ReadonlyArray>, utils ) as DeepMergeHKT; - } - if (type === ObjectType.SET) { + case ObjectType.SET: return utils.mergeFunctions.mergeSets( values as ReadonlyArray>>, utils ) as DeepMergeHKT; - } - if (type === ObjectType.MAP) { + case ObjectType.MAP: return utils.mergeFunctions.mergeMaps( values as ReadonlyArray>>, utils ) as DeepMergeHKT; - } - } - return utils.mergeFunctions.mergeOthers(values, utils) as DeepMergeHKT< - Ts, - MF - >; + default: + return utils.mergeFunctions.mergeOthers(values, utils) as DeepMergeHKT< + Ts, + MF + >; + } } /** @@ -166,26 +178,30 @@ function mergeRecords< U extends DeepMergeMergeFunctionUtils, MF extends DeepMergeMergeFunctionsURIs >(values: Ts, utils: U) { - const neverValue = {}; - return Object.fromEntries( - [...getKeys(values)] - .map((key) => { - const propValues = values - .map((value) => - objectHasProperty(value, key) ? value[key] : neverValue - ) - .filter((value) => value !== neverValue); - - // assert(propValues.length > 0); - - if (propValues.length === 1) { - return [key, propValues[0]]; - } - - return [key, mergeUnknowns(propValues, utils)]; - }) - .filter((value): value is [unknown, unknown] => value !== neverValue) - ) as DeepMergeRecordsDefaultHKT; + const result: Record = {}; + + /* eslint-disable functional/no-loop-statement, functional/no-conditional-statement -- using a loop here is more performant. */ + + for (const key of getKeys(values)) { + const propValues = []; + + for (const value of values) { + if (objectHasProperty(value, key)) { + propValues.push(value[key]); + } + } + + // assert(propValues.length > 0); + + result[key] = + propValues.length === 1 + ? propValues[0] + : mergeUnknowns(propValues, utils); + } + + /* eslint-enable functional/no-loop-statement, functional/no-conditional-statement */ + + return result as DeepMergeRecordsDefaultHKT; } /** diff --git a/src/utils.ts b/src/utils.ts index df1fd40b..dfa613b7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -55,19 +55,20 @@ export function getObjectType(object: unknown): ObjectType { export function getKeys( objects: Readonly> ): Set { - return objects.reduce>((mutableCarry, object) => { - // eslint-disable-next-line functional/no-loop-statement -- using a loop here is more efficient. + const keys = new Set(); + + /* eslint-disable functional/no-loop-statement -- using a loop here is more efficient. */ + for (const object of objects) { for (const key of [ - ...Object.getOwnPropertyNames(object), + ...Object.keys(object), ...Object.getOwnPropertySymbols(object), - ].filter((property) => - Object.prototype.propertyIsEnumerable.call(object, property) - )) { - mutableCarry.add(key); + ]) { + keys.add(key); } + } + /* eslint-enable functional/no-loop-statement */ - return mutableCarry; - }, new Set()); + return keys; } /**