Search code examples
javascriptarraysjsonobjectrecursion

Create a new array from objects with random structure


I want to make a new array from a JSON. The problem is I don't know the structure of the JSON I will be getting. It could be anything. But I know the JSON's key which will contain all the data I will need for the new array. For example say that key is 'data' and it is the array that contains all the data I need. Besides that, I will have to look for 'customerName', 'customerAddress' and 'customerAge' in that JSON array with key 'data'. And I will create the new array with those keys' values. So the new array so should look like this:

[
    {
         name: '', // it will be customerName from the JSON
         address: '', // it will be customerAddress from the JSON
         age: '', // it will be customerAge from the JSON
     },
     {
         // more objects
     }
]

Please note that customerName, customerAddress and customerAge could exist in nested arrays or objects in the 'data' array in JSON. So I have to look for them.

I have created a recursive function for it but it is returning an empty array.

This is what I tried:

function extractDataFromJSON(json, key) {
    const result = [];

    function findData(obj) {
        if (Array.isArray(obj)) {
            for (const item of obj) {
                findData(item);
            }
        } else if (obj && typeof obj === 'object') {
            if (key in obj) {
                if (Array.isArray(obj[key])) {
                    for (const item of obj[key]) {
                        findData(item);
                    }
                } else if (
                    'customerName' in obj[key] &&
                    'customerAddress' in obj[key] &&
                    'customerAge' in obj[key]
                ) {
                    const { customerName, customerAddress, customerAge } = obj[key];
                    result.push({
                        name: customerName || '',
                        address: customerAddress || '',
                        age: customerAge || '',
                    });
                }
            } else {
                for (const prop in obj) {
                    if (obj.hasOwnProperty(prop) && typeof obj[prop] === 'object') {
                        findData(obj[prop]);
                    }
                }
            }
        }
    }

    findData(json);
    return result;
}

const jsonData1 = {
    data: [
        {
            customerName: 'John Doe',
            customerAddress: '123 Main St',
            customerAge: 30,
        },
        {
            customerName: 'Jane Smith',
            customerAddress: '456 Maple Ave',
            customerAge: 25,
        },
        // ... other data ...
    ],
};

// const jsonData2 = [
//     {
//          data: [
//                  {
//                      customerName: 'John Doe',
//                      customerAddress: '123 Main St',
//                      customerAge: 30,
//                 },
//                 {
//                      customerName: 'Jane Smith',
//                      customerAddress: '456 Maple Ave',
//                      customerAge: 25,
//                 },
//                 // ... other data ...
//          ],
//     }
// ];

// const jsonData3 = {
//     data: [
//         {
//             customerName: 'John Doe',
//             customerAge: 30,
//             address: {
//                 customerAddress: '123 Main St',
//             }
//         },
//         {
//             customerName: 'Jane Smith',
//             customerAge: 30,
//             address: {
//                 customerAddress: '456 Maple Ave',
//             }
//         },
//     ],
// };



const newArray = extractDataFromJSON(jsonData1, 'data');
console.log(newArray);

I have also added 2 other objects as tests to check if my function works with those type of structures as well.

Any help would be highly appreciated.

UPDATE

const jsonData1 = {
    data: [
        {
            customerName: 'John Doe',
            customerAddress: '123 Main St',
            customerAge: 30,
        },
        {
            customerName: 'Jane Smith',
            customerAddress: '456 Maple Ave',
            customerAge: 25,
        },
        // ... other data ...
    ],
    data2: [
        {
            customerName: 'John Doe2',
            customerAddress: '123 Mainsss St',
            customerAge: 30,
        },
        {
            customerName: 'Jane Smith3',
            customerAddress: '456 Mapleaaa Ave',
            customerAge: 25,
        },
        // ... other data ...
    ],
};

Solution

  • You can use the same function for recursion, just pass the result array to it. There some trick here to collect the nested object properties - we provide acc (a property accumulator) argument to collect the properties in the descendants:

    IMPORTANT REMARK The OP wants all fields collected:

                        'customerName' in obj[key] &&
                        'customerAddress' in obj[key] &&
                        'customerAge' in obj[key]
    

    So this case won't be collected, since the field name is duplicated:

    //             customerName: 'Jane Smith',
    //             customerAddress: '456 Maple Ave',
    //             address: {
    //                 customerAddress: '456 Maple Ave',
    //             }
    

    I supposed that's an error. Moreover the code doesn't try to find duplicates and uses the first found property. It would be weird if the data contains duplicates imho...

    function extractDataFromJSON(obj, fields, result = [], acc = null) {
    
        if (Array.isArray(obj)) {
            for (const item of obj) {
                extractDataFromJSON(item, fields, result, acc);
            }
        } else if (obj && typeof obj === 'object') {
    
            const keys = Object.keys(obj).filter(key => fields[key]);
            const fieldsLen = Object.keys(fields).length;
    
            if (keys.length) {
    
                const item = acc || {};
    
                for (const key in obj) {
                    if (fields[key]) {
                        item[fields[key]] = obj[key] || '';
                        continue;
                    }
                    // check the property if the object is incomplete
                    keys.length < fieldsLen && extractDataFromJSON(obj[key], fields, null, item);
                }
                // push only complete items and if the result array is present 
                // (not the collecting properties mode)
                if(result && Object.keys(item).length === fieldsLen){
                    result.push(item);
                }
    
            } else {
                for (const key in obj) {
                    extractDataFromJSON(obj[key], fields, result, acc);
                }
            }
    
        }
        return result;
    
    }
    
    const jsonData1 = {
        data: [
            {
                customerName: 'John Doe',
                customerAddress: '123 Main St',
                customerAge: 30,
            },
            {
                customerName: 'Jane Smith',
                customerAddress: '456 Maple Ave',
                customerAge: 25,
            },
            // ... other data ...
        ],
        data2: [
            {
                customerName: 'John Doe2',
                customerAddress: '123 Mainsss St',
                customerAge: 30,
            },
            {
                customerName: 'Jane Smith3',
                customerAddress: '456 Mapleaaa Ave',
                customerAge: 25,
            },
            // ... other data ...
        ],
    };
    
    const jsonData3 = [{
        differentKey: [
            {
                customerName: 'John Doe',
                customerAge: 30,
                iAmDummy: [{ dummyArray: [null, undefined, '', false, new Date] }],
                customerAddress: '123 Main St'
            },
            {
                customerName: 'Jane Smith',
                iAmDummy: [{ dummyArray: [null, undefined, '', false, new Date] }],
                lookDeeper: {customerAge: 40},
                address: [{
                    customerAddress: '456 Maple Ave',
                }]
            },
        ],
    }];
    
    
    const newArray1 = extractDataFromJSON(jsonData1, {customerName: 'name', customerAddress: 'address', customerAge: 'age'});
    console.log(newArray1);
    
    const newArray3 = extractDataFromJSON(jsonData3, {customerName: 'name', customerAddress: 'address', customerAge: 'age'});
    console.log(newArray3);