Is there a npm module which converts a tab delimited file to a JSON object so i could look up the data by certain properties.
Example: The file would looks like below,
name sex age
A M 20
B F 30
C M 40
D F 50
JSON
[
{
"name": "A",
"sex": "M",
"age": "20"
},
{
"name": "B",
"sex": "F",
"age": "30"
},
/* Etc. */
]
Sometimes i prefer not using node modules so that I can run these scripts without any setup....
IE. nodejs ./convert-to-csv.js
Here is a nodejs script in case you choose not to use a node module.
var fs = require("fs");
fs.readFile("./birthrate_poverty.txt","utf8", function(err, data){
var rows = data.split("\n");
var json = [];
var keys = [];
rows.forEach((value, index)=>{
if(index < 1){// get the keys from the first row in the tab space file
keys = value.split("\t");
}else {// put the values from the following rows into object literals
values = value.split("\t");
json[index-1] = values.map((value, index) => {
return {
[keys[index]]: value
}
}).reduce((currentValue, previousValue) => {
return {
...currentValue,
...previousValue
}
});
}
})
// convert array of objects into json str, and then write it back out to a file
let jsonStr = JSON.stringify(json);
fs.writeFileSync("./birthrate_poverty.json", jsonStr, {encoding: "utf8"})
});
Yes, csvtojson and the delimiter can be anything not only commas.
Example:
const csvFilePath='FILE'
const csv=require('csvtojson')
csv({delimiter:"\t"})
.fromFile(csvFilePath)
.on('json',(jsonObj)=>{
console.log(jsonObj);
})
.on('done',(error)=>{
console.log('end');
})
Related
How can I replace the tokens __fruit_type__, __clothing_type__, __fitness_equipment__, __meditation_app__ in collection.js with values from values.js?
I am trying to achieve this with the string.replace() Method
collection.js
{
"collection" : [
{
"fruit": "__fruit_type__",
"clothings":{
"item": "__clothing_type__}"
}
},
{
"fitness": "__fitness_equipment__",
"mindfulness": "app called __meditation_app__"
}
]
}
values.js
{
"clothing_type": "winter",
"fruit_type": "apple",
"fitness_equipment": "treadmill",
"meditation_app": "calm"
}
replace.js
const fs = require("fs").promises;
async function dataReader(filePath, data) {
const result = await fs.readFile(filePath);
try {
return JSON.parse(result);
} catch (e) {
console.error(e);
}
}
//read values
async () => {
const value = await dataReader("./values.json");
const clothing_type = value.clothing_type;
const fruit_type = value.fruit_type;
const fitness_equipment = value.fitness_equipment;
const meditation_app = value.meditation_app;
//read collection.json
const data = await jsonReader("./collections.json");
//replace tokens in `collection.js` with `values.js`
};
USING REGEX !!
Now you have a collection.json (like a template) file.
And values.json (a filler for the template), which has a set of values that will be placed in the template (collection.json).
We assume that keys of values.json file are the fillers in collection.json
if a key in values.json is clothing_type then the placeholder is __clothing_type__
We can now iterate through the keys of values.json and look for a match in the collection.json with two underscores in the prefix and suffix
///json will be parsed after filling the value
/// for this example i'll store the both the json files in varaible
const collections = `{
"collection" : [
{
"fruit": "__fruit_type__",
"clothings":{
"item": "__clothing_type__}"
}
},
{
"fitness": "__fitness_equipment__",
"mindfulness": "app called __meditation_app__"
}
]
}`
/// naming the values.json as fillers as this may confuse with key,values in object
const fillers = `{
"clothing_type": "winter",
"fruit_type": "apple",
"fitness_equipment": "treadmill",
"meditation_app": "calm"
}`
function replaceFiller(template_collections,fillers){
/// to loop through the keys of fillers or values.json we have parse this fisrt
const fillersObj =JSON.parse(fillers);
console.log(fillersObj);
//// we dont want to modify the argument varaible "template_collection"
/// so we will use a variable to store the template collection
/// and mutate them arbitrarily
let resultJson = template_collections;
/// looping through the keys of fillersObj
for(let fillerKey of Object.keys(fillersObj)){
/// here the filler key will be "clothing_type" ,"fruit_type" on every iteration
// findig a match for the current key(fillerKey) with underscores in the collections.json and replacing them with the current keys value in fillersObj
resultJson = resultJson.replace(`__${fillerKey}__`,fillersObj[fillerKey]);
console.log(fillerKey,fillersObj[fillerKey]);
}
return JSON.parse(resultJson);
}
console.log(replaceFiller(collections,fillers));
Caveats
Values. json folder should never consist of nested objects.
I have also had the same task with nested objects as fillers; I had to write a lot of code to flatten the objects and then replace tokens.
This code is fine for simple cases like this. If you are going to handle large objects, then use this library jsonpath_object_transform
I'm trying to generate a number that doesn't replace another already existed number in a json file (orders.json) but in my discord.js bot everytime the command which makes the number keeps making the same number 002 and even creates it again when it already exists replacing the data in the json file which makes alot of issues with me
const json = require('../../orders.json')
const keys = Object.keys(json).map(el => parseInt(el));
const nums = [];
let i = 1;
while (i < 999) {
if (!keys.includes(i)) {
nums.push(`${i}`.padStart(3, '0'));
}
i++;
}
orderID = nums[1]
the generated number is orderID and it's not supposed to replace the same one in the json file
for example the json file has data like this
{
"002": "some data here"
}
now its supposed to create the number 003 since 002 already exists in the json file but it creates the same number replacing the data which causes alot of issues
this is used for a discord bot which writes "orders" by numbers which aren't supposed to do that
EDIT: using Richard's answer
json =
{ "001": "Something"
, "002": { "message": "And more stuff" }
, "003": 42
, "Suddenly": "Okay"
}
const keys = Object.keys(json).map(el => parseInt(el));
console.log(keys);
const nums = [];
i = 1;
while (i < 999) {
if (!keys.includes(i)) {
nums.push(`${i}`.padStart(3, '0'));
}
i++;
}
orderID = nums[0]
console.log(orderID);
with that code the console shows:
1
2
3
NaN
004
now I simply replaced json = { "001": "Something" , "002": { "message": "And more stuff" } , "003": 42 , "Suddenly": "Okay" } with const json = require ('../../orders.json') while the orders.json has this data
{
"001": {
"some data here"
}
}
it only logs "001" which already exists in the json
The first unused number will be nums[0]. Maybe that is what you need to use as the next orderID.
json =
{ "001": "Something"
, "002": { "message": "And more stuff" }
, "003": 42
, "Suddenly": "Okay"
}
const keys = Object.keys(json).map(el => parseInt(el));
console.log(keys);
const nums = [];
i = 1;
while (i < 999) {
if (!keys.includes(i)) {
nums.push(`${i}`.padStart(3, '0'));
}
i++;
}
orderID = nums[0]
console.log(orderID);
I learned how to create structured json file from csv file that generated from google form. However, produced json object contains time header because google form generates that and it saved to csv before creating json object from it.
Here is what I tried to solve this:
let inputCsv = `"Timestamp","Enter First Name:","Enter Middle Initial","Enter Last Name:","Enter UIN:","Are you attending the event?"
"2019/02/22 12:41:56 PM CST","Jonathan","Samson,"Rowe","670168228","No"
"2019/02/22 12:44:56 PM CST","Phil","Aspilla","beltran","6689144343","Yes"`
function convertToJson(inputCsv) {
//inputCsv passed from readfile function
/* Split input string by `,` and clean up each item */
var lines = inputCsv.split('\n');
lines.splice(0,1);
var arrayCsv = [];
for (var line = 0; line < lines.length; line++){
const lineArray = lines[line].split(',').map(s => s.replace(/"/gi, '').trim());
lineArray.splice(0,1);
arrayCsv[line] = lineArray;
}
const outputJson = [];
console.log(arrayCsv);
}
convertToJson(inputCsv)
basically I want to get rid of extra time header (like `2019/02/22 12:41:56 PM CST) every line of input csv file when I create structured json object from it, so here is my desired output:
update: desired output json file
[{
"uin": "123456789",
"studentInfo": {
"firstName": "Jonathan",
"middleName": "Samson",
"lastName": "Rowe",
"rsvpStatus": "Yes"
}
},
{
"uin": "123456788",
"studentInfo": {
"firstName": "phil",
"middleName": "Aspilla",
"lastName": "beltran",
"rsvpStatus": "No"
}
}]
I am not sure how regular expression works in javascript and I want to produce clean structured json object from csv that comes from google form. How can I make this happen? Any way to do this? Thanks
You need to build object in desired format and than push in array.
let inputCsv = `"Timestamp","Enter First Name:","Enter Middle Initial","Enter Last Name:","Enter UIN:","Are you attending the event?"
"2019/02/22 12:41:56 PM CST","Christ","A","coda","670168228","No"
"2019/02/22 12:44:56 PM CST","jack","NA","Harvey","6689144343","Yes"`
function convertToJson(inputCsv) {
const keyMapping = {
"\"Enter First Name:\"" : 'firstName',
"\"Enter Middle Initial\"": 'middleName',
"\"Enter Last Name:\"": 'lastName',
"\"Enter UIN:\"":'uin',
"\"Are you attending the event?\"":'rsvp'
}
var lines = inputCsv.split('\n');
let [keys] = lines
keys = keys.split(',').splice(1,)
lines.splice(0,1);
var arrayCsv = [];
for (var line = 0; line < lines.length; line++){
const lineArray = lines[line].split(',').splice(1,).reduce((o,s,i) =>((o[keyMapping[keys[i]]] = s.replace(/"/gi, '').trim()),o),{});
// here i am creating object in desired format
let obj = {uin:lineArray['uin'],studentInfo:{firstName:lineArray}}
arrayCsv[line] = obj;
}
const outputJson = [];
console.log(arrayCsv);
}
convertToJson(inputCsv)
I'm trying to get my head around NodeJS by writing a few test apps. I've managed okay so far but the one thing that's tripping me up is the following.
I've got it reading a directory for folders, and then parsing the settings.json files within each folder (so I can build a UI on the fly based on the folder contents). The problem is, I can't seem to "step down a level" with the JSON object.
For example, coming from a background in php where you could do something like the following to 'step' through the array:
<?php
$arr = [
'folder1' =>
[ 'name' => 'test',
'icon' => 'icon-test'
],
'folder2' =>
[ 'name' => 'test-2',
'icon' => 'icon-test-2'
]
];
foreach($arr as $k => $v){
// folder1 level
foreach($v as $k2 => $v2){
// name / icon level.
echo $k2 . ' is ' . $v2;
}
}
?>
Is there an equivalent in JS? I can do the first "level" by doing this:
function getDirectories (srcpath) {
return fs.readdirSync(srcpath)
.filter(file => fs.lstatSync(path.join(srcpath, file)).isDirectory())
}
var d = getDirectories('modules');
var modules = {};
// the following reads the json in each dir into an object like:
// { 'dirname': { 'key1': 'val1', 'key2': 'val2' }, 'dirname2'... }
for(var p in d){
modules[d[p]] = JSON.parse(fs.readFileSync('./modules/'+d[p]+'/config.json', 'utf8'));
}
for(var k in modules){
console.log(k);
}
But ideally I need to extract "name" and "icon" from the JSON. I can't seem to find any way of doing this.
I understand this is probably messy but I'm just getting my head around NodeJS. For full clarity, directory structure and my simple JSON files below:
modules directory structure
modules
|____ test
|____ config.json
|____ test-2
|____ config.json
config.json (example)
{
"name": "test",
"icon": "test-icon"
}
for(var module of modules){
console.log(module.name,module.icon);
//or over all
for(var value of module) console.log(value);
}
The for...of loops over values. With for..in (looping over keys) :
for(var name in modules){
var module=modules[name];
for(key in module){
console.log(name+"/"+key+":"+module[key]);
}
}
You're currently using a for in loop, and while that works, when iterating over arrays you ideally want to use for next loops.
Here is an example:
for (var i = 0; i < folders.length; i++) {
var folder = folders[i]
/* work with directory .. */
for (var j = 0; j < folder.items.length; j++){
var item = folder.items[j]
/* work with item.. */
}
}
The reason for not using for in when iterating over array is because an array can contain objects that aren't actually part of the array, i.e a function.
Using a for in loop, you will iterate over the array contains, and then any object/function attached to that array.
for next loops will only iterate over the array contents.
I don't know do you have only 1 subdirectory or can that be multiple? If it can be any depth use a recursive function like:
function iterateFolder(array) {
for (var i = 0; i < array.length; i++) {
if ($.isArray(array[i]))
iterateFolder(array[i]);
else
//Add to global variable for example or parse just the values
}
}
try doing a for of loop and not a for in loop
// example from a old proj
// your code has in
// if you are using es6 syntax, you should use let instead of var
for (let element of section.elements) {
let $element;
$element = $parent.find(`label[for=${element._data_text}]`);
$element.html(`<span>${element[lang]}</span>`);
}
here is a working example with my json
import lang_data from "../data/languages.json";
$(function () {
console.log("onload hit");
$("[data-language]").on("click", function () {
let $this = $(this);
let data = $this.data();
let lang = data.language;
$("[data-language]").removeClass("active");
$this.addClass("active");
switchLanguage(lang);
return true;
});
})
function switchLanguage(lang) {
for (let section of lang_data.sections) {
// the form is loaded by JS. I could not add a data-text to it.
if (section.id === "form1_handle_after") {
let $parent = $("#form1");
for (let element of section.elements) {
let $element;
if (element._data_text == "c-submit-button") {
$element = $parent.find(`#${element._data_text}`);
} else {
$element = $parent.find(`label[for=${element._data_text}]`);
}
$element.html(`<span>${element[lang]}</span>`);
}
} else {
let $parent = $(`#${section.id}`);
for (let element of section.elements) {
let $element = $parent.find(`[data-text=${element._data_text}]`);
// text needs to be highlighted
if(element.mark !== null){
let string = element[lang];
let find = element.mark[lang];
let new_string = string.replace(find, `<mark>${find}</mark>`)
// I assume it is wrapped in quotes
$element.html(`<span>“${new_string}”</span>`);
}else{
$element.html(`<span>${element[lang]}</span>`);
}
}
}
}
}
Here is a sample of my data
{
"sections": [
{
"id": "navbar_main",
"elements": [
{
"_data_text": "question",
"eng": "Have a Question?",
"chi": "有个问题?",
"mark": null
}
]
}
]
}
You can view the html and markup at http://professional.ongeo.msu.edu/
I know you are starting with node, but i guess you should use "reduce" instead of those "for loops" for this case (it's just my personal opinion xd). You can find many guides of how to use this function, you variable "d" is an array and all arrays has the "reduce" function.
Something like this:
const modules = d.reduce((all, current) => {
all[current] = current || {}
all[current] = JSON.parse(fs.readFileSync(__dirname + '/modules/'+ current +'/config.json', 'utf8'))
return all;
}, {})
I test it creating some folders and gave me this:
{ test: { name: 'test', icon: 'test-icon' }, tes_2: { name: 'test-2', icon: 'test-icon-2' } }
I'm trying to wrap my brain around promises and I'm refactoring some nested callbacks into promises. I'm encountering something I don't understand and any help anyone could provide would be appreciated. There's definitely something wrong with my for-loop; I suppose that sort of structure is invalid but I'm not sure what to do about it. Also, the result coming back from insertTickersDb function appears to take my entire array and insert it as a single record - not what I was hoping for. I know I'm doing things wrong - please help me understand which things.
I have bound a function to mongoose like this:
var insertTickersDb = q.nfbind(db.tickers.insert.bind(db.tickers));
I have an insert function that looks like this:
function insertTickers(symbols) {
console.log("inserting tickers");
if (symbols) {
if (!(symbols instanceof Array)) {
symbols = [symbols];
}
var tickersToInsert = [];
symbols.map(function(symbol) {
tickersToInsert.push({
symbol: symbol
});
});
console.log("tickersToInsert = " + JSON.stringify(tickersToInsert));
return insertTickersDb(tickersToInsert);
}
}
and I have a test like this:
describe("tickerIntegrationTests internal testing tools", function() {
it("insertTickers should insert the provided tickers", function(done) {
var tickerList = ["A", "B", "C"];
insertTickers(tickerList).then(function(data) {
console.log("inside insertTickers, data = " + JSON.stringify(data));
for (var d = 0; d < data.length; d++) {
console.log("d = " + d + ",data[d].symbol = " + data[d].symbol);
assert.equal(data[d].symbol, tickerList[d]);
}
}).then(removeTickers(tickerList)).done(function(){done();});
});
});
My output is:
tickerIntegrationTests internal testing tools
inserting tickers
tickersToInsert = [{"symbol":"A"},{"symbol":"B"},{"symbol":"C"}]
inside insertTickers, data = [[{"symbol":"A","_id":"552faf5c0aac9578428320da"},{"symbol":"B","_id":"552faf5c0aac9578428320db"},{"symbol":"C","_id":"552faf5c0aac9578428320dc"}],{"n":0}]
d = 0,data[d].symbol = undefined
It might help if you expand out the json of the inner call
[
[
{
"symbol": "A",
"_id": "552faf5c0aac9578428320da"
},
{
"symbol": "B",
"_id": "552faf5c0aac9578428320db"
},
{
"symbol": "C",
"_id": "552faf5c0aac9578428320dc"
}
],
{
"n": 0
}
]
Now you can see the the data is an array whose first element is the data array you are expecting. If you looked at data[0] you would find the first ticker. d[0].symbol should contain what you are looking for. You might want to look at t