188 lines · 5.2 KB · TypeScript
RawDownload
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;}