JavaScript splitting a file content into key value pairs - javascript

I have the below UploadDocument event which has fileReader.result which logs below message to the console
Output in the Console
A,B
aa,dsf
adfa,dfsd
fdsafds,sdf
uploadDocument(file) {
let fileReader = new FileReader();
fileReader.onload =(e) => {
console.log(fileReader.result);
}
fileReader.readAsText(this.file);
How can I split the above content (A,B...) in the console into a key value pairs ( Like a hashmap or Arraylist ) using javascript or Typescript?
I have tried splitting it
var lines = fileReader.result.split(/[\r\n]+/g);
console.log(lines);
Now how can I create A hashmap from this?

The reduce() array method can be used to build objects over each element, so we just need to split the file into multiple lines and then reduce the array of lines into an object.
var map = fileReader.result.split('\n')
.reduce((obj, line) => {
var cols = line.split(',');
// Tolerate empty lines. There may be one at the end of the input.
if (cols.length >= 2) {
obj[cols[0]] = cols[1];
}
return obj;
}, {});
Sample fiddle

One way would be to use reduce which takes an initial value - in this case an new object - and populates its keys and values with the result of splitting each line in the file on the comma with each iteration of the function.
const input = 'A,B\n\
aa,dsf\n\
adfa,dfsd\n\
fdsafds,sdf'
const obj = input.split(/\n/g).reduce((p, c) => {
const s = c.split(',');
p[s[0]] = s[1];
return p;
}, {});
console.log(obj)

Related

Nodejs find multiple value

I have two lists:
lista_source: 'B10L-A2,AABan38711$B10L-A2,AABan38811$B12A-A,AABan38912$B14-A2,AABan39314$B16B-A,AABan39616$B12A-A,AABan39818$B16L-B,AABan39919$B16L-B,AABan40019$B12A-A,AABan41112'
second_list: 'B10L-A2,B12A-A,B16L-B'
As a result I would like to get the following list (or similar one):
result = [B10L-A2:AABan38711,AABan38811],[B12A-A:AABan38912,AABan41112,AABan39818],[B16L-B:AABan39919,AABan40019]
In short, I'm looking for multiple values for the 2nd lists items.
I tried the filter function and write it to csv file but does not really work.
const first_list_object= first_list.split('$');
const second_list_object= second_list.split(',');
for (let i = 0; i < second_list_object.length; i++) {
let results= first_list_object.filter(x => x.includes(second_list_object[i]));
console.log(results);
writer = csvWriter({ sendHeaders: false });
writer.pipe(fs.createWriteStream(__dirname + '/lista.csv', { flags: 'a' }));
writer.write({
results
});
}
How should I solve it? Is there any better solution than filter?
A javascript object as output. If you need, I can convert this to .csv too.
const lista_source = 'B10L-A2,AABan38711$B10L-A2,AABan38811$B12A-A,AABan38912$B14-A2,AABan39314$B16B-A,AABan39616$B12A-A,AABan39818$B16L-B,AABan39919$B16L-B,AABan40019$B12A-A,AABan41112'
const second_list = 'B10L-A2,B12A-A,B16L-B'
// convert data to arrays
const source = lista_source.split(",")
const second = second_list.split(",")
// filter out source list items (into seperate object value) for each second list item
const res = second.reduce((obj, sec_key) => {
// get item, if string is not exact key name and string includes key name
const filtered = source.filter(key => key !== sec_key && key.includes(sec_key))
return ({...obj, [sec_key]: filtered })
}, {})
console.log(res)
Assuming that structure of the strings are gonna be like in question, I wrote same basic regex to split on and then add them accordingly to object. See if that's what you want.
Edit:
After rereading your question, I realized that comma actually doesn't separate values in your string but dolar sign instead (kinda weird but ok). I also added an if to take only values present in second list.
const lista_source = 'B10L-A2,AABan38711$B10L-A2,AABan38811$B12A-A,AABan38912$B14-A2,AABan39314$B16B-A,AABan39616$B12A-A,AABan39818$B16L-B,AABan39919$B16L-B,AABan40019$B12A-A,AABan41112'
const second_list = 'B10L-A2,B12A-A,B16L-B'.split(',')
const array = lista_source.match(/B[0-9]{2}[A-Z]-[A-Z][0-9]?,[A-Za-z0-9_]{10}/g)
let result = {}
for (let value of array) {
let [key, s] = value.split(',')
// only take keys form contained in second list
if(!second_list.includes(key)){
continue
}
key in result ? result[key] = [s, ...result[key]] : result[key] = [s]
}
console.log(result)

Find Unique value from an array based on the array's string value (Javascript)

so I want to find unique values from an array.
so for example I have this array:
const mainArr = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884']
so I want to find the first matching value for each unique item.
for example, in the array, I have two strings with the shape prefix, six items with the size prefix, and two items with the height prefix.
so I want to output to be something like
const requiredVal = ["shape-10983", "size-2364", "height-3399"]
I want only the first value from any set of different values.
the simplest solution will be to iterate on the list and storing what you got in a dictionary
function removeSimilars(input) {
let values = {};
for (let value of input) {//iterate on the array
let key = value.splitOnLast('-')[0];//get the prefix
if (!(key in values))//if we haven't encounter the prefix yet
values[key] = value;//store that the first encounter with the prefix is with 'value'
}
return Object.values(values);//return all the values of the map 'values'
}
a shorter version will be this:
function removeSimilars(input) {
let values = {};
for (let value of input)
values[value.splitOnLast('-')[0]] ??= value;
return Object.values(values);
}
You could split the string and get the type and use it aks key for an object along with the original string as value. At result take only the values from the object.
const
data = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884'],
result = Object.values(data.reduce((r, s) => {
const [type] = s.split('-', 1);
r[type] ??= s;
return r;
}, {}));
console.log(result);
If, as you mentioned in the comments, you have the list of prefixes already available, then all you have to do is iterate over those, to find each first element that starts with that prefix in your full list of possible values:
const prefixes = ['shape', 'size', 'height'];
const list = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884']
function reduceTheOptions(list = [], prefixes = [], uniques = []) {
prefixes.forEach(prefix =>
uniques.push(
list.find(e => e.startsWith(prefix))
)
);
return uniques;
}
console.log(reduceTheOptions(list, prefixes));
Try this:
function getRandomSet(arr, ...prefix)
{
// the final values are load into the array result variable
result = [];
const randomItem = (array) => array[Math.floor(Math.random() * array.length)];
prefix.forEach((pre) => {
result.push(randomItem(arr.filter((par) => String(par).startsWith(pre))));
});
return result;
}
const mainArr = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884'];
console.log("Random values: ", getRandomSet(mainArr, "shape", "size", "height"));
I modified the #ofek 's answer a bit. cuz for some reason the ??= is not working in react project.
function removeSimilars(input) {
let values = {};
for (let value of input)
if (!values[value.split("-")[0]]) {
values[value.split("-")[0]] = value;
}
return Object.values(values);
}
create a new array and loop over the first array and check the existing of element before in each iteration if not push it to the new array

How to map array with another array of object and replace an first array elements with another arrays object value

I have project in which I need to develop a specific calculator. By far everything is good but now I am stuck in one problem. I have an array of the object containing letter as key and its value as below
valueList = [{a:5}, {b:3}, {c:8}, {d:6}]
and I have an input element where user can type specific characters like this
input = "a+b-c"
how do I modifie the above string to the new string that contains values of alphabets from valueList like
newVar = "5+3-8"
I have tried below solution with no far success
const final = input.split("").map((variable) => {
return valueList.forEach((element) => {
if (variable === Object.keys(element)[0]) {
return Object.values(element)[0];
} else {
return variable;
}
});
});
console.log(final);
First turn the valueList into an object with multiple properties, rather than an array of objects with single properties. Then use a regular expression to match any of the keys of the objects, and use a replacer function to look up the matching value on the object:
const valueList = [{a:5}, {b:3}, {c:8}, {d:6}];
const obj = Object.assign({}, ...valueList);
const input = "a+b-c";
const pattern = new RegExp(
Object.keys(obj).join('|'),
'g'
);
const output = input.replace(pattern, match => obj[match]);
console.log(output);

How to group multiple sets of duplicate integers in an array into their own array of arrays?

I am trying to split an array of integers into an array of arrays by duplicate values. The original array is composed of a list of 6 digit integers, some of these integers come in pairs, others come in groups of 3 or 4s. I'd like to get these duplicates pushed to their own arrays and have all of these arrays of duplicates composed into an array of arrays that I can later loop through.
I've looked on in the lodash library for some method or combination of but can't quite find anything that seems to work. I've also tried a few different configurations with nested for loops but also am struggling with that.
const directory = "X/";
let files = fs.readdirSync(directory);
let first6Array = [ ];
for(i=0; i< files.length; i++){
let first6 = files[i].substring(0, 6);
first6Array.push(first6);
};
console.log(first6Array);
example output of first6Array:
[ '141848',
'141848',
'141848',
'142851',
'142851',
'143275',
'143275']
I'd like to end up with something like
let MasterArray = [[141848,141848,141848],[142851,142851],[143275,143275]];
You can use new Set() to filter out the duplicates.
Then you use the unique Array and filter for every value.
const firstArray = [ '141848', '141848', '141848', '142851', '142851', '143275', '143275'];
const numberArray = firstArray.map(Number);
const masterArray = [];
const unique = new Set (numberArray); // Set {141848, 142851, 143275}
unique.forEach(u => {
masterArray.push(numberArray.filter(e => e === u));
});
console.log(masterArray);
Using lodash, you can create a function with flow:
map the items by truncating them and converting to numbers.
groupBy the value (the default).
convert to an array of arrays using values.
const { flow, partialRight: pr, map, truncate, groupBy, values } = _;
const truncate6 = s => truncate(s, { length: 6, omission: '' });
const fn = flow(
pr(map, flow(truncate6, Number)),
groupBy,
values,
);
const firstArray = [ '141848abc', '141848efg', '141848hij', '142851klm', '142851opq', '143275rst', '143275uvw'];
const result = fn(firstArray);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Use reduce to create an object of arrays, indexed by number, and push to the associated array on each iteration (creating the array at the key first if needed), then get the values of the object:
const directory = "X/";
const files = fs.readdirSync(directory);
const output = Object.values(
files.reduce((a, file) => {
const num = Number(file.slice(0, 6));
if (!a[num]) a[num] = [];
a[num].push(num);
return a;
}, {})
);
It's pretty weird to have an array of identical values, though - you might consider a different data structure like
{
'141848': 3,
'142851': 2
}
to keep track of the number of occurrences of each number:
const output = files.reduce((a, file) => {
const num = file.slice(0, 6);
a[num] = (a[num] || 0) + 1;
return a;
}, {})
To obtain exactly the result you desire, you need a nested find, something like this should works:
const directory = "X/";
let files = fs.readdirSync(directory);
let first6Array = files.reduce((acc, value)=> {
let n = +value.substr(0, 6); // assumes it can't be NaN
let arr = acc.find(nested => nested.find(item => item === n));
if (arr) {
arr.push(n);
} else {
acc.push([n]);
}
return acc;
}, []);
console.log(first6Array);
Notice that an hashmap instead, with the value and the number of occurrence, would be better, also in term of performance, but I don't think it mind since you have really few elements.
Also, it assumes the first six characters are actually numbers, otherwise the conversion would fail and you'll get NaN.
It would be safer adding a check to skip this scenario:
let n = +value.substr(0, 6);
if (isNaN(n)) {
return acc;
}
// etc

convert CSV lines into Javascript objects

I have a simple csv file
people.csv:
fname, lname, uid, phone, address
John, Doe, 1, 444-555-6666, 34 dead rd
Jane, Doe, 2, 555-444-7777, 24 dead rd
Jimmy, James, 3, 111-222-3333, 60 alive way
What I want to do it get each line of the CSV, convert it to a JavaScript object, store them into an array, and then convert the array into a JSON object.
server.js:
var http = require('http');
var url = require('url');
var fs = require('fs');
var args = process.argv;
var type = args[2] || 'text';
var arr = [];
var bufferString;
function csvHandler(req, res){
fs.readFile('people.csv',function (err,data) {
if (err) {
return console.log(err);
}
//Convert and store csv information into a buffer.
bufferString = data.toString();
//Store information for each individual person in an array index. Split it by every newline in the csv file.
arr = bufferString.split('\n');
console.log(arr);
for (i = 0; i < arr.length; i++) {
JSON.stringify(arr[i]);
}
JSON.parse(arr);
res.send(arr);
});
}
//More code ommitted
My question is if I am actually converting that CSV lines into Javascript objects when I call the .split('\n') method on bufferString or is there another way of doing so?
By doing this:
arr = bufferString.split('\n');
you will have an array containing all rows as string
["fname, lname, uid, phone, address","John, Doe, 1, 444-555-6666, 34 dead rd",...]
You have to break it again by comma using .split(','), then separate the headers and push it into an Javascript Object:
var jsonObj = [];
var headers = arr[0].split(',');
for(var i = 1; i < arr.length; i++) {
var data = arr[i].split(',');
var obj = {};
for(var j = 0; j < data.length; j++) {
obj[headers[j].trim()] = data[j].trim();
}
jsonObj.push(obj);
}
JSON.stringify(jsonObj);
Then you will have an object like this:
[{"fname":"John",
"lname":"Doe",
"uid":"1",
"phone":"444-555-6666",
"address":"34 dead rd"
}, ... }]
See this FIDDLE
Using ES6/ES7 and some functional programming guidelines:
All variables are const (immutability)
Use map/reduce instead of while/for
All functions are Arrow
No dependencies
// Split data into lines and separate headers from actual data
// using Array spread operator
const [headerLine, ...lines] = data.split('\n');
// Split headers line into an array
// `valueSeparator` may come from some kind of argument
// You may want to transform header strings into something more
// usable, like `camelCase` or `lowercase-space-to-dash`
const valueSeparator = '\t';
const headers = headerLine.split(valueSeparator);
// Create objects from parsing lines
// There will be as much objects as lines
const objects = lines
.map( (line, index) =>
line
// Split line with value separators
.split(valueSeparator)
// Reduce values array into an object like: { [header]: value }
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
.reduce(
// Reducer callback
(object, value, index) => ({
...object,
[ headers[index] ]: value,
}),
// Initial value (empty JS object)
{}
)
);
console.log("Objects:", objects);
For CSV files using , as separator and quotes string values, you can use this version:
// Split data into lines and separate headers from actual data
// using Array spread operator
const [headerLine, ...lines] = data.split('\n');
// Use common line separator, which parses each line as the contents of a JSON array
const parseLine = (line) => JSON.parse(`[${line}]`);
// Split headers line into an array
const headers = parseLine(headerLine);
// Create objects from parsing lines
// There will be as much objects as lines
const objects = lines
.map( (line, index) =>
// Split line with JSON
parseLine(line)
// Reduce values array into an object like: { [header]: value }
.reduce(
(object, value, index) => ({
...object,
[ headers[index] ]: value,
}),
{}
)
);
return objects;
Note: For big files, it would be better to work with streams, generators, iterators, etc.
You could try using MVC Razor,
<script type="text/javascript">
MyNamespace.myConfig = #Html.Raw(Json.Encode(new MyConfigObject()));
</script>
The Json.Encode will serialize the initialized object to JSON format. Then the Html.Raw will prevent it from encoding the quotes to ".
Here the entire example
You can use lodash (or underscore) to help with this.
var objects = _.map(arr, function(item){return item.split(',');});
var headers = objects[0];
objects.splice(0, 1); // remove the header line
populatedObject = [];
objects.forEach(function(item){
var obj = _.zipObject(headers, item);
populatedObject.push(obj);
});
The .zipObject method will match each header to each value in the items array and produce an object.
Here is a solution if you already have an array and want that the csv header (first line) to be the object's property.
const csvArrayToObj = (csvData) => {
return csvData
.map((csvLine, csvIndex) => {
if (csvIndex === 0 || !csvLine.length) return null; // skip header and empty lines
return csvLine.reduce((a, v, i) => ({ ...a, [csvData[0][i]]: v }), {});
})
.filter((filter) => !!filter); //filter empty lines
};
const csvArray = [
['name', 'age'],
['John Doe', 20],
['Jane Doe', 30],
];
csvArrayToObj(csvArray);
// output
[
{
"name": "John Doe",
"age": 20
},
{
"name": "Jane Doe",
"age": 30
}
]

Categories