将 javascript 中的深层嵌套数组转换为 CSV

问题描述 投票:0回答:1

我有一个深层嵌套数组,需要转换为可下载的 CSV。我试图通过迭代每个数组和子数组来构建 CSV 文件。有更有效的方法吗?

这就是嵌套数组的样子:

[{
    id: 123,
    name: "ABC",
    superDepartment: [{
        id: 456,
        name: "PQR",
        keyCompetitor: ["JKL"],
        department: [{
            id: 765,
            name: "XYZ",
            category: [{
                id: 965,
                name: "GHJ",
                keyCompetitor: ["NMK"],
                otherCompetitor: ["SDF", "LKJ"],
                subcategory: [
                    {id: 346, name: "DRJ"},
                    {id: 789, name: "SRE"}        
                ]
            }]
        }]
    }]
}];

CSV 中的预期输出

SBU 超级部门 部门 类别 子类别 主要竞争对手 核心竞争对手 其他竞争对手
ABC
PQR JKL
XYZ
GHJ NMK 自卫队、李光耀
DRJ
SRE

这是我到目前为止所拥有的:

let csvData = [];
downloadData.map(data => {
    if(csvData.length === 0) {
        csvData.push(data.name, "", "", "", csvData.keyCompetitors ? csvData.keyCompetitors[0] : "",
        csvData.coreCompetitors ?  csvData.coreCompetitors.join(): "",
        csvData.otherCompetitors ? csvData.otherCompetitors.join() : "")
    }
});
javascript reactjs arrays tree export-to-csv
1个回答
0
投票

您可以分几个阶段进行:

  • 从嵌套结构中获取行,其中每行都是一个对象,具有为该行指定的属性。假设顶级值属于“SBU”属性。这个函数可以使用递归来处理嵌套,它可以是一个generator函数。

  • 获取上一步中整个数据中使用的不同属性的列表,按具有嵌套内容的属性的优先顺序排列。

  • 使用前面的信息来完成步骤 1 中缺少属性的对象,因此所有对象都以相同的顺序具有相同的属性

  • 将该结果转换为一个矩阵,其中属性名称作为标题行,但使用驼峰式命名法转换为单独的单词。

  • 将该结果转换为 CSV 字符串,使用引号将可能导致歧义的值括起来,转义(双倍)数据中的双引号。您可以使用库来实现此目的,但在这里我为其定义了一个函数。

实施:

// Function to determine whether the argument is an object array or not
const isObjectArray = arr => Array.isArray(arr) && Object(arr[0]) === arr[0];

// Generator function to list the rows encoded by a nested object structure
function* getRows(data, field="SBU") {
    for (const {id, name, ...obj} of data) {
        const categories = [[[field, name]], []];
        for (const [key, value] of Object.entries(obj)) {
            categories[+isObjectArray(value)].push([key, value]);
        }
        yield Object.fromEntries(categories[0]);
        for (const [key, value] of categories[1]) {
            yield* getRows(value, key);
        }
    }
}

// Function to convert camelCase to separate words in title case
const wordify = camelCase =>
    camelCase.replace(/[A-Z][a-z]+/g, " $&").trim()
             .replace(/^[a-z]/, a => a.toUpperCase());

// Function to get the list of properties used by objects in an array
const getColumns = rows => [...
    new Set([
        ...rows.map(row => Object.keys(row)[0]), // Priority columns
        ...rows.flatMap(Object.keys) // Other columns
    ])
];

// Function to complete objects in an array with missing properties
const completeObjects = rows => {
    const template = Object.fromEntries(getColumns(rows).map(column => [column, ""]));
    return rows.map(row => Object.assign({}, template, row));
};

// Function to turn an object array into a matrix of strings, including a header row
const toMatrix = fullRows => [
    Object.keys(fullRows[0]).map(wordify), // Header
    ...fullRows.map(row => Object.values(row).map(String)) // Data
];

// Function to prepare a string to be included as a CSV value
const csvEscape = s =>
    !s.match(/[\n\r," ]/) ? s
    : '"' + s.replaceAll('"', '""') + '"';

// Function to turn a matrix into CSV formatted string
const toCsv = matrix =>
    matrix.map(row => row.map(csvEscape).join(",")).join("\n");

// Example input:
const downloadData = [{
    id: 123,
    name: "ABC",
    superDepartment: [{
        id: 456,
        name: "PQR",
        keyCompetitor: ["JKL"],
        department: [{
            id: 765,
            name: "XYZ",
            category: [{
                id: 965,
                name: "GHJ",
                keyCompetitor: ["NMK"],
                otherCompetitor: ["SDF", "LKJ"],
                subcategory: [
                    {id: 346, name: "DRJ"},
                    {id: 789, name: "SRE"}        
                ]
            }]
        }]
    }]
}];

const csv = toCsv(toMatrix(completeObjects([...getRows(downloadData)])));
console.log(csv);

请注意,输出中没有“核心竞争对手”列,因为它在示例输入中没有出现。

© www.soinside.com 2019 - 2024. All rights reserved.