How to fix csv to json converter module? - javascript

I can't figure out how to match the title and genre correctly based on what I have in my module.
The csv_json module has an exception where it doesn't match each of the properties accordingly and that is when the title has "The" in it.
//csv file
movieId,title,genre
1,"American President, The (1995)",Comedy|Drama|Romance
2,"Creation, The creator(xxxx)",Comedy|Drama|Romance
3,"Destruction, The destroyer(xxxxx)",Comedy|Drama|Romance
//csv_json module
const readline = require('readline');
const fs = require('fs');
function readCsv(pathToFile) {
return new Promise((resolve, reject) => {
const csvReader = readline.createInterface({
input: fs.createReadStream(pathToFile)
});
let headers;
const rows = [];
let tempRows = [];
csvReader
.on('line', row => {
if (!headers) {
headers = row.split(','); // header name breed age
} else {
rows.push(row.split(','));
}
})
.on('close', () => {
// then iterate through all of the "rows", matching them to the "headers"
for (var i = 0; i < rows.length; i++) {
var obj = {};
var currentline = rows[i];
for (var j = 0; j < headers.length; j++) {
obj[headers[j]] = currentline[j]; //Kitty Siamese 14
}
tempRows.push(obj);
}
resolve(JSON.stringify(tempRows));
});
// This would be in place of the "return" statement you had before
});
}
module.exports = readCsv;
//js file
const readCsv = require('./csvjson.js');
readCsv('movieTest.csv').then((data) => {
console.log(data)
let movieJson = JSON.parse(data);
console.log(movieJson)
/*data output:
[{"movieId":"1","title":"\"American President","genre":" The (1995)\""},{"movieId":"2","title":"\"Creation","genre":" The creator(xxxx)\""},{"movieId":"3","title":"\"Destruction","genre":" The destroyer(xxxxx)\""}]
*/
/*movieJson output:
[ { movieId: '1',
title: '"American President',
genre: ' The (1995)"' },
{ movieId: '2',
title: '"Creation',
genre: ' The creator(xxxx)"' },
{ movieId: '3',
title: '"Destruction',
genre: ' The destroyer(xxxxx)"' } ]
*/
});
I expect the output to match:
[ { movieId: '1',
title: "American President, The (1995)",
genre:'Comedy|Drama|Romance' },
{ movieId: '2',
title: "The creator(xxxx) Creation",
genre: ' Comedy|Drama|Romance' },
{ movieId: '3',
title: "Destruction The destroyer(xxx)",
genre: ' Comedy|Drama|Romance' } ]

This is probably since you're splitting each row on every occurrence of a comma.
const row = '1,"American President, The (1995)",Comedy|Drama|Romance'
row.split(',')
// returns ["1", ""American President", " The (1995)"", "Comedy|Drama|Romance"]
Try replacing every comma that is not followed by a whitespace with some unique string that wouldn't occur anywhere else in the CSV file, and then split on that:
row.replace(/\,(\S)/g, '&unique;$1').split('&unique;')
// returns ["1", ""American President, The (1995)"", "Comedy|Drama|Romance"]
Hope this helps! :)

Related

Filter function with 2 array sets

I am trying to achieve the same result as i wrote in below syntax by implementing filter function to my script.
The current script i have
let sheet = [
{ $0: { 'Name': 'Report1' } },
{ $0: { 'Name': 'Row Count' } },
{ $0: { 'Name': 'Report2' } },
{ $0: { 'Name': 'User' } }
]
let nope = ['User','Row Count','Container']
let result = []
for(let i = 0; i < sheet.length ;i++){
if(sheet[i].$0.Name != nope[0] && sheet[i].$0.Name != nope[1] && sheet[i].$0.Name != nope[2]){
result.push(sheet[i])
}
}
console.log(result)
On my browser inspect element, it will result of (2) [{…}, {…}] on console.log
I tried using filter function
let result_2 = sheet.filter(w => !w.$0.Name.includes(nope[0]))
console.log(result_2)
1 : One problem and logic i face is that im unsure on how can i includes all the element of 'nope' in 'includes()'
2 : I will have to hard code the index such as nope[0] which i dont think is advisable if its going to be a big array
You actually almost finish but you reverse the w.$0.Name and nope.
let sheet = [
{ $0: { Name: "Report1" } },
{ $0: { Name: "Row Count" } },
{ $0: { Name: "Report2" } },
{ $0: { Name: "User" } },
];
let nope = ["User", "Row Count", "Container"];
let result_2 = sheet.filter(w => !nope.includes(w.$0.Name));
console.log(result_2);
PS: I think you should take a break and drink some tea. :)
let result_2 = sheet.filter(w => !nope.includes(w.$0.Name))
A simple and alternativr solution would be to use every() method within the filter. Like this:
It will check one element for every case of the second array and return true if nothing similar would be found.
let result_2 = sheet.filter(w => nope.every(x=> x !== w.$0.Name))
console.log(result_2)
let sheet = [{
$0: {
'Name': 'Report1'
}
},
{
$0: {
'Name': 'Row Count'
}
},
{
$0: {
'Name': 'Report2'
}
},
{
$0: {
'Name': 'User'
}
}
]
let nope = ['User', 'Row Count', 'Container']
let result = []
let result_2 = sheet.filter(w => nope.every(x => x !== w.$0.Name))
console.log(result_2)
Or if you want to use includes() you can do this:
let result_3 = sheet.filter(w => !nope.includes(w.$0.Name))
console.log(result_2)
let sheet = [{
$0: {
'Name': 'Report1'
}
},
{
$0: {
'Name': 'Row Count'
}
},
{
$0: {
'Name': 'Report2'
}
},
{
$0: {
'Name': 'User'
}
}
]
let nope = ['User', 'Row Count', 'Container']
let result = []
let result_3 = sheet.filter(w => !nope.includes(w.$0.Name))
console.log(result_2)
Fiddle example

calculate list of the sums of each column from csv

I'm trying to calculate sums of each columns of csv. I'm able to read a csv in js using readfile method. I also was able to loop through it and parsed data into array of objects. Now I just to figure out a way to add up all the column elements, that's where I'm struggling. My csv object is in array of object format which looks like this.
[
{ item: '18', count: '180' },
{ item: '19', count: '163' },
{ item: '20', count: '175' },
{ item: '', count: undefined }
]
CSV input is like this:
item,count
18,180
19,163
20,175
I want to add 18 + 19 + 20 and final answer should look like this [57,518].
Here's I've done so far, I just need help to make this better and column wise adding logic in JS, please help.
const fs = require('fs')
let result = []
var dataArray = []
fs.readFile(filename, 'utf8', function (err, data) {
dataArray = data.split(/\r?\n/);
// console.log("dataArray", dataArray)
var headers = dataArray[0].split(",");
for (var i = 1; i < dataArray.length; i++) {
var obj = {};
console.log("dataArray", dataArray)
var currentline = dataArray[i].split(",");
for (var j = 0; j < headers.length; j++) {
obj[headers[j]] = currentline[j];
}
result.push(obj);
}
})
You can iterate through each row of your csv and sum up values of items and count using array#reduce and array#forEach.
const fs = require('fs').promises;
const fileName = 'data.csv'
const calculateSum = async () => {
const data = await fs.readFile(fileName, 'utf-8');
const dataArray = data.split(/\r?\n/);
const header = dataArray[0].split(',')
const result = dataArray.slice(1).reduce((sum, arr) => {
arr.split(',').forEach((v, i) => {
sum[i] = (sum[i] || 0) + Number(v.trim());
})
return sum;
}, []);
console.log(result);
}
Generic function
let dataArray = [
{ item: '18', count: '180' },
{ item: '19', count: '163' },
{ item: '20', count: '175' },
{ item: '', count: undefined }
]
const sums = dataArray.reduce((sum, tableRow) => {
Object.keys(tableRow).forEach((obj) => {
if (Number(tableRow[obj])) sum[obj] = (sum[obj] || 0) + Number(tableRow[obj]);
})
return sum;
}, []);
console.log(sums) // [ item: 57, count: 518 ]

Array of objects how do i check for deeply nested text string duplicates & remove from array?

I have an array of objects
Deep inside those objects is a text string
I want to check if other objects in the same array have the same text string / are duplicates.
Then i need a new array with those duplicates removed.
I thought this would be quite simple but it's been testing my intellect for two days now.
const arr = [
{..obj 1}
{..obj 2}
{..obj 3}
{
id: 4,
uid: 24872-2847-249249892842,
tags: ['some', 'stuff'],
type: "blogpage",
href: "https://link-to-stuff",
first_publication_date: "2020-02-12T16:05:04+0000",
last_publication_date: "2020-02-18T21:52:06+0000",
data: {
...some stuff
heading: [
{ type: "heading1", text: "Here Is My Text I Need To Check Duplicates
Of"}
]
}
}
{..obj 5}
{..obj 6}
{..obj 7}
{..obj 8}
{..obj 9}
{..obj 10}
]
I figured something like:
filterOutDuplicates = (blogIndexContent) => {
let arr = blogIndexContent.pages;
let results = [];
arr.map(each => {
if (!results || !results.length) {
results.push(each);
} else {
for (let i = 0; i < results.length; i++) {
const headline = results[i].data.heading[0].text;
if (headline === each.data.heading[0].text) {
return;
} else {
return results.push(each);
}
}
}
})
console.log('Results :', results); // <-- this just gives me the same 9 blog stories again, no duplicates removed.
}
What am i doing wrong guys?
If you dont mind using lodash, it could be easily solved using _.uniqBy
const withoutDups = _.uniqBy(arr, 'data.heading[0].text')
Try this
const arr = [
{
id: 4,
data: {
heading: [
{
type: "heading1",
text: "Here Is My Text I Need To Check Duplicates Of"
}
]
}
},
{
id: 5,
data: {
heading: [
{
type: "heading1",
text: "Here Is My Text I Need To Check Duplicates Of"
}
]
}
},
{
id: 6,
data: {
heading: [
{
type: "heading1",
text: "Not Duplicates"
}
]
}
}
];
const withoutDuplicates = arr.reduce(
(prev, curr) =>
prev
.map(d => d["data"]["heading"][0]["text"])
.includes(curr["data"]["heading"][0]["text"])
? [curr]
: [...prev, curr],
[]
);
console.log(withoutDuplicates);
Slight changes to your code
1) remove using map, have loop over array.
2) Build the uniq object with keys. (Here headline is what we want)
3) Add to results array only when key is not in uniq
let arr = blogIndexContent.pages;
let results = [];
const uniq = {};
for (let i = 0; i < arr.length; i++) {
const headline = arr[i].data.heading[0].text;
if (!(headline in uniq)) {
results.push(each);
uniq[each] = 1;
}
}
console.log("Results :", results);
This should work for you:
filterOutDuplicates = blogIndexContent => {
let arr = blogIndexContent.pages
const result = []
arr.forEach(each => {
if (result.length === 0) {
result.push(each)
}
else {
const headline = each.data.heading[0].text
let found = false
for (let i = 0; i < result.length; i++) {
if (result[i].data.heading[0].text === headline) {
found = true
break
}
}
if (!found) {
result.push(each)
}
}
})
console.log('Results :', results)
}

javascript array of string to deep merged object

I am trying to convert an array of strings (with many more items):
fullRoutes = ['POST /api/v1/user/login','POST /api/v1/user/logout']
Into a deep nested object like this (to use in the following module react-checkbox-tree):
const nodes = [{
value: 'api',
label: 'api',
children: [
{ value: 'v1',
label: 'v1',
children: [
{ value: 'user',
label: 'user',
children: [
{ value: login, label: login},
{ value: logout, label: logout}
]
}
]
}
]
I managed to get to:
fullRoutes.forEach(function(route){
let path = route.split(" ")[1].split("/").filter(function(e){ return e === 0 || e })
let object = {}
path.reduce(function(o, s) {
return o['children'] = {label: s, value: s, children: []}
}, object)
routes.push(object)
})
Which returns the object with the 'children', but I am struggling to merge them correctly
I believe this will work:
fullRoutes = [
'POST /api/v1/user/login',
'POST /api/v1/user/logout',
'POST /api/v2/user/login'
];
routes = [];
fullRoutes.forEach(route => {
let path = route.split(' ')[1].split('/').filter(e => e);
let rs = routes;
for (let i = 0, n = path.length; i < n; i++) {
let seg = path[i];
let segp = path.slice(0, i + 1).join('/');
let node = rs.find(r => r.label == seg);
if (!node)
rs.push(node = {
label: seg,
value: segp,
children: []
});
rs = node.children;
}
});
console.log(routes);
One way is to reduce everything to an object including the children and use the path name as key within the children
Then recursively loop through all children and use Object#values() to convert them from objects to arrays
const fullRoutes = ['POST /api/v1/user/login', 'POST /api/v1/user/logout'];
const tmp = fullRoutes.reduce(function(tmp, route){
let path = route.split(" ")[1].split("/");
path.reduce(function(o, s, i) {
o[s] = o[s] || {label: s, value: s, children: {}};
return o[s].children;
}, tmp);
return tmp;
},{});
const nodes = Object.values(tmp);
nodes.forEach(childrenToArray);
console.log(nodes)
//recursive helper
function childrenToArray(obj) {
obj.children = Object.values(obj.children);
obj.children.forEach(childrenToArray)
}
.as-console-wrapper {max-height: 100%!important;}

Merging 2 arrays in JavaScript

I have 2 arrays and I would like to combine the two arrays which will result in the third array, like in the second image. How can I do that?
const mapper = {
characters: {
'onepiece': {
'luffy': 'pirate king',
'sanji': 'cook'
},
'deathnote': {
'ryuk': 'shinigami',
'lee': 'weirdo :)'
}
},
animes: {
'onepiece': 'one piece',
'deathnote': 'death note'
}
}
I wanted to have a result like this:
'Luffy : Pirate King in One Piece',
'Sanji : Cook in One Piece',
'Ryuk : Shinigami in Death Note',
'Lee : Weirdo :) in Death Note'
Javascript implementation
const mapper = {
characters: {
'onepiece': {
'luffy': 'pirate king',
'sanji': 'cook',
},
'deathnote': {
'ryuk': 'shinigami',
'lee': 'weirdo :)',
},
},
animes: {
'onepiece': 'one piece',
'deathnote': 'death note',
},
};
const result = Object.assign({}, mapper.characters.onepiece, mapper.characters.deathnote);
console.log(result);
You can do nested a classic for / in loop for this.
const mapper = {
characters: {
'onepiece': {
'luffy': 'pirate king',
'sanji': 'cook'
},
'deathnote': {
'ryuk': 'shinigami',
'lee': 'weirdo :)'
}
},
animes: {
'onepiece': 'one piece',
'deathnote': 'death note'
}
};
let result = [];
for (var key in mapper.characters) {
for (var key2 in mapper.characters[key]) {
result.push(jsUcfirst(key2) + " : " + jsUcfirst(mapper.characters[key][key2]) + " in " + jsUcfirst(mapper.animes[key]));
}
}
//Capitalize first letter of each word on a string
function jsUcfirst(string) {
let arr = [];
string = string.split(" ");
for (var key in string) arr.push(string[key].charAt(0).toUpperCase() + string[key].slice(1));
return arr.join(" ");
}
console.log(result);

Categories