File size: 5,279 Bytes
0e759d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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;
}