0

I am unsure why do I get undefined after I run console.log(class1.roster), even though I have passed an array to that object in the previous command class1.importRoster(createRoster(filePath));. I would appreciate an explanation and a possible way to see class1.roster have a value. I am using the npm package csv-parser. Here is my MWE and the contents of test.csv

mwe.js

const fs = require ('fs');
const csv = require('csv-parser');
const filePath = 'test.csv'
let classRoster = [];

function schoolClass (subject, period, schoolYear) {
  this.subject = subject;
  this.period = +period;
  this.schoolYear = schoolYear;
  this.importRoster = function (roster) {
    this.roster = roster;
  }
}

function Student (firstName,lastName,ID,grade,period) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.ID = ID;
  this.grade = grade;
  this.period = period;
}

function createRoster (path)  {
  fs.createReadStream(path)
  .pipe(csv())
  .on('data', (data)=>{
    classRoster.push(new Student(data.first_name, data.last_name, data.id, data.grade, data.period))
  })
  .on('end', ()=>{
    console.log("Done");
    return classRoster;
  })
}

class1 = new schoolClass("GER", 3, "19-20");

console.log(class1);

class1.importRoster(createRoster(filePath));

console.log(class1.roster);

test.csv

first_name,last_name,grade,id,period
Blondy,Debrett,10,0217842058,3
Nester,Langstrath,10,2574570346,3
Waldon,Trunby,11,4785462996,8
Lark,Alsopp,11,0039229732,7
Almira,Baistow,12,1272978281,3
Carmela,Abberley,12,7279500295,8
Cristabel,Soanes,10,3086318686,5
Currie,Milton-White,11,8123679871,8
Alexei,Everist,11,2538149622,7
Lina,Hehir,9,1345944671,3
1028
  • 129
  • 7

1 Answers1

0

You're not returning anything here:

function createRoster (path)  {
  fs.createReadStream(path)
  .pipe(csv())
  .on('data', (data)=>{
    classRoster.push(new Student(data.first_name, data.last_name, data.id, data.grade, data.period))
  })
  .on('end', ()=>{
    console.log("Done");
    return classRoster;
  })
}

Here you are calling the stream's on function and passing two arguments - the event to listen for and a function to run when that event is emitted by the stream. The return value of that anonymous function is not consumed by anything.

  .on('end', ()=>{
    console.log("Done");
    return classRoster;
  })

There's some other potential pitfalls here, namely a global classRoster variable that you are pushing data to. If you want this to be a global cache, there is no reason to ever call importRoster as you can just refer to it directly. However, calling createRoster more than once will continue to push data to the same array.

It looks like you should probably restructure your code to expect an asynchronous format. You can use the library's sync API but this might cause performance issues depending on your use case: https://csv.js.org/parse/api/sync/

Because of its simplicity, this is the recommended approach if you don't need scalability and if your dataset fit in memory. It is much easier to use at the expense of not being scalable.

Damon
  • 4,216
  • 2
  • 17
  • 27
  • I am new to the idea of asynchronous functions, how would I get an actual value pushed to class1.roster without going to the sync API? – 1028 Mar 21 '20 at 02:12
  • @1028 There's a few options. You could pass the instance into `createRoster` and set it in the 'end' callback. You could have `createRoster` return the stream and chain the `on('end'...)` outside `createRoster`. You could return a Promise from `createRoster` which is a common way to handle async logic: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise. – Damon Mar 21 '20 at 05:12
  • I have an example of wrapping a csv-parse pipe function with promise - https://stackoverflow.com/q/66402555/3761234 – charsi Feb 27 '21 at 23:57