从节点中解析的 csv 文件构建对象数组

Posted

技术标签:

【中文标题】从节点中解析的 csv 文件构建对象数组【英文标题】:Building array of objects from parsed csv files in node 【发布时间】:2015-08-25 12:04:55 【问题描述】:

我有多个 csv 格式的文件

型号1A 模型1B model2A model2B

每个 csv 都是一个数组,即model1A = [1, 1, 1]

我想解析这些 csvs 并创建一个包含所有这些模型的数组,其中数组中的每个元素都是对应于一个特定模型的对象,即

finalArray = [ 
   
    "model" :   "model1",
    "A"     :   [1, 1, 1],
    "B"     :   [2, 2, 2]
  ,
   
    "model" :   "model2",
    "A"     :   [3, 3, 3],
    "B"     :   [4, 4, 4]
  
]

我目前的代码是

var csv = require('csv');
var fs = require('fs');
var parser = csv.parse();
var util = require('util');
var junk = require('junk');
var _ = require('lodash');
var models = [];


fs.readdir(__dirname+'/data', function(err, files) 
    var model = ;
    _.forEach(files, function(n, key) 

        console.log('Analysing file: ' + n);
        var modelName;
        var modelNum;
        var modelParam;


        modelNum = n.match(/\d+/)[0];
        modelName = 'model' + modelNum;
        modelParam = (n.substring(0, n.indexOf('.'))).replace(modelName,'');

        model.model = modelName;
        model[modelParam] = [];
        models.push(model);

        //if (Object.keys(model).length === 3) 
        //    models.push(model);
        //    model = ;
        //


        fs.createReadStream(__dirname+'/data/'+n).pipe(csv.parse()).pipe(csv.transform(function(row) 
            model[modelParam].push(row);

        )).on('readable', function()
            while(this.read())
        ).on('end', function() 
            console.log('finished reading file ' + n);
            if (key === (files.length - 1)) 
                fs.writeFile('result.json', JSON.stringify(models), function (err) 
                    if (err) throw err;
                    console.log(models.length + ' model(s) parsed');
                    console.log('done');
                );
            

        ).on('error', function(error) 
            console.log(error);
        );    
    );
);

我知道我的一个问题是我很快就会将模型推送到数组中,从而产生以下形式的最终数组,其中model1model2 覆盖

[  model: 'model2', A: [], B: [] ,
   model: 'model2', A: [], B: [] ,
   model: 'model2', A: [], B: [] ,
   model: 'model2', A: [], B: []  ]

这就是我尝试这段代码的原因

if (Object.keys(model).length === 3) 
  models.push(model);
  model = ;

但这当然行不通,因为fs.createReadStream 是异步的,我正在用model = 清除模型,然后它才能正常运行。

我现在处于这样一个阶段,我觉得我在兜圈子,只会让事情变得更糟。我想创建一些更通用的东西,但是,现在我很高兴让它适用于这里介绍的案例,然后我可以考虑改进它。

任何帮助将不胜感激!


更新 1

按照 saquib khan 的建议,将 var model = 移动到循环内有助于让我更接近我的目标,但它仍然不正确。下面是目前的结果

[
    
        "model": "model1",
        "A": [
            [
                "1"
            ],
            [
                "2"
            ],
            [
                "3"
            ],
            [
                "4"
            ]
        ]
    ,
    
        "model": "model1",
        "B": [
            [
                "1"
            ],
            [
                "2"
            ],
            [
                "3"
            ],
            [
                "4"
            ]
        ]
    ,
    
        "model": "model2",
        "A": [
            [
                "1"
            ],
            [
                "2"
            ],
            [
                "3"
            ],
            [
                "4"
            ]
        ]
    ,
    
        "model": "model2",
        "B": [
            [
                "1"
            ],
            [
                "2"
            ],
            [
                "3"
            ],
            [
                "4"
            ]
        ]
    
]

更新 2

同样遵循 Denys Denysiuk 的建议,结果更接近我想要的,但仍然很短

[
    
        "model": "model1",
        "A": [
            "1",
            "2",
            "3",
            "4"
        ]
    ,
    
        "model": "model1",
        "B": [
            "1",
            "2",
            "3",
            "4"
        ]
    ,
    
        "model": "model2",
        "A": [
            "1",
            "2",
            "3",
            "4"
        ]
    ,
    
        "model": "model2",
        "B": [
            "1",
            "2",
            "3",
            "4"
        ]
    
]

如果我能以某种方式迭代最终的对象数组,合并具有匹配 model 名称的对象,这将起作用。我目前正在查看lodash docs 以查看是否可以解决问题。如果有的话,我会在这里发帖。

【问题讨论】:

看来,row 是数组。你可以试试model[modelParam].push(row[0]); 【参考方案1】:

试试这个:

fs.readdir(__dirname+'/data', function(err, files) 

    _.forEach(files, function(n, key) 

        console.log('Analysing file: ' + n);            

        var modelNum = n.match(/\d+/)[0];
        var modelName = 'model' + modelNum;
        var modelParam = (n.substring(0, n.indexOf('.'))).replace(modelName,'');

        var model = ;
        var isNewModel = true;
        for(var i = 0; i < models.length; i++) 
            if(models[i].model == modelName) 
               model = models[i];
               isNewModel = false;
               break;
            
        
        if(isNewModel) 
            model.model = modelName;
            models.push(model);
        

        model[modelParam] = [];

        fs.createReadStream(__dirname+'/data/'+n).pipe(csv.parse()).pipe(csv.transform(function(row) 
            model[modelParam].push(row[0]);

        )).on('readable', function()
            while(this.read())
        ).on('end', function() 
            console.log('finished reading file ' + n);
            if (key === (files.length - 1)) 
                fs.writeFile('result.json', JSON.stringify(models), function (err) 
                    if (err) throw err;
                    console.log(models.length + ' model(s) parsed');
                    console.log('done');
                );
            

        ).on('error', function(error) 
            console.log(error);
        );    
    );

【讨论】:

你能解释一下问题是什么以及你建议如何解决它 问题出在每次迭代的新模型中。解决方案是尝试查找现有模型,如果找不到 - 创建新模型。【参考方案2】:

您的代码中有一个很小的编码错误。

var model = ; 应该在 forEach 循环内。

试试下面的代码:

var csv = require('csv');
var fs = require('fs');
var parser = csv.parse();
var util = require('util');
var junk = require('junk');
var _ = require('lodash');
var models = [];


fs.readdir(__dirname+'/data', function(err, files) 

    _.forEach(files, function(n, key) 

        console.log('Analysing file: ' + n);
        var model = ;
        var modelName;
        var modelNum;
        var modelParam;


        modelNum = n.match(/\d+/)[0];
        modelName = 'model' + modelNum;
        modelParam = (n.substring(0, n.indexOf('.'))).replace(modelName,'');

        model.model = modelName;
        model[modelParam] = [];
        models.push(model);

        //if (Object.keys(model).length === 3) 
        //    models.push(model);
        //    model = ;
        //


        fs.createReadStream(__dirname+'/data/'+n).pipe(csv.parse()).pipe(csv.transform(function(row) 
            model[modelParam].push(row);

        )).on('readable', function()
            while(this.read())
        ).on('end', function() 
            console.log('finished reading file ' + n);
            if (key === (files.length - 1)) 
                fs.writeFile('result.json', JSON.stringify(models), function (err) 
                    if (err) throw err;
                    console.log(models.length + ' model(s) parsed');
                    console.log('done');
                );
            

        ).on('error', function(error) 
            console.log(error);
        );    
    );
);

【讨论】:

【参考方案3】:

Node.js 是事件驱动的,所以也许你可以使用 Event 模块来构建你的代码:https://nodejs.org/api/events.html

您的问题似乎是您正在覆盖数组中以前的条目,所以也许您应该只在前一个完成写入所需的所有内容时才进入下一步(读取另一个 CSV?)。

您可以使用 Event 将此逻辑添加到您的代码中。

【讨论】:

以上是关于从节点中解析的 csv 文件构建对象数组的主要内容,如果未能解决你的问题,请参考以下文章

csv转换obj

Jmeter 解析json文件(多条件获取数组中某个节点的值)

转换对象数组并附加到 csv 文件

Angular 2 - 从嵌套数组创建 CSV 文件

解析 JSON 文件(本地存储),从而快速创建对象数组

在 iOS 中解析 json 并将值添加到对象数组