如何将嵌套对象数组转换为CSV?

9

我有一个包含嵌套对象的数组,就像这样:

[
    {"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"}]},
]

这些对象可以包含不同类型的值,包括其他嵌套对象。

我希望将这个数组转换为CSV格式。

我已经尝试使用for .. in循环,常规嵌套for循环,.map()和递归进行迭代。但我认为递归可能是解决这个特定问题的唯一方法。对于CSV字段名称,我想使用导致该值的键序列。

对于给定的示例,我要找到的CSV结果是:

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

JSON对象并不存在。 - nicovank
1
如果您有两个嵌套的“children”对象呢? - zerkms
1
@nicovank -- 点击这里了解什么是JSON?http://www.json.org/ - user1789573
@zerkms -- 正是我努力想弄清楚的。这里的问题实际上是数组中多个维度和 JSON 中更深层次的嵌套对象之间的差异,可能是这样。 - user1789573
1
@user1789573 好的,请问是谁指派给您这个任务的?请向他们咨询如何表示嵌套结构。这不是技术问题,而是业务问题。如果我对这个业务逻辑问题做出最终决定,我会说“这没有太多意义,请以更有结构的形式提供数据”。 - zerkms
2个回答

16

你可以使用这个ES6函数来创建你需要的2D数组,然后很容易地将其转换为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; }

如果想要了解其他将2D数组转换为CSV的方法,请参见此Q&A


非常感谢!我之前不知道JS中有关于省略号的map功能。 - user1789573
1
我们能否做相反的事情? - Vipin Yadav
@Yipin,好的。如果你在这方面遇到问题...可以查看许多关于此主题的问答,如果还没有解决,可以提出新的问题。 - trincot
请注意 - Date对象将不会被正确处理,并且会失败'Object(val)!== val',因此还应添加类似于' || val instanceof Date'的内容。 - Googs
@Googs,这个函数基本上是为了 JSON 兼容的对象而设计的。任何没有暴露出所需可枚举属性的对象都需要进行转换。如果日期应该被转换为字符串,则需要自己按照所需格式完成转换。 - trincot

0

这是一个 ES5 提案。

您可以迭代数组并在哈希表中收集键,并稍后使用间隙存储数据。

var data = [{ 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" }] }],
    csv = function (array) {
        var cols = [],
            collection = Object.create(null),
            i = -1,
            toCSV = function (v) { return isNaN(v)? JSON.stringify(v): v; },
            csv;

        array.forEach(function iter(path) {
            return function (o) {
                path.length || i++;
                Object.keys(o).forEach(function (k) {
                    if (Array.isArray(o[k])) {
                        o[k].forEach(iter(path.concat(k)));
                        return;
                    }
                    var key = path.concat(k).join('.');
                    if (!collection[key]) {
                        cols.push(key);
                        collection[key] = [];
                    }
                    collection[key][i] = o[k];
                });
            };
        }([]));

        csv = cols.map(toCSV).join() + '\n';
        for (i = 0; i < array.length; i++) {
            csv += cols.map(function (k) { return toCSV(collection[k][i]); }).join() + '\n';
        }
        return csv;
    }(data);

console.log(csv);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接