Reply To: Comma Separator in Numbers

#1242794
Kal Starkis
Member

Hi Masood

Firstly, in response to your original question…

When working with regular expressions, you can be far more efficient with your code if you use quantifiers. For example, instead of writing (\d\d\d) you can just write \d{3}. Since you’re not sure how many digits there will be in a row, you can just search for one or more with a plus character: \d+. Each group of digits may or may not have a comma after it—in other words, the comma is optional. We can match an optional character with a question mark: \d+,?. The complete number has one or more of these groups, so our regex now looks like this: (\d+,?)+. Now you only want to match currencies (not phone numbers, years, etc), so lets’s only match strings that have one of the currency symbols before it. We can achieve this with a ‘positive lookbehind’: (?<=\$|ÂŁ|€). We’re not really interested in everything after the decimal point, so we’ll just ignore it. So… here’s our complete regex:

(?<=\$|£|€)(\d+,?)+

(For more help with regex basics, check out my article: [Getting started with regular expressions](https://inkwire.app/articles/getting-started-with-regular-expressions.html).)

So how do we combine this approach with capture groups, so you can reconstruct each number with $1, $2, etc? Unfortunately we can’t. Quantifiers don’t dynamically create groups as they go. This kind of problem isn’t one we can easily solve with regular expressions alone, so yeah… we need to write a script.

I’ve modified and simplified Ariel’s script to your purposes here. You can define a thousands separator (set to a comma by default) and whether you want to ignore 4-digit numbers (set to true by default). The script will search your selected text, or the whole story if you place your cursor anywhere in the text or select a text frame…


app.doScript (main, undefined, undefined, UndoModes.ENTIRE_SCRIPT, "Format numbers");

function main(){
    // Script settings
    var delimiter = ","; // Thousands separator
    var ignore4 = true // Ignore 4-digit numbers?
    
    // Get selection
    var mySelection = textSelection();
    if (!mySelection) {
        alert("No text selected.");
        return;
    }
    
    var mySourceText;
    var mySelectionType = mySelection.constructor.name;
    if (mySelectionType == "InsertionPoint" || mySelectionType == "TextFrame") {
        // Select whole story
        mySourceText = mySelection.hasOwnProperty("storyOffset") ? mySelection.storyOffset.parentStory : mySelection.insertionPoints[0].parentStory;
    } else {
        mySourceText = mySelection;
    }

    var mySearch = /(?<=\$|£|€)(\d+,?)+/;

    app.findGrepPreferences = null;
    app.findGrepPreferences.findWhat = mySearch.source;
    var myFinds = mySourceText.findGrep();
    var f, n, t;
    while (f = myFinds.pop()){
        n = f.contents.toString().replace(/,/g,''); // Remove existing commas from string
        // Add commas to thousands
        if (!ignore4 || (n && n.length > 4)){
            t = n.split('').reverse().join('').match(/.{1,3}/g).join(delimiter).split('').reverse().join('');
        }
        t && f.contents = t;
    }
    
    function textSelection() {  // Returns text-based object
        if ( app.selection.length == 1 && app.selection[0].hasOwnProperty("findGrep") ) {
            return (app.selection[0]);
        }
        // Invalid selection
        return null;
    }
}

Let me know if you want me to explain anything about the script. Cheers!

This article was last modified on May 2, 2020

Comments (0)

Loading comments...