I'm not good at javascript I've tried to search on stackoverflow.com but unable to find any help I'm trying to create CSV where I want to add content below relevant column heading/index, where the column heading/index is array key and column content is value, below is code but it is showing data only in two columns:
function downloadCSV(csvStr) {
CSV = ConvertToCSV(csvStr);
var uri = [
[
'application.csv','data:text/csv;charset=utf-8,' + escape(CSV)
]];
downloadAll(uri)
}
function ConvertToCSV(objArray) {
var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
let str = "";
for (var i = 0; i < array.length; i++) {
var line = '';
for (var index in array[i]) {
if (line != '') line += ''
line += `${index} ,`
line += `${array[i][index]}`
}
str += line + '\n';
}
return str;
}
And below is the array data I have where I want to use First, Street Address as the column heading
0: {First: 'asdkjf,\n', Street Address: 'lasdkfj ,\n', City:
'alsdf,\n', State: 'Alaska,\n', ZIP / Postal Code: 'asl;dfj,\n', …}
1: {First: 'asdkjf,\n', Street Address: 'lasdkfj ,\n', City: 'alsdf,\n', State: 'Alaska,\n', ZIP / Postal Code: 'asl;dfj,\n', …}
This is how I'm getting the result
Consider the following example. This assumes you are receiving JSON Data format.
var myDataArray = [{
"First": "asdkjf",
"Street Address": "lasdkfj",
"City": "alsdf",
"State": "Alaska",
"ZIP / Postal Code": "asl;dfj"
}, {
"First": "asdkjf",
"Street Address": "lasdkfj",
"City": "alsdf",
"State": "Alaska",
"ZIP / Postal Code": "asl;dfj"
}];
function convertToCSV(jsonData) {
var rows = [];
jsonData.forEach(function(obj, i) {
var keys = Object.keys(obj);
keys.forEach(function(key, j) {
rows.push(['"' + key + '"', '"' + obj[key] + '"'].join(","));
});
rows.push('\r\n');
});
return rows.join("\r\n");
}
console.log(convertToCSV(myDataArray));
You can make use of many of the Array tools. This helps create the comma separation. arr.join(",") will result in a String of each element of the array, joined by a ,.
This results in the following:
"First","asdkjf"\r\n
"Street Address","lasdkfj"\r\n
"City","alsdf"\r\n
"State","Alaska"\r\n
"ZIP / Postal Code","asl;dfj"\r\n
\r\n
"First","asdkjf"\r\n
"Street Address","lasdkfj"\r\n
"City","alsdf"\r\n
"State","Alaska"\r\b
"ZIP / Postal Code","asl;dfj"\r\n
\r\n
CSV Format is generally:
Cell1,Cell2,Cell3\r\n
Cell4,Cell5,Cell6\r\n
Where each line is terminated with a Carriage Return (\r) and New Line (\n) character at the End of Line. Some Operating systems do not use the Carriage Return. If this is a Windows format, you will want to use both. More complex CSV content may need to quoted, enclosed within double-quote characters.
See More:
https://en.wikipedia.org/wiki/Comma-separated_values
https://datatracker.ietf.org/doc/html/rfc4180#page-1
I made some changes to the answer given by #Twisty because it was again creating only two columns where column one was heading (array key) and two was content (array value) what I needed was to add all headings (array keys) to top row and content (array value) to relevant box.
I know it's less efficient because I used three loops but it was the only choice to me so far!
Here is the code below:
function ConvertToCSV(objArray) {
var jsonData = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
var rows = [];
let inc = 1;
jsonData.forEach(function(obj, i) {
var keys = Object.keys(obj);
let keyInc = 1;
if (inc === 1) {
keys.forEach(function(key, j) {
if (keyInc === 1) {
rows.push([',"' + key + '"'].join(","));
} else {
rows.push(['"' + key + '"'].join(","));
}
keyInc++
});
rows.push('\n\n');
}
keys.forEach(function(key, j) {
if (obj[key].includes('"')) {
let doubleQuoteValue = obj[key].replaceAll('"', '');
rows.push(['"' + doubleQuoteValue + '"'].join(","));
console.log('double quote',doubleQuoteValue);
} else {
rows.push(['"' + obj[key] + '"'].join(","));
}
console.log(obj[key]);
keyInc++
});
inc++;
rows.push('\n');
});
return rows;
}
Related
While I am able to properly parse this payload if I specify the keys via bracket notation, how could you parse it dynamically?
{
"name": "Demo User",
"birthday": "January 1st",
"hobbies":
{
"morning": "coding",
"afternoon": "kewl kids stuff",
"nighttime": "random"
},
"date": "June 25th"
}
What I mean by that is hobbies might not be there, or another field such as 'favorite foods' might exist, which could be an array, or an object.
This scenario has caused me a lot of frustration over the last couple months, and I'm trying to see if anyone can explain to me how to successfully parse it, dynamically.
I found a recursive 'walk' approach that is no longer erroring, but it's returning the first thing it comes across for every iteration.
var data = require("./demo.json");
//data = JSON.stringify(data);
function walk(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
console.log(val);
walk(val);
}
}
}
walk(data);
Basic on what I found, data is being imported as JSON, and if I stringify it, it becomes a string (obviously).
Default
{ name: 'Demo User',
birthday: 'January 1st',
hobbies:
{ morning: 'coding',
afternoon: 'kewl kids stuff',
nighttime: 'random' },
date: 'June 25th' }
Stringify
{"name":"Demo User","birthday":"January 1st","hobbies":{"morning":"coding","afternoon":"kewl kids stuff","nighttime":"random"},"date":"June 25th"}
Both are similar, but the only difference on output is it spamming D X times (Being the first value, I'm thinking?) or spamming { X times (Being the first value of the string?
I've tried a much more basic approach of
var data = require("./demo.json");
for (var key in data){
console.log(key + ':' + data[key]);
}
Which works fine, but as expected, hobbies is returning [object Object] since I am not iterating through it. I could go through hobbies, but again - I don't know if it will exist.
Welcome any input - Generic question, but a process that has caused me a lot of frustration on different projects over last few months.
UPDATE
My vagueness is causing, rightfully-so, confusion.
Let's say my objective is to turn this JSON Payload into a CSV. I need every key for the headers, and every value to be a row under said header.
My issue is, as I iterate through it, I end up with the highest-level objects correctly converted. Then I end up with an object Object column with no data.
For this exact example, let's say my goal is to convert the JSON into
name, birthday, hobbies/morning, hobbies/afternoon, hobbies/nighttime, data
Demo User, January 1st, coding, kewl kids stuff, random, June 25th
Update # 2
Additional array variant.
I would expect
{
...
"hobbies":
{
"morning": "coding",
"afternoon": "kewl kids stuff",
"nighttime": "random"
},
...
}
To output
hobbies/morning, hobbies/afternoon, hobbies/nighttimes
I would expect
{
...
"hobbies": ["coding", "kewl kids stuff", "coding"]
...
}
To output one column
hobbies with quote-enclosed items
"coding, kewl kids stuff, coding"
You can check the type of each value and decide what you want to do,
var data = require("./demo.json");
walk(obj){
for (var key in data){
if(type(data[key]) === "string"){
console.log(key + ':' + data[key]);
}
else if(Array.isArray(data[key])){
//its an array
}
else if(type(data[key]) === "object"){
//its an object
walk(data[key])
}
}
}
The reason your walk function is spamming you with D or { is because it goes on infinite loop when it encountners an string,
function walk(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
console.log(val);
//here you need to check if it is an array or object, only then you should call walk
//calling walk on string would send it on a infinite loop
if(typeof(val) === "object"){
walk(val);
}
}
}
}
Try using this function ( Snippet at the end of the answer )
/**
*
* #param {object} input
* #returns {Array<string>}
*/
function translateObject(input) {
if (typeof input === "object" && input !== null) {
if (input instanceof Array) {
var result = '"';
for (var index in input) {
if (index) result += ", ";
result += input[index];
}
return [result + '"'];
} else {
var data = "", result = "";
for (var key in input) {
if (key.includes(",")) {
throw new Error("Key cannot have a comma");
}
var val = translateObject(input[key]);
if (val.length === 2) {
var titles = val[0].split(", ");
var textIndex = 0;
var size = 0;
for (var index in titles) {
var title = titles[index];
var titleVal = val[1].substring(textIndex, textIndex + title.length);
if (result) { result += ", "; data += ", "; }
textIndex += title.length + 2;
title = key + "/" + title;
size = Math.max(title.length, titleVal.length);
result += title + " ".repeat(size - title.length);
data += titleVal + " ".repeat(size - titleVal.length);
}
} else if (val.length === 1) {
size = Math.max(val[0].length, key.length);
if (result) { result += ", "; data += ", "; }
result += key + " ".repeat(size - key.length);
data += val[0] + " ".repeat(size - val[0].length);
}
}
return [result, data];
}
}
return [input];
}
Here is a working example:
var object = {
"a": "1",
"b": "2",
"c":
{
"e": "3",
"f": "4",
"g": "5"
},
"d": "6"
};
function translateObject(input) {
if (typeof input === "object" && input !== null) {
if (input instanceof Array) {
var result = '"';
for (var index in input) {
if (index) result += ", ";
result += input[index];
}
return [result + '"'];
} else {
var data = "", result = "";
for (var key in input) {
if (key.includes(",")) {
throw new Error("Key cannot have a comma");
}
var val = translateObject(input[key]);
if (val.length === 2) {
var titles = val[0].split(", ");
var textIndex = 0;
var size = 0;
for (var index in titles) {
var title = titles[index];
var titleVal = val[1].substring(textIndex, textIndex + title.length);
if (result) { result += ", "; data += ", "; }
textIndex += title.length + 2;
title = key + "/" + title;
size = Math.max(title.length, titleVal.length);
result += title + " ".repeat(size - title.length);
data += titleVal + " ".repeat(size - titleVal.length);
}
} else if (val.length === 1) {
size = Math.max(val[0].length, key.length);
if (result) { result += ", "; data += ", "; }
result += key + " ".repeat(size - key.length);
data += val[0] + " ".repeat(size - val[0].length);
}
}
return [result, data];
}
}
return [input];
}
function objectToCsv(object) {
var result = translateObject(object);
return result[0] + "\n" + result[1];
}
var csv = objectToCsv(object);
document.querySelector("#console").innerText = csv;
console.log(csv);
#console {
font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
white-space: pre;
}
span {
color: darkgrey;
}
<div id="console"></div>
<span>Names were minified to fit result in one line so that it is easier to read</span>
<span>Use this font family if you want all characters to have the same width</span>
Maybe what you are describing is a case where one or more attributes of the object you are expecting is not present, or has no content (or members, if it is an array), and how to build your code on that basis.
It can't be entirely random, otherwise you wouldn't be talking about csv style output. So I will assume that your object contents are mostly there but occasionally you'll find something missing.
If it were me I would pre-process the objects loaded when you use JSON.parse() or whatever equivalent you use to convert the string to javascript objects. I would use something like jquery's $.extend to merge a perfectly formed object into my data, and array merging where the target is an array attribute. This would give me a consistent data model to code against.
In summary - you have to make the data the way you want it to be in order to be able to work with it without surprises.
how would you go about outputting the found output, including the rest of the string its apart of? The array is just full of strings. Thanks
var searchingfor = document.getElementById('searchfield').value;
var searchingforinlowerCase = searchingfor.toLowerCase();
var searchDiv = document.getElementById('searchDiv');
var convertarraytoString = appointmentArr.toString();
var arraytolowerCase = convertarraytoString.toLowerCase();
var splitarrayString = arraytolowerCase.split(',')
if(search(searchingforinlowerCase, splitarrayString) == true) {
alert( searchingforinlowerCase + ' was found at index' + searchLocation(searchingforinlowerCase,splitarrayString) + ' Amount of times found = ' +searchCount(searchingforinlowerCase,splitarrayString));
function search(target, arrayToSearchIn) {
var i;
for (i=0; i<arrayToSearchIn.length; i++)
{ if (arrayToSearchIn[i] == target && target !=="")
return true;
}
Try this
if(search(searchingforinlowerCase, appointmentArr) == true) {
alert( searchingforinlowerCase + ' was found at index' + searchLocation(searchingforinlowerCase,splitarrayString) + ' Amount of times found = ' +searchCount(searchingforinlowerCase,splitarrayString));
function search(target, arrayToSearchIn) {
var i;
for (i=0; i<arrayToSearchIn.length; i++)
{ if (arrayToSearchIn[i].indexOf(target >= 0))
return true;
}
return false;
}
This code will help you find that a match is present. You can update code to display full text where match was found. Original posted code was comparing entire string rather than partial match.
You can do like this
var test = 'Hello World';
if (test.indexOf('Wor') >= 0)
{
/* found substring Wor */
}
In your posted code you are converting Array to string and then again converting it back to Array using split(). That is unnecessary. search can be invoked as
search(searchingforinlowerCase, appointmentArr);
Try utilizing Array.prototype.filter() , String.prototype.indexOf()
// input string
var str = "america";
// array of strings
var arr = ["First Name: John, Location:'america'", "First Name: Jane, Location:'antarctica'"];
// filter array of strings
var res = arr.filter(function(value) {
// if `str` found in `value` , return string from `arr`
return value.toLowerCase().indexOf(str.toLowerCase()) !== -1
});
// do stuff with returned single , or strings from `arr`
console.log(res, res[0])
The following will look for a word in an array of strings and return all the strings that match the word. Is this something you are looking for?
var a = ["one word", "two sentence", "three paragraph", "four page", "five chapter", "six section", "seven book", "one, two word", "two,two sentence", "three, two paragraph", "four, two page", "five, two chapter",];
function search(needle, haystack){
var results = [];
haystack.forEach(function(str){
if(str.indexOf(needle) > -1){
results.push(str);
}
});
return results.length ? results : '';
};
var b = search("word", a);
console.log(b);
Here's the fiddle to try.
I have a JSON string of the form:
[
{"ID":153,"CircuitID":53,"StartTime":"2014-11-12 12:45:00","EventFormatID":224,"TotalPlaces":8,"BookedPlaces":0,"ProvisionalPlaces":0},
{"ID":161,"CircuitID":53,"StartTime":"2014-11-12 17:15:00","EventFormatID":224,"TotalPlaces":0,"BookedPlaces":0,"ProvisionalPlaces":0},
{"ID":734,"CircuitID":53,"StartTime":"2014-11-12 18:30:00","EventFormatID":231,"TotalPlaces":14,"BookedPlaces":0,"ProvisionalPlaces":0}
]
In place of Event Format ID and Circuit ID I will be returning the names
What I need to do is group the results by Event Format ID and return the results in the following format:
Event 224 : 12:45 (8 places available), 17:15 (0 places available)
Event 231 : 18:30 (14 places available)
I can't seem to figure out how to loop through the data, group it by Event Format ID to present it in the required format!
Thanks
Can you use any additional libraries? I'd use lo-dash which would make this relatively simple:
var grouped = _.groupBy(data, "EventFormatID");
_(grouped).forEach(function (group, key) {
console.log("Event:" + key);
_(group).forEach(function (course) {
console.log(course.StartTime + " (" + course.TotalPlaces + " places available)");
});
});
Obviously the example above logs to the console but it'd be fairly simple to change to build up whatever string or object you need.
This is easier with lodash/underscore, or even with ES5 array and object methods, but since you asked about pure JS:
var data = {}, results = [], i, j, id, time, obj, evts; // obj is your object above
for (i=0; i<obj.length; i++) {
id = obj[i].EventFormatID;
time = obj[i].StartTime; // you can simplify to get just the time, not the datetime, if you prefer
data[id] = data[id] || [];
data[id].push({"time":time,"places":obj[i].TotalPlaces});
}
// now you have a proper data structure, just print it out
for (i in data) {
if (data.hasOwnProperty(i)) {
// just show the output properly formatted
evts = [];
for (j=0;i<data[i].length;j++) {
evts.push(data[i][j].time+" ("+data[i][j].places+" places available)");
}
results.push("Event "+i+" : "+evts.join(","));
}
}
ES5 makes this so much easier
var data = {}, results = [], obj; // obj is your object above
obj.forEach(function(val,i) {
data[val.EventFormatID] = data[val.EventFormatID] || [];
data[val.EventFormatID].push({"time":val.StartTime,"places":val.TotalPlaces});
});
// now you have a proper data structure, just print it out
Object.keys(data).forEach(function(key) {
var value = data[key], evts = [];
value.forEach(function(elm) {
evts.push(elm.time+" ("+elm.places+" places available)");
});
results.push("Event "+key+" : "+evts.join(","));
});
And lodash is even easier.
Please take a look that:
http://jsfiddle.net/m260n5ud/
html
<div id="contentDiv"></div>
js
function tidyUp(jsonArray) {
var myObject = {};
for (i = 0; i < jsonArray.length; i++) {
var key = jsonArray[i]['EventFormatID'];
var time = jsonArray[i]['StartTime'].replace(' ', ':').split(/[- :]/);
time = time[3] + ":" + time[4];
var totalPlace = jsonArray[i]['TotalPlaces'];
if (myObject[key] == null) {
myObject[key] = "Event : " + key + " : " + time + " ( " + totalPlace + " places available)";
} else {
myObject[key] += ", " + time + " ( " + totalPlace + " places available)";
}
}
console.log(myObject);
for (var k in myObject) {
document.getElementById('contentDiv').innerHTML += myObject[k] + "<br/>";
}
}
I have two arrays, one with name of the country and one with the currency type. I would like to merge these together and use the country key instead of the currencies array. What would be the best way to accomplish this?
This is what my code looks like now:
var country = new Array();
country["SEK"] = 'Sweden';
country["USD"] = 'United states';
var currencies = ["SEK","USD"];
var options = '';
for (var i = 0; i < currencies.length; i++) {
options += '<option value="' + currencies[i] + '" id="' + currencies[i] + '">' + currencies[i] + ' (' + country[currencies[i]] + ')</option>';
}
It's a common misconception that you can just use this:
for (var currency in country) {
result += { ... something done with both currency and country[currency] ... };
}
The catch here is that hash is technically unordered in JS, so you cannot guarantee the same order of these options.
The common alternative is using array of objects instead:
var countriesData = [
{
country: 'Sweden',
currency: 'SEK'
},
{
country: 'United States',
currency: 'USD'
}
];
for (var i = 0, l = countriesData.length; i < l; i++) {
result += { something of countriesData[i].currency and countriesData[i].country };
}
As a sidenote, consider this...
var country = new Array();
country["SEK"] = 'Sweden';
country["USD"] = 'United states';
console.log(country.length); // wait, what?
... and 0 will be logged, not 2 - as probably expected. Again, there's no such thing as 'PHP-like associative array' in JS: there are objects and arrays (which are technically objects too; typeof country will give you 'object' string).
So this is what happens here: you create an Array object, and it inherits all the properties of Array.prototype (such as length), in particular. Now you extend this Array with two properties - 'SEK' and 'USD'; but it's not the same as pushing these strings into array with push or some similar methods! That's why its length stays the same, introducing chaos and confusion. )
Try this:
var countries = {
'Sweden': 'SEK',
'United Stated': 'USD'
}, options = '', country;
for(country in countries) {
options += '<option value="' + countries[country] + '" id="' + countries[country] + '">' + countries[country] + ' (' + country + ')</option>';
}
It seem uncomfortable to use arrays. Use json object json.org wikipedia instead, this way you can take advantage of the relation key-value. In addition you can also validate it with lint. No jQuery needed. So this is the code:
var currency = {
"SEK": "Sweden",
"USD": "United states",
"GBP": "United Kingdom"
};
(function(obj) {
var myselect = document.getElementById("currency");
for (var key in obj) {
var optElement = new Option( key+ " ( " + obj[key] + " ) ", key );
optElement.id = key; //optElement.setAttribute ( "id", key);
myselect.add ( optElement, null);
}
})(currency);
As for the function - I think it is better to do it with objects, instead of making a string and then adding it to the select. It is an anonymous function, so it is self-contained and won't interfere with any other code. Just use it after the select is created, or place it at the end of the page.
And jsfiddle example
Edit:
Using add() or innerHTML - in Chrome, innerHTML is not working. And it is better this way.
As for the removing of the options - there is remove() method. Use one of those:
var x = document.getElementById("currency");
for ( var i = x.length; i > 0; i-- ) { x.remove ( 0 ); }
or
while ( x.length > 0 ) { x.remove ( x.length - 1 ); }
I have some input text field in a form that have name with this format:
sometext[234][sometext]
Something like <input type="text" name="user[2][city]" />
I need obtain 'user','2' and 'city' with split function.
Thank you
I guess a regular expression fits better here.
var res = document.getElementsByTagName('input')[0].getAttribute('name').match(/^(\w+)?\[(\d+)?\]\[(\w+)?\]$/);
console.log(res[1]); // === "user"
console.log(res[2]); // === "2"
console.log(res[3]); // === "city"
>>> "user[2][city]".split(/[\[\]]+/)
Returns this array:
["user", "2", "city", ""]
Have you used regexes? Try this sample (available in jsFiddle):
var re = /(.+?)\[(\d+)\]\[(.+?)\]/;
var result = re.exec("user[2][city]");
if (result != null)
{
var firstString = result[1]; // will contain "user"
var secondString = result[2]; // will contain "2"
var thirdString = result[3]; // will contain "city"
alert(firstString + "\n" + secondString + "\n" + thirdString);
}