import isEqual from "lodash/isEqual"; /** * Transforms an array of objects into a single object, merging properties with the same name. * @param originalSchema - The schema of the original data. * @param arrayData - The array of objects to transform. * @returns A single object with merged properties. */ export function transformArrayToObject( originalSchema: any, arrayData: any[], ): any { if (Object.keys(originalSchema).length == 0) { return {}; } const transformedResult: any = {}; // Function to find the array key in a nested schema function findArrayKey(schema: any): string | null { for (const key in schema.properties) { if (schema.properties[key].type === "array") { return key; } else if (schema.properties[key].type === "object") { const nestedKey = findArrayKey(schema.properties[key]); if (nestedKey) { return `${key}.${nestedKey}`; } } } return null; } const arrayKeyPath = findArrayKey(originalSchema); if (!arrayKeyPath) { return arrayData.reduce((acc, item) => { for (const key in item) { if (!acc[key]) { acc[key] = item[key]; } else if ( typeof acc[key] === "object" && typeof item[key] === "object" ) { acc[key] = { ...acc[key], ...item[key] }; } } return acc; }, {}); } const arrayKeyParts = arrayKeyPath.split("."); const arrayKey = arrayKeyParts.pop(); if (!arrayKey) { throw new Error("Array key not found in schema"); } const parentSchema = arrayKeyParts.reduce( (schema, key) => schema.properties[key], originalSchema, ); const itemSchema = parentSchema.properties[arrayKey].items; if (!itemSchema) { throw new Error("Item schema not found for array key"); } // Initialize the array in the transformed result let currentLevel = transformedResult; arrayKeyParts.forEach((part) => { if (!currentLevel[part]) { currentLevel[part] = {}; } currentLevel = currentLevel[part]; }); currentLevel[arrayKey] = []; // Helper function to check if an object is already in the array function isDuplicateObject(array: any[], obj: any): boolean { return array.some((existingItem) => isEqual(existingItem, obj)); } // Helper function to validate if an object follows the schema function isValidObject(obj: any, schema: any): boolean { return Object.keys(schema.properties).every((key) => { return ( obj.hasOwnProperty(key) && typeof obj[key] === schema.properties[key].type ); }); } // Iterate over each item in the arrayData arrayData.forEach((item) => { let currentItem = item; // Skip null items if (currentItem === null) { return; } arrayKeyParts.forEach((part) => { if (currentItem && currentItem[part]) { currentItem = currentItem[part]; } else { currentItem = null; } }); // Skip if we couldn't find the nested path if (currentItem === null) { return; } // Copy non-array properties from the parent object for (const key in parentSchema.properties) { if ( key !== arrayKey && currentItem.hasOwnProperty(key) && !currentLevel.hasOwnProperty(key) ) { currentLevel[key] = currentItem[key]; } } // Ensure that the currentItem[arrayKey] exists and is an array before mapping if (currentItem && currentItem[arrayKey] && Array.isArray(currentItem[arrayKey])) { currentItem[arrayKey].forEach((subItem: any) => { if ( typeof subItem === "object" && subItem !== null && isValidObject(subItem, itemSchema) ) { // For arrays of objects, add only unique objects const transformedItem: any = {}; let hasValidData = false; for (const key in itemSchema.properties) { if (subItem.hasOwnProperty(key) && subItem[key] !== undefined) { transformedItem[key] = subItem[key]; hasValidData = true; } } if ( hasValidData && !isDuplicateObject(currentLevel[arrayKey], transformedItem) ) { currentLevel[arrayKey].push(transformedItem); } } }); } else { console.warn( `Expected an array at ${arrayKey}, but found:`, currentItem ? currentItem[arrayKey] : 'undefined' ); // create an array if it doesn't exist if (currentLevel[arrayKey] === undefined) { currentLevel[arrayKey] = []; } } // Handle merging of array properties for (const key in parentSchema.properties) { if ( parentSchema.properties[key].type === "array" && currentItem && Array.isArray(currentItem[key]) ) { if (!currentLevel[key]) { currentLevel[key] = []; } currentItem[key].forEach((value: any) => { if ( !currentLevel[key].includes(value) && !isDuplicateObject(currentLevel[arrayKey], value) ) { currentLevel[key].push(value); } }); } } }); return transformedResult; }