求通用JavaScript对象合并结构化函数(按嵌套模板)
Hey, let's fix this nested object merging issue you're facing! I get that you need to group flat objects into a nested structure based on a predefined hierarchy, merging from bottom-up while preserving all non-key fields in a DATA layer—including fields that were used as keys in lower levels. Your current recursive approach is missing some grouping logic and handling levels incorrectly, which is why you're seeing missing data and duplicates at the CRT level. Let's break this down step by step.
First, let's restate your needs clearly to make sure we're on the same page:
- Input: A flat array of objects (all with the same keys, varying values) + a nested "schema" object that defines the hierarchy of keys to group by.
- Grouping Rule: Merge objects starting from the deepest key in the schema (bottom-up) to avoid losing unique values during merging.
- Data Preservation: All non-key fields (and even fields that were used as keys in lower levels) should be stored in a
DATAproperty at the deepest level of the nested structure.
Your existing function has a few critical issues:
- Top-Down Instead of Bottom-Up: You're traversing the schema from top to bottom, which means you're creating parent nodes before grouping by child keys—leading to duplicates and missing merged data.
- No Proper Grouping: You're passing single objects to the recursive call instead of grouping objects by the current key first. This prevents merging of objects with the same key value.
- Hardcoded Properties:
HODNOTA,NAZEV, andMETAare hardcoded, which doesn't align with your requirement to use a genericDATAlayer for all non-key data.
Here's a robust solution that addresses all your requirements. It first extracts the bottom-up hierarchy from your schema, then recursively groups objects by each level:
function mergeNestedObjects(flatArray, structure) { // Extract the bottom-up hierarchy from the schema object const getKeyHierarchy = (obj, path = []) => { const entries = Object.entries(obj); if (entries.length === 0) return path; // Assume each level of the schema has exactly one key (as per your use case) const [key, nestedObj] = entries[0]; // Prepend the current key to build bottom-up order return getKeyHierarchy(nestedObj, [key, ...path]); }; const hierarchy = getKeyHierarchy(structure); // Recursive function to group objects by each level in the hierarchy const groupByLevel = (arr, remainingLevels) => { // No more levels to group by: collect all objects into DATA if (remainingLevels.length === 0) { return { DATA: arr.map(item => ({ ...item })) // Preserve all original fields, including former keys }; } const currentKey = remainingLevels[0]; const grouped = {}; // Group objects by the current key's value arr.forEach(item => { const keyValue = item[currentKey]; if (!grouped[keyValue]) { grouped[keyValue] = []; } grouped[keyValue].push(item); }); // Recursively group each subset by the next level up Object.keys(grouped).forEach(key => { grouped[key] = groupByLevel(grouped[key], remainingLevels.slice(1)); }); return grouped; }; return groupByLevel(flatArray, hierarchy); }
Let's walk through the logic with an example to make it concrete:
Example Input
// Flat object array const flatData = [ { CRT: 'CRT1', ZO: 'ZO1', SKUPINA: 'SK1', VALUE: 10 }, { CRT: 'CRT1', ZO: 'ZO1', SKUPINA: 'SK2', VALUE: 20 }, { CRT: 'CRT2', ZO: 'ZO1', SKUPINA: 'SK1', VALUE: 15 }, { CRT: 'CRT2', ZO: 'ZO2', SKUPINA: 'SK1', VALUE: 30 } ]; // Schema defining the nested structure (bottom-up: SKUPINA → CRT → ZO) const structure = { ZO: { CRT: { SKUPINA: null } } };
Example Output
{ "ZO1": { "CRT1": { "SK1": { "DATA": [ { "CRT": "CRT1", "ZO": "ZO1", "SKUPINA": "SK1", "VALUE": 10 } ] }, "SK2": { "DATA": [ { "CRT": "CRT1", "ZO": "ZO1", "SKUPINA": "SK2", "VALUE": 20 } ] } }, "CRT2": { "SK1": { "DATA": [ { "CRT": "CRT2", "ZO": "ZO1", "SKUPINA": "SK1", "VALUE": 15 } ] } } }, "ZO2": { "CRT2": { "SK1": { "DATA": [ { "CRT": "CRT2", "ZO": "ZO2", "SKUPINA": "SK1", "VALUE": 30 } ] } } } }
Key Details
- Bottom-Up Hierarchy Extraction:
getKeyHierarchyparses your schema to get the order of keys from deepest to shallowest (e.g.,['SKUPINA', 'CRT', 'ZO']for the example). This ensures we group by the most specific key first. - Recursive Grouping:
groupByLevelgroups objects by the current key, then recursively processes each group with the next key in the hierarchy. This avoids duplicates and ensures all matching objects are merged under the correct nested path. - Data Preservation: The
DATAproperty at the deepest level contains full copies of all original objects, including fields that were used as keys in higher levels. This prevents any data loss.
- If you want to merge duplicate fields in
DATA(e.g., combineVALUEinto an array), you can modify the base case ofgroupByLevelto aggregate values instead of storing full objects. - If your schema has multiple keys at a level (unlikely per your description), you can adjust
getKeyHierarchyto handle multiple keys per level.
内容的提问来源于stack exchange,提问作者Lukáš Schöbel

