for a school project I have to rewrite a Json Object using node fs. I wrote a module and if I use deleteCity or addCity on their own, they work perfectly fine. But when I call both after another only one works. In my JSON File there is one array with 10 objects. In my main javascript file I require my module and call upon addCity and deleteCity functions.
//Modules
const fs = require("fs");
//Variablen
const citiesPath = "./cities.json";
//Funktionen
const deleteCity = (name) => {
fs.readFile(citiesPath, "utf-8", (err, jstring) => {
if (err) {
console.log(err);
}
try {
let data = JSON.parse(jstring);
for (let i = 0; i < data.length; i++) {
if (data[i].Name == name) {
data.splice(i, 1);
}
}
fs.writeFile(citiesPath, JSON.stringify(data, null, 2), (err) => {
if (err) {
console.log(err);
}
});
} catch (error) {
console.log(error);
}
});
};
const addCity = (obj) => {
fs.readFile(citiesPath, "utf-8", (err, jstring) => {
if (err) {
console.log(err);
}
try {
let data = JSON.parse(jstring);
data.push(obj);
fs.writeFile(citiesPath, JSON.stringify(data, null, 2), (err) => {
if (err) {
console.log(err);
}
});
} catch (error) {
console.log(error);
}
});
};
const showCity = () => {
fs.readFile(citiesPath, "utf-8", (err, jstring) => {
if (err) {
console.log(err);
}
try {
let data = JSON.parse(jstring);
console.log(data);
} catch (error) {
console.log(error);
}
});
};
//Exporte
module.exports = {
deleteCity,
addCity,
showCity
};
I suppose you are calling both function synchronously, i.e.
deleteCity("London");
addCity({ "Name": "Paris" });
The problem here is that the calls are both asynchronous and the second one will start before the first call terminates, so basically before a city has been deleted.
If this is a school project the simplest solution to fix your code is using the synchronous version of the fs calls fs.readFileSync and fs.writeFileSync:
//Modules
const fs = require("fs");
//Variablen
const citiesPath = "./cities.json";
//Funktionen
const deleteCity = (name) => {
const jstring = fs.readFileSync(citiesPath, "utf-8");
let data = JSON.parse(jstring);
for (let i = 0; i < data.length; i++) {
if (data[i].Name == name) {
data.splice(i, 1);
}
}
fs.writeFileSync(citiesPath, JSON.stringify(data, null, 2));
};
const addCity = (obj) => {
const jstring = fs.readFileSync(citiesPath, "utf-8");
let data = JSON.parse(jstring);
data.push(obj);
fs.writeFileSync(citiesPath, JSON.stringify(data, null, 2));
};
const showCity = () => {
const jstring = fs.readFileSync(citiesPath, "utf-8");
let data = JSON.parse(jstring);
console.log(data);
};
//Exporte
module.exports = {
deleteCity,
addCity,
showCity
};
Note that you don't need to catch the errors only to log them inside your synchronous functions. If an error is thrown and not caught, Node.js will log it for your.
Related
The scenario is I have two large CSV files csv1.csv and csv2.csv. In both the files, there is an email column and I have to read csv1.csv row by row and check if the email exists in csv2.csv and if matches write the row of csv2.csv in csv3.csv. I have tried read stream as well but it is not working as expected. Any guidance or help is appreciated.
Thanks to all in advance.
Following are the CSV files
csv1.csv
email,header1,header2
test1#example.com,test1,test1
test2#example.com,test2,test2
test3#example.com,test3,test3
test4#example.com,test4,test4
test5#example.com,test5,test5
csv2.csv
email,header1,header2
test4#example.com,test4,test4
test5#example.com,test5,test5
test6#example.com,test6,test6
test7#example.com,test7,test7
test8#example.com,test8,test8
Following is the code that I tried
const fs = require('fs');
const csv = require('fast-csv')
class CsvHelper {
static write(filestream, rows, options) {
return new Promise((res, rej) => {
csv.writeToStream(filestream, rows, options)
.on('error', err => rej(err))
.on('finish', () => res());
});
}
constructor(opts) {
this.headers = opts.headers;
this.path = opts.path;
this.writeOpts = {
headers: this.headers,
includeEndRowDelimeter: true
};
}
create(rows) {
return CsvHelper.write(fs.createWriteStream(this.path, { flags: 'a' }), rows, { ...this.writeOpts });
}
append(rows) {
return CsvHelper.write(fs.createWriteStream(this.path, { flags: 'a' }), rows, {
...this.writeOpts,
writeHeaders: false,
});
}
}
class Helper {
async matchCsv (outerRow) {
try {
const filePath2 = "csv2.csv";
const filePath3 = "csv3.csv";
let row = [];
const csvFile = new CsvHelper({
path: filePath3,
headers: ["Email", "Active"]
});
return new Promise((resolve, reject) => {
fs.createReadStream(filePath2)
.on("error", err => {
reject(err);
})
.pipe(csv.parse({headers: true}))
.on("error", err => {
reject(err);
})
.on("data", async innerRow => {
if(outerRow["email"] === innerRow["email"]) {
console.log("====================");
console.log("match found");
console.log(innerRow);
console.log("====================");
row.push([innerRow["email"], "yes"]);
console.log("row: ", row);
}
})
.on("finish", async() => {
if (!fs.existsSync(filePath3)) {
await csvFile.create(row).then(() => {
resolve("Done from matchCsv");
})
} else {
await csvFile.append(row).then(() => {
resolve("Done from matchCsv");
})
}
})
});
} catch (err) {
throw(err);
}
}
async generateCsv () {
try {
const filePath1 = "csv1.csv";
return new Promise((resolve, reject) => {
fs.createReadStream(filePath1)
.on("error", err => {
reject(err);
})
.pipe(csv.parse({headers: true}))
.on("error", err => {
reject(err);
})
.on("data", async outerRow => {
const result = await this.matchCsv(outerRow);
console.log("result: ", result);
})
.on("finish", () => {
resolve("Generated csv3.csv file.");
});
});
} catch (err) {
throw(err);
}
}
}
async function main() {
const helper = new Helper();
const result = await helper.generateCsv()
console.log(result);
}
main();
So the question is a little confusing, but I think I know what you want. Here's what I would do to check if the email exists. It will add all the rows to an array, cycle through them, then if the email address matches the email you're looking for, it will do something else... I think you said you wanted to write to a csv file again with the row, but that should be simple enough.
const csv = require('csv-parser');
const fs = require('fs');
const filepath = "./example_data.csv";
const emailAdd = "myemail#email.com";
var rowsArr = [];
fs.createReadStream(filepath)
.on('error', () => {
// handle error
})
.pipe(csv())
.on('data', (row) => {
rowsArr.push(row);
})
.on('end', () => {
for (var i = 0; i <= rowsArr.length; i++) {
if (rowsArr[i].emailAddress == emailAdd) {
//do something
}
}
})
Im trying to read multiple xml files and parse data from them and i managed to do that but now new problem appeared.
allData variable is never changed, no matter what i do. What am i supposed to do here?
I dont know what to do or what to try, this is my first time working with files and im honestly surprised ive managed to come this far.
var parseString = require('xml2js').parseString;
var fs = require('fs')
var allData = {
store: []
}
function readFiles(__dirname, onFileContent, onError) {
fs.readdir(__dirname + '\\parse\\', function (err, filenames) {
if (err) {
return;
}
filenames.forEach(function (filename) {
console.log(filename)
fs.readFile(__dirname + '\\parse\\' + filename, 'utf-8', function (err, content) {
if (err) {
console.log(err)
return;
}
parseString(content, function (err, result) {
let tempObj = {}
let data = result.storeD[0]
if (data.name) {
tempObj['name'] = data.name[0];
}
if (data.price) {
tempObj['price'] = data.price[0];
}
//more of the same type of code
console.log(tempObj)
//output: { name: 'Data1', price: '1000' }
allData.store.push(tempObj)
})
})
})
});
console.log("All data: ",allData)
//Outputs once at the begining
//output: All data: { store: [] }
}
readFiles(__dirname)
SOLVED
adjusted code to use.readFileSync()(removed callback function) and now it works.
var parseString = require('xml2js').parseString;
var fs = require('fs')
var allData = {
store: []
}
function readFiles(__dirname, onFileContent, onError) {
fs.readdir(__dirname + '\\parse\\', function (err, filenames) {
if (err) {
return;
}
filenames.forEach(function (filename) {
console.log(filename)
let file = fs.readFileSync(__dirname + '\\parse\\' + filename, 'utf-8')
parseString(file, function (err, result) {
let tempObj = {}
let data = result.storeD[0]
if (data.name) {
tempObj['name'] = data.name[0];
}
if (data.price) {
tempObj['price'] = data.price[0];
}
//more of the same type of code
console.log(tempObj)
//output: { name: 'Data1', price: '1000' }
allData.store.push(tempObj)
})
})
console.log("All data: ",allData)
});
//Outputs once at the begining
//output: All data: { store: [] }
}
readFiles(__dirname)
The .readdir() and .readFile() methods are async, so in fact the console.log() is executed before all of the readFile operations.
In order to access the allData variable after these operations are complete, you have to either make them sync using .readFileSync() instead or you need to promisify the .readFile() method and wait for all of the promises to resolve.
I'm trying to launch an exe file from an Electron app with React/Redux.
From the component i'm doing dispatch(launch(titleId, titleName))
The problem is i'm getting path undefined when i'm waiting for readFolders() async.
Any idea what i'm doing wrong and what should i change in my aproach?
Thanks in advance!
launch.js
export const launch = async (titleId, titleName) => {
const path = await readFolders(titleId);
console.log('path:', path) //undefined
execFile(path, (err, data) => {
if (err) {
console.log('err', err);
} else if (data) {
console.log('data:', data)
} else {
console.log('success');
}
});
return {
type: 'LAUNCH',
};
};
readFolders.js
import fs from 'fs';
import { homedir } from 'os';
const fsPromises = fs.promises;
const isExeFile = file => file.match(/.*\.exe$/i);
export const readFolders = async titleId => {
const userDir = homedir();
const folderPath = `${userDir}/downloads`;
const fullPath = `${folderPath}/${titleId}`;
try {
const contents = await fsPromises.readdir(fullPath);
contents.forEach(async item => {
if (isExeFile(item)) {
console.log('isExeFile');
return `${fullPath}/${item}`;
}
try {
const nestedFolder = await fsPromises.readdir(`${fullPath}/${item}`);
nestedFolder.forEach(nestedItem => {
if (isExeFile(nestedItem)) {
return `${fullPath}/${item}/${nestedItem}`;
}
return null;
});
} catch (err) {
console.log('err:', err);
}
});
} catch (err) {
console.log('err main:', err);
}
};
Edit:
I also tried this way and now const path = await readFolders(titleId); returns the correct result, but this way eslint is complaining (https://eslint.org/docs/rules/no-async-promise-executor) and it doesn't feel like a good solution.
return new Promise(async (resolve, reject) => {
try {
const contents = await fsPromises.readdir(fullPath);
contents.forEach(async item => {
if (isExeFile(item)) {
console.log(`${fullPath}/${item}`);
return resolve(`${fullPath}/${item}`);
}
try {
const nestedFolder = await fsPromises.readdir(`${fullPath}/${item}`);
nestedFolder.forEach(nestedItem => {
if (isExeFile(nestedItem)) {
console.log(`${fullPath}/${item}/${nestedItem}`);
return resolve(`${fullPath}/${item}/${nestedItem}`);
}
return null;
});
} catch (err) {
console.log('err:', err);
reject(err);
}
});
} catch (err) {
console.log('err main:', err);
reject(err);
}
});
Missing return at the end. When you return in forEach, It returns from callback anonymous function only. return ${fullPath}/${item}/${nestedItem};
For more you can read my blog on it:
https://medium.com/#deepak_v/weird-part-how-to-break-the-loop-in-javascript-8bba3e658267
Updated code:
export const readFolders = async (titleId) => {
const userDir = homedir();
const folderPath = `${userDir}/downloads`;
const fullPath = `${folderPath}/${titleId}`;
try {
const contents = await fsPromises.readdir(fullPath);
let path = "";
contents.some(async (item) => {
if (isExeFile(item)) {
console.log("isExeFile");
path = `${fullPath}/${item}`;
return path;
}
try {
const nestedFolder = await fsPromises.readdir(`${fullPath}/${item}`);
const found = nestedFolder.some((nestedItem) => {
if (isExeFile(nestedItem)) {
path = `${fullPath}/${item}/${nestedItem}`;
return path;
}
return false;
});
if (found) return path;
else return false;
} catch (err) {}
});
return path;
} catch (err) {
console.log("err main:", err);
}
};
Calling Oracle SP which fills the data in the record set.
Fetching rows through the recordset getrows() method.
When called the getrows method and pass a function for fetching the inner methods always run in the end.
The first method calls the inner method, and the inner method received the data, and recordset is being send to inner function.
Example in below code functioncall method return empty data and then responseObj.push run. After this the getrows method process.
function lookups(req, res, next) {
rows = functioncall(context);
responesObj.push({ "Return Data": rows });
}
function simpleProcedureExecute(query, bindvars, opts = {}) {
return new Promise((resolve, reject) => {
oracledb.getConnection(
conn,
function (err, connection) {
if (err) throw err;
connection.execute(
query,
bindvars,
function (err, result) {
if (err) {
console.error(err.message);
reject(err);
}
procJson = [];
function processResultSet() {
console.log("internal method");
console.log(result.outBinds.result);
try {
result.outBinds.result.getRows(1000, function (err, rows) {
if (err) console.log(err);
if (rows.length) {
for (var i = 0; i < rows.length; i++) {
procJson.push({});
for (var j = 0; j < result.outBinds.result.metaData.length; j++) {
procJson[i][result.outBinds.result.metaData[j].name.toLowerCase()] = rows[i][j];
}
}
processResultSet();
return;
}
resultSet.close(function (err) {
if (err) console.error(err.message);
conn.release(function (err) {
if (err) console.error(err.message);
});
});
});
}
catch (err) {
console.log(err);
}
}
processResultSet();
}
);
}
);
resolve(procJson);
});
}
The most obvious issue is the timing of when you're resolving the promise - which is way too soon. You're invoking resolve outside of the call to oracledb.getConnection. You don't have the connection yet, haven't executed the query yet, and haven't collected the rows yet. You have to do all of that first, then invoke resolve and pass along the data.
This is one of the harder things to wrap your head around when you're new to Node.js. See this video, it may help some: https://www.youtube.com/watch?v=iAdeljxq_hs&t=0s&index=2&list=PL_lVOJzXeE__1Kh3ko0F-39-cpNLPXbJL
Also, see this series which covers different async patterns in Node.js. Most Node.js developers start with callbacks and then move to alternative patterns for async work: https://jsao.io/2017/06/how-to-get-use-and-close-a-db-connection-using-various-async-patterns/
Finally, here's an example of how one can iterate a result set with async/await:
const oracledb = require('oracledb');
const config = require('./dbConfig.js');
async function runTest() {
let conn;
let result;
try {
conn = await oracledb.getConnection(config);
result = await conn.execute(
'select * from all_objects where rownum < 100',
[],
{
resultSet: true
}
);
let row;
while (row = await result.resultSet.getRow()) {
console.log(row);
}
} catch (err) {
console.error(err);
} finally {
if (result && result.resultSet) {
try {
await result.resultSet.close();
} catch (err) {
console.error(err);
}
}
if (conn) {
try {
await conn.close();
} catch (err) {
console.error(err);
}
}
}
}
runTest();
Depending on the workload, it may be better to get more rows at once with getRows:
const oracledb = require('oracledb');
const config = require('./dbConfig.js');
async function runTest() {
let conn;
let result;
try {
conn = await oracledb.getConnection(config);
result = await conn.execute(
'select * from all_objects where rownum < 100',
[],
{
resultSet: true
}
);
let rows = await result.resultSet.getRows(50);
while (rows.length) {
for (let x = 0; x < rows.length; x += 1) {
console.log(rows[x]);
}
rows = await result.resultSet.getRows(50);
}
} catch (err) {
console.error(err);
} finally {
if (result && result.resultSet) {
try {
await result.resultSet.close();
} catch (err) {
console.error(err);
}
}
if (conn) {
try {
await conn.close();
} catch (err) {
console.error(err);
}
}
}
}
runTest();
After getting direction from Dan to use getrows directly without calling inner function. Find below my code to resolve it.
async function simpleProcedureExecute(query, bindvars, opts = {}) {
let rowss;
let conn;
let procJson = [];
try {
conn = await oracledb.getConnection();
result = await conn.execute(query, bindvars);
rowss = await result.outBinds.result.getRows(1000);
if (rowss.length) {
for (var i = 0; i < rowss.length; i++) {
procJson.push({});
for (var j = 0; j < result.outBinds.result.metaData.length; j++) {
procJson[i][result.outBinds.result.metaData[j].name.toUpperCase()] = rowss[i][j];
}
}
}
return procJson;
} catch (err) {
console.log(err);
} finally {
if (conn) { // conn assignment worked, need to close
try {
await conn.close();
} catch (err) {
console.log(err);
}
}
}
}
I am new in Node.js and I want to pass data array to controller. But I am unable to insert for loop data in array and I also want to get result data out side function.
router.get("/list-group", function(req, res) {
sess = req.session;
var response = {};
if (sess.loginData) {
var TableData = [];
var i = {};
var result = [];
mongoOp.users_group.find({
group_id: req.query.group_id
}, function(e, d) {
var len = d[0].assign_user_id.length;
var assignuserid = d[0].assign_user_id;
for (var i = 0; i < assignuserid.length; i++) {
var assignid = assignuserid[i];
mongoOp.users.find({
_id: assignid
}, function(err, data) {
// This is result array
result[i] = data;
})
}
// And I want to print result array here
console.log(result);
});
} else {
response = {
"error": true,
"message": "Invalid Login"
};
res.json(response);
}
})
I would make use of async and await
router.get('route', (req, res) => {
// ...
users.find(query, (err, d) => {
try {
// ...
var results = []
for (var data of array) {
const result = await fetchUser(data)
results.push(result)
}
console.log(results)
} catch (err) {
console.log('some error occured', err)
}
})
})
async function fetchUser(id) {
return new Promise((resolve, reject) => {
users.find({ _id: id }, (err, data) => {
return err ? reject(err) : resolve(data)
})
})
}
If you're not that familiar with async and await I would recommend this video
u need read about async and callbacks in javascript. An alternative is read about async and await.