Search code examples
javascriptrecursionnestedtraversal

How to convert array of nested objects to CSV?


I have an array with nested objects, such as this one:

[
    {"name": "1", "children": [{"name": "1.1", "children":"1.2"}]},
    {"id": "2", "thing": [{"name": "2.1", "children":"2.2"}]},
    {"name": "3", "stuff": [{"name": "3.1", "children":"3.2"}]},
]

The objects can contain values of different types, including other, nested objects.

I want to convert this array to CSV format.

I've tried to iterate with for .. in loops, regular nested for loops, .map() and recursion. I think recursion might be the only way to solve this particular problem, though. For the CSV field names I want to use the sequence of keys that lead to the value.

For the given example, the CSV result I'm looking for is:

name, children.name, children.children,id, thing.name, thing.children,  stuff.name, stuff.children
1, 1.1, 1.2,
,,,2,2.1,2.2
3,,,,3,3.1,3.2

Solution

  • You could use this ES6 function to create the 2D array you are looking for, which you can then easily transform to CSV:

    function pivot(arr) {
        var mp = new Map();
        
        function setValue(a, path, val) {
            if (Object(val) !== val) { // primitive value
                var pathStr = path.join('.');
                var i = (mp.has(pathStr) ? mp : mp.set(pathStr, mp.size)).get(pathStr);
                a[i] = val;
            } else {
                for (var key in val) {
                    setValue(a, key == '0' ? path : path.concat(key), val[key]);
                }
            }
            return a;
        }
        
        var result = arr.map( obj => setValue([], [], obj) );
        return [[...mp.keys()], ...result];
    }
    
    function toCsv(arr) {
        return arr.map( row => 
            row.map ( val => isNaN(val) ? JSON.stringify(val) : +val ).join(',')
        ).join('\n');
    }
    
    // Sample data
    var arr = [
        {"name": "1", "children": [{"name": "1.1", "children":"1.2"}]},
        {"id": "2", "thing": [{"name": "2.1", "children":"2.2"}]},
        {"name": "3", "stuff": [{"name": "3.1", "children":"3.2"}]},
    ];
    
    // Conversion to 2D array and then to CSV:
    console.log(toCsv(pivot(arr)));
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    For other ways to convert a 2D array to CSV, see this Q&A.