I have created a Google Form that has a field where a numeric value is entered by the user (with numeric validation on the form) and is then submitted. When the number (e.g., 34.00) gets submitted, it appears as 34 in the Google spreadsheet, which is annoying but understandable. I already have a script that runs when the form is submitted to generate a nicely-formatted version of the information that was submitted on the form, but I'm having trouble formatting that value as a monetary value (i.e., 34 --> $34.00) using the Utilities.formatString function. Can anyone help? Thanks in advance.
The values property of a form submission event as documented in Event Objects is an array of Strings, like this:
['2015/05/04 15:00', 'amin#example.com', 'Bob', '27', 'Bill', '28', 'Susan', '25']
As a result, a script that wishes to use any of these values as anything but a String will need to do explicit type conversion, or coerce the string to number using the unary operator (+).
var numericSalary = +e.values[9];
Alternatively, you could take advantage of the built-in type determination of Sheets, by reading the submitted value from the range property also included in the event. Just as it does when you type in values at the keyboard, Sheets does its best to interpret the form values - in this case, the value in column J will have been interpreted as a Number, so you could get it like this:
var numericSalary = e.range.getValues()[9];
That will be slower than using the values array, and it will still provide an unformatted value.
Formatting
Utilities.formatString uses "sprintf-like" formatting values. If you search the interwebs, you'll find lots of references for sprint variables, some of which are helpful. Here's a format that will turn a floating-point number into a dollar-formatted string:
'$%.2f'
$ - nothing magic, just a dollar sign
% - magic begins here, the start of a format
.2 - defines a number with two decimal places, but unspecified digits before the radix
f - expect a floating point number
So this is your simplest line of code that will do the conversion you're looking for:
var currentSalary = Utilities.formatString( '$%.2f', +e.values[9] );
The correct format to get your string into a proper numeric format is as follows:
var myString = myString.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
If you only care about the $ sign and don't need commas the code below (also shown in one of the answers above will suffice.
var myString = Utilities.formatString( '$%.2f', myString );
In my experience toLocaleString performs sometimes performs strangely in Apps Script as opposed to JavaScript
I know this was years ago but might help someone else.
You should be able to add the commas in between with this
.toLocaleString(); after you format the string decimal then add the '$' sign in the beginning by just concating them together.
Ex:
myString = Utilities.formatString( '%.2f', myNumber );
myString = '$' + myString.toLocaleString();
Related
I've a Sheets with many values in euro with 3 values after the decimal point (for exemple (2,154 €). I would like to convert this document in PDF to join it in mail.
When I convert it in temporary Sheet, this value change and I have 2.154 instead of. I would like to change the format of this cell.
So I decided to apply a setFormatNumber (.setNumberFormat('#,###.000 [$€]')) at this value but I don't get the result what I want. I obtain 2.154 € but I would like to have "," an not "." to separe entire to decimal values. I try to modify setFormatNumber by (.setNumberFormat('#,###,000 [$€]')) but my result is 2.154000 €.
I don't want to apply toString method and use replace method after because I think it's possible to have what I want by using this method.
Anyone can help me with that please ? I don't join my code because it's so long and, except the setNumberFormat, it's not interesting for you but if you need it, I can edit my post. Sorry for my english, I don't speak and write it very well.
Dots and commas have other meanings in the context of this “mask”-like parameter.
The numberFormat parameter of the setNumberFormat() is documented here.
According to the documentation, dots indicate where the decimal separator will be in the mask and commas indicate where the thousand separator will be.
The symbol of the decimal separator is however controlled according to the Spreadsheet locale settings.
You can change those settings via UI going to File > Settings > General > Locale or via Apps Scripts using the method SpreadsheetApp.getActive().setSpreadsheetLocale('XXXXX')
I have a csv, which looks like this:
name,code,age
Himsara,9877,12
John,9437721,16
Razor,232,45
I have to replace the column code according to some regular expressions. My logic is shown in a Scala code below.
if(str.trim.length == 9 && str.startsWith("369")){"PROB"}
else if(str.trim.length < 8){"SHORT"}
else if(str.trim.startsWith("94")){"LOCAL"}
else{"INT"}
I used a UpdateRecord Processor to replace the data in the code column. I added a property called /code which contains the value.
${field.value:replaceFirst('^[0-9]{1,8}$','SHORT'):replaceFirst('[94]\w+','OFF_NET')}
This works when replacing code's with
length less than 8 with "SHORT"
starting with 94 with "LOCAL"
I am unable to find a way to replace data in the column, code when it's equal to 8 digits AND when it starts with 0. Also how can I replace the data if it doesn't fall into any condition mentioned above. (Situation which the data should be replaced with INT)
Hope you can suggest a workflow or value to be added to the property in Update record to make the above two replacements happen.
There is a length and startsWith functions.
${field.value:length():lt(8):ifElse(
'SHORT', ${field.value:startsWith(94):ifElse(
'LOCAL', ${field.value:length():equals(9):and(${field.value:startsWith(369)}):ifElse(
'PROB', 'INT'
)})})}
I have put the line breaks for easy to recognize the functions but it should be removed.
By the way, the INT means that some string values to replace? Sorry for the confusion.
Well, if you want to regular expression only, you can try the below code.
${field.value
:replaceFirst('[0-9]{1,8}', 'SHORT')
:replaceFirst('[94]\w+', 'OFF_NET')
:replaceFirst('369[0-9]{6}', 'PROB')
:replace(${field.value}, 'INT')
}
In my web app I need to allow the user to enter in money into an input field. The currency and how the amount is formatted can be different in different fields. So while the user can enter this in one field:
$1,234.56
for US dollars for the United States region, they can enter this in another:
€ 1.234,56
for euros using Germany as the region.
Regardless what the currency is and what region the amount is formatted in, the value internally should be returned in the same manner, which in this case would be:
1234.56
I could just use an input field with a type set to "text" but then I would have to write code to check for the currency symbol and how the value is formatted. But it's possible that the user doesn't enter a currency symbol. Also, I don't see a clear way of distinguishing regional numbers without the currency symbol.
Is there any solution that lets the user select the currency symbol and manages the value entered to provide a consistent internal value?
If there is a way to achieve this using just a specifically typed HTML input I don't know about it, the closest one would be type=number, though that doesn't allow for formatted numbers.
For a text type and having code check the input, if you can assume the numbers to always be formatted as latin numbers, you can use a rather naive approach using regular expressions.
function normalize(currency) {
const naive = /(?:(-)\s*)?(?:[^0-9]+\s*)?([0-9]{1,3}(?:[ ,\.]?[0-9]{3})*)(?:[,\.]([0-9]+))?(?:\s*[^0-9-]+)?(?:\s*(-))?/;
const [,mina,base,fraction,minb] = currency.match(naive)||[];
const min = mina || minb ? '-' : '';
return base ? Number(`${min}${base.replace(/[^\d]+/g, '')}.${fraction||0}`) : '';
}
const output = document.querySelector('output');
document.querySelector('input[name=currency]')
.addEventListener('input', (event) => {
const { value } = event.target;
output.value = normalize(value);
});
<label>
amount <input type=text name=currency>
</label>
<div>
The normalized value: <output></output>
</div>
The expression is build around the following concepts:
(?:(-)\s*)?(?:[^0-9]+\s*)?: a minus symbol may be before the number, optionally followed by a currency symbol
([0-9]{1,3}(?:[ ,\.]?[0-9]{3})*): a base number which may contain grouping symbols and groups of three numbers
(?:[,\.]([0-9]+))?: an optional fraction number, indicated by either a comma or period
(?:\s*[^0-9-]+)?(?:\s*(-))?: a minus symbol may be after the number, optionally preceded by a currency symbol
This works for a lot of use cases, though I consider it naive mostly because it does not verify the "thousands separator" not being used also as fraction separator.
a more thorough example
I'm trying to add 1 to the value of a cell in a Google Spreadsheet when a certain event happens. But instead of...say changing -6 to -5, it changes it to -61. What's wrong with my code?
var Spending = sheet.getRange("B2");
var SPEND = [Spending.getCell(1, 1).getValue()];
**EVENT TRIGGERED**
SPEND = SPEND+1;
Spending.setValue(SPEND);
It appears that was reading the value as a string and simply concatenate it. Try to parse the the var SPEND to Integer.
example:
SPEND = parseInt(SPEND)+1 or SPEND = Number(SPEND)+1
for some reason i have yet to fully discover (mainly through lack on needing to know the deeper answer), + is treated as a concatenation for anything but an integer value. so unless the var is declared as an intiger, it's treated as a string. if it's a string, the "+" means to concat, not perform a mathematical operation. "-" doesn't seem to have the same dual usage, only being used for mathematical operations.
Hope that helps!
Im trying to figure out how to extract the lat/long of the start/end in a google maps directions link that looks like this:
https://www.google.com/maps/preview#!data=!1m4!1m3!1d189334!2d-96.03687!3d36.1250439!4m21!3m20!1m4!3m2!3d36.0748342!4d-95.8040972!6e2!1m5!1s1331-1399+E+14th+St%2C+Tulsa%2C+OK+74120!2s0x87b6ec9a1679f9e5%3A0x6e70df70feebbb5e!3m2!3d36.1424613!4d-95.9736986!3m8!1m3!1d189334!2d-96.03687!3d36.1250439!3m2!1i1366!2i705!4f13.1&fid=0
Im guessing the "!" is a separator between variables followed by XY where x is a number and y is a lower case letter, but can not quite figure out how to reliably extract the coordinates as the number/order of variables changes as well as their XY prefixes.
ideas?
thanks
Well, this is old, but hey. I've been working on this a bit myself, so here's what I've figured out:
The data is an encoded javascript array, so the trick when trying to generate your own data string is to ensure that your formatting keeps the structure of the array intact. To do this, let's look at what each step represents.
As you're correctly figured out, each exclamation point defines the start of a value definition. The first character, an int value, is an inner count, and (I believe) acts as an identifier, although I'm not 100% certain on this. It seems to be pretty flexible in terms of what you can have here, as long as it's an int. The second character, however, is much more important. It defines the data type of the value. I don't know if I've found all the data types yet, but the ones I have figured out are:
m: matrix
f: float
d: double
i: integer
b: boolean
e: enum (as integer)
s: string
u: unsigned int
x: hexdecimal value?
the remaining characters actually hold the value itself, so a string will just hold the string, a boolean will be '1' or '0', and so on. However, there's an important gotcha: the matrix data type.
The value of the matrix will be an integer. This is the length of the matrix, measured in the number of values. That is, for a matrix !1mx, the next x value definitions will belong to the matrix. This includes nested matrix definitions, so a matrix of form [[1,2]] would look like !1m3!1m2!1i1!2i2 (outer matrix has three children, inner matrix has 2). this also means that, in order to remove a value from the list, you must also check it for matrix ancestors and, if they exist, update their values to reflect the now missing member.
The x data type is another anomaly. I'm going to guess it's hexdecimal encoded for most purposes, but in my particular situation (making a call for attribution info), they appear to also use the x data type to store lat/long information, and this is NOT encoded in hex, but is an unsigned long with the value set as
value = coordinate<0 ? (430+coordinate)*1e7 : coordinate*1e7
An example (pulled directly from google maps) of the x data type being used in this way:
https://www.google.com/maps/vt?pb=!1m8!4m7!2u7!5m2!1x405712614!2x3250870890!6m2!1x485303036!2x3461808386!2m1!1e0!2m20!1e2!2spsm!4m2!1sgid!2sznfCVopRY49wPV6IT72Cvw!4m2!1ssp!2s1!8m11!13m9!2sa!15b1!18m5!2b1!3b0!4b1!5b0!6b0!19b1!19u12!3m1!5e1105!4e5!18m1!1b1
For the context of the question asked, it's important to note that there are no reliable identifiers in the structure. Google reads the values in a specific order, so always keep in mind when building your own encoded data that order matters; you'll need to do some research/testing to determine that order. As for reading, your best hope is to rebuild the matrix structure, then scan it for something that looks like lat/long values (i.e. a matrix containing exactly two children of type double (or x?))
Looks like the developer tools from current browsers (I am using Chrome for that) can give you a lot of info.
Try the following:
Go to Google Maps with Chrome (or adapt the instructions for other browser);
Open Developer Tools (Ctrl + Shift + I);
Go to Network tab. Clear the current displayed values;
Drag the map until some url with encoded data appears;
Click on that url, and then go to the Preview sub-tab;
Try this.
function URLtoLatLng(url) {
this.lat = url.replace(/^.+!3d(.+)!4d.+$/, '$1');
this.lng = url.replace(/^.+!4d(.+)!6e.+$/, '$1');
return this;
}
var url = new URLtoLatLng('https://www.google.com/maps/preview#!data=!1m4!1m3!1d189334!2d-96.03687!3d36.1250439!4m21!3m20!1m4!3m2!3d36.0748342!4d-95.8040972!6e2!1m5!1s1331-1399+E+14th+St%2C+Tulsa%2C+OK+74120!2s0x87b6ec9a1679f9e5%3A0x6e70df70feebbb5e!3m2!3d36.1424613!4d-95.9736986!3m8!1m3!1d189334!2d-96.03687!3d36.1250439!3m2!1i1366!2i705!4f13.1&fid=0');
console.log(url.lat + ' ' + url.lng);