Saving as CSV through google script handling newline characters - javascript

So I'm attempting to take a sheet from a Google Spreadsheet and save it as a .CSV file just like you can manually do. It is working fairly well, but I've ran into an issue with new lines/line feeds/carriage returns etc. I have used and modified Google's solution and some stack overflow resources that have meet my needs so far, however I'm stuck on this new line issue.
The Problem:
The Cause:
I will note that if I manually save the CSV file from Google Spreadsheets it looks fine, but that box you see in the cause picture is the issue I cannot resolve.
My current code:
function convertRangeToCsvFile_(csvFileName, sheet) {
// get available data range in the spreadsheet
var activeRange = sheet.getDataRange();
try {
var data = activeRange.getValues();
var csvFile = undefined;
// loop through the data in the range and build a string with the csv data
if (data.length > 1) {
var csv = "";
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
//Handle special characters
var text = data[row][col].toString();
text = text.replace(/(\r\n|\n|\r)/g," ");
for(var i = 0; i < text.length; i++)
{
if(text[i] == "\'" || text[i] == "\"" || text[i] == "\\" || text[i] == "\n" || text[i] == "\r" || text[i] == "\t" || text[i] == "\b" || text[i] == "\f")
{
text = spliceSlice(text,i,0,"\\");
i++;
}
}
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + text + "\""; //Puts quotes around the data for strings.
}
}
// join each row's columns
// add a carriage return to end of each row, except for the last one
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
}
}
csvFile = csv;
return csvFile;
}
catch(err) {
Logger.log(err);
}
}
As you can see I'm attempting to handle the new lines with what I have read about 'regex expressions' however, they don't seem to actually be doing anything for me. So perhaps I'm just using it wrong and then it will work, or I need to go about this differently.
EDIT: I've looked at the file in Notepad++ and determined that they are LF characters causing the problem in the middle of the rows. Where as my end of row \r\n is working fine.

Instead of .replace(/(\r\n|\n|\r)/g," ") try .replace("\n", " ").replace("\r", " "), I know it's essentially the same, but I'm currently using this solution without hiccups.

This is an old question, but I ran into it at the top of my Google search when just looking for a simple solution to handling newlines on a CSV export from Sheets.
Since it's 2022 now, you can use Sheets' pretty powerful find + replace to target all newline characters (removing them, or replacing them with a literal \n string, etc). You can then export as CSV normally.
In my example here, the "Replace with" doesn't use regex, only the search does, so I'm wanting to preserve the newlines here before export.

Related

Problems with IF condition

I'm trying to make a website that gathers information from APIs. The following code always evaluates to 'Beep Boop Beep! I can\t find the Wikipedia page with the API! :-( \n Anyways here is more info on...'! Anyone have any ideas why?
var geoNamesWiki = result.geoNamesWiki;
for (let j = 0; j < 30; j++) {
if (geoNamesWiki.geonames[j].feature == 'country' &&
(geoNamesWiki.geonames[j].countryCode == openCage.results[0].components["ISO_3166-1_alpha-2"] ||
geoNamesWiki.geonames[j].title.includes(openCage.results[0].components.country))) {
$('#summary').html(geoNamesWiki.geonames[j].summary);
$('#wikiLink').html(geoNamesWiki.geonames[j].wikipediaUrl).attr("href", "https://" + geoNamesWiki.geonames[j].wikipediaUrl);
} else {
$('#summary').html('Beep Boop Beep! I can\t find the wikipedia page with the API! :-( \n Anyways here is more info on' + openCage.results[0].components.country + ':');
$('#wikiLink').html('https://en.wikipedia.org/wiki/' + encodeURI(openCage.results[0].components.country)).attr("href", 'https://en.wikipedia.org/wiki/' + encodeURI(openCage.results[0].components.country));
}
}
Is suspect you have a string there at var geoNamesWiki = result.geoNamesWiki;
Try parsing it to a JSON object first var geoNamesWiki = JSON.parse( result.geoNamesWiki );
I found the answer thanks to #Bekim Bacaj! I was overwriting what I had already done, so just needed to add a break on the final line of the IF part.

Making a custom function in Google Sheets, the code works outside of sheets but not in sheets

So I'm writing a custom function for Google sheets which is supposed to take a single row or cells, produce a 1 dimensional array and use that data to create a URL. This is basically so I can help my friends track the prices of an item in a video game.
I have ran the code in a javascript environment outside of Google Sheets and everything acted as expected. But when I put the code in the script editor, it runs with unexpected behavior. The range of cells will always be 1 row by 13 columns but some of the columns may be void so the script is supposed to truncate the array of values so that there are no empty indexes. Then it is supposed to insert a "." between each value as it is concatenated into a string to produce the final URL. For some reason it does not remove any values and the "." are ","s when the URL is created in the cell.
Here is the code:
function STONKLINK(prices, premrkt) {
var webLink = "https://turnipprophet.io?prices=";
var priceLink = "";
for (i = (prices.length - 1); prices[i] == 0; i--){
prices.pop();
}
for (i = 0; i < prices.length; i++) {
priceLink = (priceLink + prices[i] + ".")
}
priceLink = priceLink.substring(0, priceLink.length - 1);
if (premrkt == "N/A") {
webLink = webLink + priceLink;
} else if (premrkt == "Fluctuating") {
webLink = webLink + priceLink + "&pattern=0";
} else if (premrkt == "Small Spike") {
webLink = webLink + priceLink + "&pattern=3";
} else if (premrkt == "Large Spike") {
webLink = webLink + priceLink + "&pattern=1";
} else if (premrkt == "Decreasing") {
webLink = webLink + priceLink + "&pattern=2";
}
return webLink;
}
Any help is very much appreciated. Thank you so much.
EDIT: While I still do not understand why this is happening, and I would like to understand, I have added a simple work around. By adding this line of code
webLink = webLink.replace(/,/g, ".");
before the return, all the commas will be replaced with dots. Still, any insight into the problem will be much appreciated. Thank you again.
When you store values in Spreadsheets, the values which have a , are considered to be strings whilst the ones which have a . will be considered to be numbers.
So for example, if you have these values stored in the B column:
If you log the typeof(val1) and typeof(val2) (where val1 and val2 are the values stored in B1 respectively B2), this is what you will get:
So the issue you were encountering is due to this fact. The values you have stored in your sheet are numbers and when you try to build the webLink string they are converted into strings - therefore, the . for a number becomes ,.
Reference
Date and Number Formats

Search for string between two characters if next to a specific string

I am in need of some modification to my function to allow for a search of two strings on one line of a value. I am trying to work through this on my own but I need some help. Here is an example of a cell value being looked at. Assume there are no leading or trailing newlines. Also, all the cells have the same format. same number of lines, same structure of membertype: last, first etc.
Say I want to see if this cell contains a team lead with the name of last2 or a Manager with the name first4. Both the type of employee and name would be user inputted.
I tried using the following that I created with the help of this.
indexOf(':(.*):')
It returns the position of the content between and including the colons. Then I tried the following:
flatUniqArr[0].search('Supervisor:')
This is where I'm stuck. It returns the index to the last digit of the first line.
My thought was to do a search of the user inputted name between the colons if they follow the user inputted member type. How can I accomplish this?
Clarifications:
The end goal is to verify that the name and member type are on the same line and excluded from an array I am building for .setHiddenValues(). So if they are on the same line exclude from list.
Here is the function I will be adding it to:
var flatUniqArr = colValueArr.map(function(e){return e[0].toString();})
.filter(function(e,i,a){
return (a.indexOf(e) == i && !(visibleValueArr.some(function(f){
return e.search(new RegExp(f,'i')) + 1;
})));
});
return flatUniqArr;
Where flatUniqArr is the list of hidden values. colValueArr is the array of values from a column. visibleValueArr is the name which is user inputted and memberType will be the member type.
Attempts using Liora's solution: (Updated... Works now)
var flatUniqArr = []
var lines = []
Logger.log(visibleValueArr)
Logger.log(memberType)
for (var i = 0; i < colValueArr.length; i++){
lines = colValueArr[i].toString().split('\n');
var found = false;
for(var j = 0; j < lines.length; j++){
var data = lines[j].toLowerCase().split(':')
if(data[0] == memberType.toString().toLowerCase() && data[1].indexOf(visibleValueArr.toString().toLowerCase()) != -1){
found = true;
}
}
Logger.log(found)
if(found == false){flatUniqArr.push(colValueArr[i])}
}
return flatUniqArr;
It works now. It seems like a lot of code though. I'd be open to alternative solutions if they are faster and/or less lines of code.
Updated: Added .toString().toLowerCase() as the user may input lowercase values.
I assume all the line have this format.
If you split each line with the separator ":"
var array = value.split(":")
Then you'd have
array[0] //the current role
array[1] //the list of name
array[2] //the email
And you can check each names then
if(array[0] == "Team Lead" && array[1].indexOf("last2") != -1)
An example with a linesplit:
var lines = value.toString().split("\n");
var found = false;
for(var i = 0; i < lines.length ; i++){
var data = value.split(":")
if(data[0] == "Team Lead" && data[1].indexOf("last2") != -1){
found = true;
}
}
How about just building the regex using the user input?
function search(line, employeeType, employeeName) {
var regexp = '/' + employeeType + ': ' + employeeName + '/'
return line.search(regexp)
}
Or better yet, if it always occurs at the beginning of the string, just use startsWith()

How can i rearrange words in a div

I have got a div with words seprated by a comma. How can i arrange them so there is four words per line?
edit: the words are dynamically generated
Use the <pre>...</pre> tag. The text will appear on the screen exactly the way you have formatted it.
Using jQuery to simplify access to the DOM:
var content = $('#mydiv').text(); // get current content from the page
var words = content.split(','); // break into words
for (var i = 3; i < words.length; i += 4) { // every fourth word (but not the first)
words[i] += '<br>'; // append <br>
}
content = words.join(' '); // and rejoin with spaces
$('#mydiv').html(content); // and put it back in the page
NB: I've used .html() to write the contents back out otherwise the <br> tags won't be rendered properly.
Use the BR tag after every four words. See http://www.w3.org/wiki/Html/Elements/br
If str contains the words, then:
var lines = str.match(/([^,]+,*){1,4}/g);
var output = lines === null ? '' : lines.join('<br>');
Example
If:
str = "a,b,c,d,e,f,g,h,i";
Then output will be:
"a,b,c,d,<br>e,f,g,h,<br>i"
Demo here.
Using jQuery, everthing is easier. jQuery is a bunch of tools written in JavaScript and it is all saved to a variable called $. You can call jQuery which will call the base JavaScript for you and make life easier.
Add this to the <head> of your web page
<script type="text/javascript" src="http://code.jquery.com/jquery-1.5.1.min.js"></script>
Add id="arrange" to your div. If you don't like the word arrange, you can use any word you like. Make sure there are no spaces and only letters, numbers, underscores.
<div class="arrange">One two three four one two three four</div>
Add this anywhere after the div that has the text
<script type="text/javascript">
$('div.arrange').each(function () { // For each div with the class="arrange"
var words = this.innerHTML.split(/,/) // Get an array of all the words (separated by comma)
var newHTML = '' // We will add the new contents of the div here.
for(var i = 0; i < words.length; i++) { // For every word
if(i != 0) {// If this is not the first word
if(i % 4 == 0) { // If this is the fourth word
newHTML += '<br/>' // Add a <br/>
} else {
newHTML += ',' // Add a comma
}
}
newHTML += words[i] // Add the word
}
this.innerHTML = newHTML
})
</script>
In this case, I am using jQuery to loop through each div with class="arrange"
You can see a live example here: http://jsfiddle.net/gf5wD/
This example automatically includes jQuery and automatically adds the JavaScript to the end.
You will probably eventually want to save the jquery file to servers you control. The script tag in your head will call the jquery servers which means if their site goes down, yours gets messed up.
Greetings user521180 I got you and i think this code will help
function arrangeDiv() {
var div = document.getElementById('div');
var divinnerHTMLAry = div.textContent.split(' ');
div.innerHTML = "";
var divNewinnerHTML = '';
var count = 0;
for (var i = 0; i < divinnerHTMLAry.length; i++) {
if (divinnerHTMLAry[i] != "" && divinnerHTMLAry[i] != "\n") {
divNewinnerHTML += divinnerHTMLAry[i] + " ";
count += 1;
if (count == 4) {
divNewinnerHTML += "<br />";
}
}
}
div.innerHTML = divNewinnerHTML;
}
Regards :)
edit : this is a PHP solution
Maybe something like that should be able to make what you want.
$string = 'a, b, c, d, e, f';
$array = explode(',', $string,);
foreach($array as $key => $word) {
print trim($word);
if ($key % 4 == 0) {
print '<br />';
}
}
If you get your word in an other way, just arrange the code so you can skip the explode() function.

parse a textarea in substrings based on line breaks in Javascript

I have a text area that I need to parse. Each new line needs to be pulled out and an operation needs to be performed on it. After the operation is done the operation needs to be run on the next line. This is what I have at the moment. I know the indexOf search won't work because it's searching character by character.
function convertLines()
{
trueinput = document.getElementById(8).value; //get users input
length = trueinput.length; //getting the length of the user input
newinput=trueinput; //I know this looks silly but I'm using all of this later
userinput=newinput;
multiplelines=false; //this is a check to see if I should use the if statement later
for (var i = 0; i < length; i++) //loop threw each char in user input
{
teste=newinput.charAt(i); //gets the char at position i
if (teste.indexOf("<br />") != -1) //checks if the char is the same
{
//line break is found parse it out and run operation on it
userinput = newinput.substring(0,i+1);
submitinput(userinput);
newinput=newinput.substring(i+1);
multiplelines=true;
}
}
if (multiplelines==false)
submitinput(userinput);
}
So for the most part it is taking the userinput. If it has multiply lines it will run threw each line and seperatly and run submitinput. If you guys can help me I'd be eternally thankful. If you have any questions please ask
Line breaks within the value of a textarea are represented by line break characters (\r\n in most browsers, \n in IE and Opera) rather than an HTML <br> element, so you can get the individual lines by normalizing the line breaks to \n and then calling the split() method on the textarea's value. Here is a utility function that calls a function for every line of a textarea value:
function actOnEachLine(textarea, func) {
var lines = textarea.value.replace(/\r\n/g, "\n").split("\n");
var newLines, i;
// Use the map() method of Array where available
if (typeof lines.map != "undefined") {
newLines = lines.map(func);
} else {
newLines = [];
i = lines.length;
while (i--) {
newLines[i] = func(lines[i]);
}
}
textarea.value = newLines.join("\r\n");
}
var textarea = document.getElementById("your_textarea");
actOnEachLine(textarea, function(line) {
return "[START]" + line + "[END]";
});
If user is using enter key to go to next line in your text-area you can write,
var textAreaString = textarea.value;
textAreaString = textAreaString.replace(/\n\r/g,"<br />");
textAreaString = textAreaString.replace(/\n/g,"<br />");
textarea.value = textAreaString;
to simplify the answers, here is another approach..
var texta = document.getElementById('w3review');
function conv (el_id, dest_id){
var dest = document.getElementById(dest_id),
texta = document.getElementById(el_id),
val = texta.value.replace(/\n\r/g,"<br />").replace(/\n/g,"<br />");
dest.innerHTML = val;
}
<textarea id="targetted_textarea" rows="6" cols="50">
At https://www.a2z-eco-sys.com you will get more than what you need for your website, with less cost:
1) Advanced CMS (built on top of Wagtail-cms).
2) Multi-site management made easy.
3) Collectionized Media and file assets.
4) ...etc, to know more, visit: https://www.a2z-eco-sys.com
</textarea>
<button onclick="conv('targetted_textarea','destination')" id="convert">Convert</button>
<div id="destination">Had not been fetched yet click convert to fetch ..!</div>

Categories

Resources