import { isPlainObject } from "../infra/plain-object.js" ;
import { isBlockedObjectKey } from "./prototype-keys.js" ;
type PlainObject = Record<string, unknown>;
type MergePatchOptions = {
mergeObjectArraysById?: boolean ;
};
function isObjectWithStringId(value: unknown): value is Record<string, unknown> & { id: string } {
if (!isPlainObject(value)) {
return false ;
}
return typeof value.id === "string" && value.id.length > 0 ;
}
/**
* Merge arrays of object - like entries keyed by ` id ` .
*
* Contract :
* - Base array must be fully id - keyed ; otherwise return undefined ( caller should replace ) .
* - Patch entries with valid id merge by id ( or append when the id is new ) .
* - Patch entries without valid id append as - is , avoiding destructive full - array replacement .
*/
function mergeObjectArraysById(
base: unknown[],
patch: unknown[],
options: MergePatchOptions,
): unknown[] | undefined {
if (!base.every(isObjectWithStringId)) {
return undefined;
}
const merged: unknown[] = [...base];
const indexById = new Map<string, number>();
for (const [index, entry] of merged.entries()) {
if (!isObjectWithStringId(entry)) {
return undefined;
}
indexById.set(entry.id, index);
}
for (const patchEntry of patch) {
if (!isObjectWithStringId(patchEntry)) {
merged.push(structuredClone(patchEntry));
continue ;
}
const existingIndex = indexById.get(patchEntry.id);
if (existingIndex === undefined) {
merged.push(structuredClone(patchEntry));
indexById.set(patchEntry.id, merged.length - 1 );
continue ;
}
merged[existingIndex] = applyMergePatch(merged[existingIndex], patchEntry, options);
}
return merged;
}
export function applyMergePatch(
base: unknown,
patch: unknown,
options: MergePatchOptions = {},
): unknown {
if (!isPlainObject(patch)) {
return patch;
}
const result: PlainObject = isPlainObject(base) ? { ...base } : {};
for (const [key, value] of Object.entries(patch)) {
if (isBlockedObjectKey(key)) {
continue ;
}
if (value === null ) {
delete result[key];
continue ;
}
if (options.mergeObjectArraysById && Array.isArray(result[key]) && Array.isArray(value)) {
const mergedArray = mergeObjectArraysById(result[key] as unknown[], value, options);
if (mergedArray) {
result[key] = mergedArray;
continue ;
}
}
if (isPlainObject(value)) {
const baseValue = result[key];
result[key] = applyMergePatch(isPlainObject(baseValue) ? baseValue : {}, value, options);
continue ;
}
result[key] = value;
}
return result;
}
Messung V0.5 in Prozent C=99 H=88 G=93
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-06-09)
¤
*© Formatika GbR, Deutschland