1

This question relates to an animated map template which we have developed at the UKs Office for National Statistics. It has been applied to many datasets and geographies many uses without problem. For example,

http://www.ons.gov.uk/ons/interactive/vp3-census-map/index.html

http://www.statistica.md/pageview.php?l=ro&idc=390&id=3807

The .fla calls on a supporting .as file (see below) to introduce a thousand separator (in the UK a comma, in Germany a full stop (period) defined elsewhwere.

However, the dataset I am currently mapping has large negative values, and it tutrns out that the ORIGINAL HELPER FUNCTION below does not like negative values with 3, 6, 9 or 12 (etc) digits.

-100 to -999 for instance are rendered NaN,100 to NaN,999.

This is because such values are recognised as being 4 digits long. They are being split, the comma introduced, and the -ve sign is misunderstood.

I reckon the approach must be to use absolute values, add in the comma and then (for the negative values) add the -ve sign back in afterwards. But so far, trials of the ADAPTED HELPER FUNCTION have produced only error. :-(

Can anyone tell me how to put the -ve sign back in , please?

Many thanks.

Bruce Mitchell

==================================================================================

//ORIGINAL HELPER FUNCTION: ACCEPTS A NUMBER AND RETURNS A STRING WITH THOUSANDS SEPARATOR ATTACHED IF NECESSARY

function addThouSep(num) {  
    /*
    a. Acquire the number  -  'myTrendValue' or 'myDataValue' - from function calcValues
    b. Record it (still as a number) to data precision. 
    1. Turn dataORtrend into a string
    2. See if there is a decimal in it.
    3. If there isn't, just run the normal addThouSep.
    4. If there is, run addThouSep just on the first bit of the string - then add the decimal back on again at the end.
    */

    var myNum:Number = correctFPE(num);         //  Create number variable myNum and populate it with 'num' 
                                                //  (myTrendvalue or myData Value from calcValues function) passed thru 'correctPFE'
    var strNum:String = myNum+"";               //  Create string version of the dataORtrend number - so instead of 63, you get '63'
    var myArray = strNum.split(".");            //  Create array representing elements of strNum, split by decimal point.
    //trace(myArray.length);                    //  How long is the array?

    if (myArray.length==1) { // Integer, no decimal.
        if (strNum.length < 4)//999 doesn't need a comma.
            return strNum;
        return addThouSep(strNum.slice(0, -3))+xmlData.thouSep+strNum.slice(-3);

    } 
    else {              // Float, with decimal   
        if (myArray[0].length < 4)//999 doesn't need a comma
            return strNum;
        return (addThouSep(myArray[0].slice(0, -3))+xmlData.thouSep+myArray[0].slice(-3)+"."+myArray[1]); 
    }
}

==================================================================================

//ADAPTED HELPER FUNCTION: ACCEPTS A NUMBER AND RETURNS A STRING WITH THOUSANDS SEPARATOR ATTACHED IF NECESSARY

function addThouSep(num) {  
    /*
    a. Acquire the number  -  'myTrendValue' or 'myDataValue' - from function calcValues
    b. Record it (still as a number) to data precision. 
    1. Turn dataORtrend into a string
    2. See if there is a decimal in it.
    3. If there isn't, just run the normal addThouSep.
    4. If there is, run addThouSep just on the first bit of the string - then add the decimal back on again at the end.
    */

        var myNum:Number = correctFPE(num);     //  Create number variable myNum and populate it with 'num' 
                                                //  (myTrendvalue or myData Value from calcValues function) passed thru 'correctPFE'
        var myAbsNum:Number = Math.abs(myNum);  //  ABSOLUTE value of myNum 
        var strNum:String = myAbsNum+"";        //  Create string version of the dataORtrend number - so instead of 63, you get '63'
        var myArray = strNum.split(".");        //  Create array representing elements of strNum, split by decimal point.
        //trace(myArray.length);                //  How long is the array?


    if (myNum <0){  // negatives
        if (myArray.length==1)  { // Integer, no decimal.      
            if (strNum.length < 4)//999 doesn't need a comma.
                return strNum;
            return addThouSep(strNum.slice(0, -3))+xmlData.thouSep+strNum.slice(-3);
       } 
       else { // Float, with decimal           
            if (myArray[0].length < 4)//999 doesn't need a comma
                return strNum;
            return (addThouSep(myArray[0].slice(0, -3))+xmlData.thouSep+myArray[0].slice(-3)+"."+myArray[1]); 
        }
    }
    else    // positive
        if (myArray.length==1)  { // Integer, no decimal.      
            if (strNum.length < 4)//999 doesn't need a comma.
                return strNum;
            return addThouSep(strNum.slice(0, -3))+xmlData.thouSep+strNum.slice(-3);        
        } 
        else {              // Float, with decimal         
            if (myArray[0].length < 4)//999 doesn't need a comma
                return strNum;
            return (addThouSep(myArray[0].slice(0, -3))+xmlData.thouSep+myArray[0].slice(-3)+"."+myArray[1]); 
       }
}

==================================================================================

mihai
  • 2,746
  • 3
  • 35
  • 56
Bruce Mitchell
  • 159
  • 2
  • 14
  • So basically you're just looking for a function to convert any Number (negative, positive, with/without decimal values) to a format like: {sign-of-number}###,###.## - basically? – chamberlainpi Jun 20 '13 at 13:49
  • But in the main .fla file there are a host of nested if/thens which call on the addThouSep function. I'd rather not hacve to reengineer these. – Bruce Mitchell Jun 20 '13 at 13:55
  • But in the main .fla file there are a host of nested if/thens which call on the addThouSep function. I'd rather not hacve to reengineer these. – Bruce Mitchell Jun 20 '13 at 13:55
  • For example: if (xmlData.currency!=undefined) //IF THE XML DATA FILE INCLUDES A CURRENCY SYMBOL { txtStat.text=myAreaName+" ("+aDates[timeIndex]+") = "+xmlData.currency+""+addThouSep(myDataValue.toFixed(dataPrecision))+" "+xmlData.units+". "; – Bruce Mitchell Jun 20 '13 at 13:57

3 Answers3

3

If you're adding commas often (or need to support numbers with decimals) then you may want a highly optimized utility function and go with straightforward string manipulation:

public static function commaify( input:Number ):String
{
    var split:Array = input.toString().split( '.' ),
        front:String = split[0],
        back:String = ( split.length > 1 ) ? "." + split[1] : null,
        pos:int = input < 0 ? 2 : 1,
        commas:int = Math.floor( (front.length - pos) / 3 ),
        i:int = 1;

    for ( ; i <= commas; i++ )
    {
        pos = front.length - (3 * i + i - 1);
        front = front.slice( 0, pos ) + "," + front.slice( pos );
    }

    if ( back )
        return front + back;
    else
        return front;
}

While less elegant it's stable and performant — you can find a comparison suite at my answer of a similar question https://stackoverflow.com/a/13410560/934195

Community
  • 1
  • 1
Mark Fox
  • 8,694
  • 9
  • 53
  • 75
  • 1 million iterations with your method takes ~1577ms whereas mine takes ~5644ms. Kudos. – Marty Aug 26 '13 at 02:19
  • @MartyWallace Trust me, I *wish* your answer was faster — AS3 just has a cruddy regular expression engine. – Mark Fox Aug 26 '13 at 03:03
1

Why not use something simple like this function I've made?

function numberFormat(input:Number):String
{
    var base:String = input.toString();
    base = base.split("").reverse().join("");
    base = base.replace(/\d{3}(?=\d)/g, "$&,");

    return base.split("").reverse().join("");
}

Tests:

trace( numberFormat(-100) );    // -100
trace( numberFormat(5000) );    // 5,000
trace( numberFormat(-85600) );  // -85,600

Explanation:

  1. Convert the input number to a string.
  2. Reverse it.
  3. Use .replace() to find all occurrences of three numbers followed by another number. We use $&, as the replacement, which basically means take all of those occurences and replace it with the value we found, plus a comma.
  4. Reverse the string again and return it.
Marty
  • 39,033
  • 19
  • 93
  • 162
1

Did you try using the built in Number formatting options that support localized number values: Localized Formatting with NumberFormatter

JRobbins
  • 63
  • 1
  • 5