Fire-crawl / src /lib /extract /helpers /transform-array-to-obj.ts
Echo-AI-official's picture
Upload 280 files
0e759d2 verified
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;
}