I am writing my first script for an invoice template based on a Google Doc.
It works fine. I need to use it in Germany so I need to be able to format the prices in the way they are shown in Germany.
I have written a short function to do this but am interested in whether there is a better or more obvious way of achieving this.
// this formats numbers as used in Germany
function numberToGerman(number){
number = Utilities.formatString("%d.%d,%02d", number/1000, number%1000, number%1*100);
return number;
}
It has a problem above 999.999,99 or below 1.000,00 at the moment but I hope there is a better solution out there.
Here is a slight modification of the answer by Elias Zamaria, replacing separators and ensuring exactly two decimals:
function numberToGerman(number){
var parts = number.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ".");
parts[1] = ((parts[1] || "") + "00").slice(0,2);
return parts.join(",");
}
The parts are the integer and fractional parts. Groups of three digits are processed by the regex, which places dots accordingly. The fractional parts is expanded/truncated to two places.
Remarks
Unfortunately, number.toLocaleString("de-DE") is not yet supported by Google Apps Script. One can use number.toLocaleString() but then the performance depends on someone having set the correct locale. Also, options concerning decimal places aren't supported either, so this doesn't help that much.
If this was to be used in a spreadsheet, you can apply appropriate formatting to cells either manually from the menu, or from a script: for example cell.setNumberFormat("#,###.00"). This looks like U.S.-style formatting, but once the locale of the spreadsheet is changed to Germany, it changes accordingly, e.g. 1.234.567,89.
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')
So I'd like 1/3 (which equals 0.33333 recurring) to return true and 1/8 (which equals 0.125 non-recurring) to be false.
Something like =ISRECURRING(A1) which returns a boolean.
The reason for this is that I want to highlight cells that have had rounding applied to them.
You can build a JavaScript function to check that and use it in your sheet as an Apps Script custom function. To achieve this, follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Copy this function in the script editor, and save the project (credits to Larry Battle, whose answer here this function is based on):
function ISRECURRING(num) {
num = (num || "").toString().replace(/\d$/, '');
var RE_PatternInRepeatDec = /(?:[^\.]+\.\d*)(\d{2,})+(?:\1)$/;
return RE_PatternInRepeatDec.exec(num) ? true : false;
};
Now, if you go back to your spreadsheet, you can use this function as if you were using a regular sheets formula. You just have to provide the appropriate range as an argument, as you can see here:
Note:
As #Ron Rosenfeld noted in a comment below, this function will not work for very long repetends (e.g. 1/97). This is due to the precision limit that spreadsheets have (15 digits, as far as I know). Because of this, repetends longer than 7 digits won't be detected.
Reference:
Custom Functions in Google Sheets
Credit to #Ron Rosenfeld.
=A1-ROUND(A1,14) <> 0 works a treat. Where A1 is the number in question.
I can't guarantee that every rounded number works but it appears to work on all the examples I've tried.
I am a new user of Google Script and scripts in general.
My company has Office licences and for strategics reasons it wants to use google services.
My problem is that we extract from a software various data containing numbers. When we paste these datas on a spreadsheet the negatives numbers format is not recognized because they are like :
screenShot
I would like to apply the script only on a selection of active spreadsheet and the texte "1 234,56-" become a number "-1 234,56". The selection may contains positive number as "1 234,56".
Thank you for your help.
Best regards,
Anthony.
=VALUE(REGEXREPLACE(REGEXREPLACE(TEXT(A1; "0000.00"); "\s"; ""); "(.*?)-"; "-$1"))
This will first convert the number to a text string, then remove the whitespace character, then move the - sign in front of the number, and lastly convert it back to a numeric value.
Before:
1 234,56-
352,90
2 342,89-
24,0
45,00-
After (and you can use Sheets' number formats to further alter if needed):
−1234,56
352,9
−2342,89
24
−45
if you're range is not too long you can try something like that :
How to replace text in Google Spreadsheet using App Scripts?
i have the same probleme for a file of 5k line to remplace "." by "," it work but need a bit time :)
i hope this will help you
Best regards
Is it possible to apply the 'Automatic' number format programmatically through GAS? My issue is that as I write columns of numbers, Sheets seems to attempt to apply appropriate formatting, but gets it wrong sometimes. That is, particular small integers (1 sometimes) will be formatted as dates. The range is being written in one myRange.setValues() method and I can't see any pattern to the mistakes and therefore don't see any way to prevent the surprise mis-formatting.
But, when I select the range in sheets and just click "Automatic" on the number format menu all returns to normal. It doesn't help to click that upfront as the writing of data somehow resets the format.
Despite the long-winded intro, my question is very simple: how to programmatically apply "Automatic" number formatting. I'm thinking this is very basic, especially since google and searches here have been no help.
My current fallback solution is to use myRange.setNumberFormat("0") as the format for the whole range. This is not ideal as some numbers are very large and are easier to read in scientific notation. There are also some text strings in the range, but these format properly regardless of format applied. I also would prefer to avoid having to iterate through the data and test for values to determine the best format, when it's just a couple clicks in the user interface.
we can use .setNumberFormat('General');
Below is the example:
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange("B:B").setNumberFormat("General");
I use copyFormatToRange to copy/apply Automatic format:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var source_cell = sheet.getRange("A1");//A1: cell having automatic format
source_cell.copyFormatToRange(sheet,1,1,2,2);//copy format of cell A1 to cell A2
You can write an API that opens another spreadsheet, read any cell that having the automatic format.
var ss = SpreadsheetApp.openById(SpreadsheetId);//Id of another spreadsheet
Then use copyFormatToRange to your wanted cell.
I was having trouble finding anything documented, and tried pretty much everything suggested previously (null, 'General', the "magic" format of '0.###############', etc., etc.).
In my particular case, I had ranges previously set to strict plain text, which then got replaced with a checkbox data validation. Anytime the box was checked it was converted to the text "TRUE" instead of remaining a checkbox. 'General' and the "magic" format functionally worked fine, but did not actually set the format back explicitly to "Automatic".
I finally decided, why not just try this:
range.setNumberFormat('Automatic');
And it worked. This really should be documented, but at least a little bit of common sense lead me to the answer regardless.
If you don't have dates in the range, the below solution appears to be the best available option (without resorting to an API-based solution):
myRange.setNumberFormat('0.###############');
A zero-point-15x'#' seems to be a 'magic' number format that will allow very large numbers to show as scientific notation and smaller integers and decimals to show in the 'standard' format pre-application of number formatting. This is also the format that is returned for cells that contain non-dates formatted with the 'Automatic' selection in the user interface.
Adding or removing even one # will 'break the spell' and cause very large numbers to display in non-scientific notation. I also tested changes before the decimal place, but leaving the 15x#:
Also functional: myRange.setNumberFormat('#,##0.###############');
So there is some flexibility for prefixes.
Non-functional: myRange.setNumberFormat('#.###############');
The 0 is evidently required.
And finally,
Non-functional: savegameRange.setNumberFormat('0.##############[red]');
This turns numbers red, but breaks the 'magic' formatting. So no suffixes it appears.
Again, if you have dates in the range, this will not work as they will, not surprisingly, display as the underlying number. And potentially more problematic (but totally understandable), the only way to return them to date form is manually applying a date format, assuming you know which cells 'were' dates.
Complete replication of 'Automatic' number formatting requires traversing the range to find dates and apply desired date format, but otherwise applying the 'magic' format. (My original dataset was a mix of numbers and strings, so the simple approach given above works.)
Seeking a method to:
Take whitespace separated tokens in a String; return a suggested Word
ie:
Google Search can take "fonetic wrd nterpreterr",
and atop of the result page it shows "Did you mean: phonetic word interpreter"
A solution in any of the C* languages or Java would be preferred.
Are there any existing Open Libraries which perform such functionality?
Or is there a way to Utilise a Google API to request a suggested word?
In his article How to Write a Spelling Corrector, Peter Norvig discusses how a Google-like spellchecker could be implemented. The article contains a 20-line implementation in Python, as well as links to several reimplementations in C, C++, C# and Java. Here is an excerpt:
The full details of an
industrial-strength spell corrector
like Google's would be more confusing
than enlightening, but I figured that
on the plane flight home, in less than
a page of code, I could write a toy
spelling corrector that achieves 80 or
90% accuracy at a processing speed of
at least 10 words per second.
Using Norvig's code and this text as training set, i get the following results:
>>> import spellch
>>> [spellch.correct(w) for w in 'fonetic wrd nterpreterr'.split()]
['phonetic', 'word', 'interpreters']
You can use the yahoo web service here:
http://developer.yahoo.com/search/web/V1/spellingSuggestion.html
However it's only a web service... (i.e. there are no APIs for other language etc..) but it outputs JSON or XML, so... pretty easy to adapt to any language...
You can also use the Google API's to spell check. There is an ASP implementation here (I'm not to credit for this, though).
First off:
Java
C++
C#
Use the one of your choice. I suspect it runs the query against a spell-checking engine with a word limit of exactly one, it then does nothing if the entire query is valid, otherwise it replaces each word with that word's best match. In other words, the following algorithm (an empty return string means that the query had no problems):
startup()
{
set the spelling engines word suggestion limit to 1
}
option 1()
{
int currentPosition = engine.NextWord(start the search at word 0, querystring);
if(currentPosition == -1)
return empty string; // Query is a-ok.
while(currentPosition != -1)
{
queryString = engine.ReplaceWord(engine.CurrentWord, queryString, the suggestion with index 0);
currentPosition = engine.NextWord(currentPosition, querystring);
}
return queryString;
}
Since no one has yet mentioned it, I'll give one more phrase to search for: "edit distance" (for example, link text).
That can be used to find closest matches, assuming it's typos where letters are transposed, missing or added.
But usually this is also coupled with some sort of relevancy information; either by simple popularity (to assume most commonly used close-enough match is most likely correct word), or by contextual likelihood (words that follow preceding correct word, or come before one). This gets into information retrieval; one way to start is to look at bigram and trigrams (sequences of words seen together). Google has very extensive freely available data sets for these.
For simple initial solution though a dictionary couple with Levenshtein-based matchers works surprisingly well.
You could plug Lucene, which has a dictionary facility implementing the Levenshtein distance method.
Here's an example from the Wiki, where 2 is the distance.
String[] l=spellChecker.suggestSimilar("sevanty", 2);
//l[0] = "seventy"
http://wiki.apache.org/lucene-java/SpellChecker
An older link http://today.java.net/pub/a/today/2005/08/09/didyoumean.html
The Google SOAP Search APIs do that.
If you have a dictionary stored as a trie, there is a fairly straightforward way to find best-matching entries, where characters can be inserted, deleted, or replaced.
void match(trie t, char* w, string s, int budget){
if (budget < 0) return;
if (*w=='\0') print s;
foreach (char c, subtrie t1 in t){
/* try matching or replacing c */
match(t1, w+1, s+c, (*w==c ? budget : budget-1));
/* try deleting c */
match(t1, w, s, budget-1);
}
/* try inserting *w */
match(t, w+1, s + *w, budget-1);
}
The idea is that first you call it with a budget of zero, and see if it prints anything out. Then try a budget of 1, and so on, until it prints out some matches. The bigger the budget the longer it takes. You might want to only go up to a budget of 2.
Added: It's not too hard to extend this to handle common prefixes and suffixes. For example, English prefixes like "un", "anti" and "dis" can be in the dictionary, and can then link back to the top of the dictionary. For suffixes like "ism", "'s", and "ed" there can be a separate trie containing just the suffixes, and most words can link to that suffix trie. Then it can handle strange words like "antinationalizationalization".