Commas in Numbers

From Erlang Community

(Difference between revisions)
Revision as of 23:15, 3 September 2006 (edit)
Bfulgham (Talk | contribs)

← Previous diff
Revision as of 12:43, 24 September 2006 (edit) (undo)
Ayrnieu (Talk | contribs)
(ah, I put this first in discussion, but nevermind: I hate the given solution. Also, they aren't "Perl-compatible regular expressions".)
Next diff →
Line 5: Line 5:
== Solution == == Solution ==
-Use the Perl-compatible regular expression engine to insert the commas where needed. It's simplest to reverse the string prior to the regexp-pass so that we can hop over any decimal portion. Then reverse the string back on return. +Turn the number into a list and then use pattern-matching to insert commas where needed.
 + 
<code> <code>
-partition_number(Number, Separator) ->+partition_integer(N,P) ->
- SepChar = hd(Separator),+ partition_integer(lists:reverse(integer_to_list(N)),P,[]).
- NumStr = if+ 
- is_integer(Number) -> hd(io_lib:format("~B", [Number]));+partition_integer([A,B,C,D|T],P,Acc) ->
- is_float(Number) -> hd(io_lib:format("~f", [Number]));+ partition_integer([D|T],P,[P,C,B,A|Acc]);
- true -> Number+partition_integer(L,_,Acc) ->
- end,+ lists:reverse(L) ++ Acc.
- % If there is a decimal point, use that as the starting point+
- {ChgStr, Remain} = case regexp:first_match(NumStr, "\\.") of+
- {match,Start,Length} ->+
- {string:substr(NumStr, 1, Start - 1),+
- string:substr(NumStr,Start,string:len(NumStr))};+
- nomatch -> {NumStr, ""}+
- end,+
- {ok, Subs, _} = regexp:gsub(lists:reverse(ChgStr),+
- "([0-9][0-9][0-9])", "&" ++ Separator),+
- Final = case lists:nth(string:len(Subs), Subs) of+
- SepChar -> string:substr(Subs, 1, string:len(Subs) - 1);+
- _ -> Subs+
- end,+
- lists:reverse(Final) ++ Remain.+
</code> </code>
-While this function works, it would be better if it checked for someone passing the separator parameter as a character (rather than a string). +Floating-point numbers generally require additional separator, and also an explicit fractional-part length:
-Here's an example of partition_number in action:  
<code> <code>
-1> partition_number(1918928282, ",").+partition_float(N,P,FP,L) ->
 + F = tl(tl(hd(io_lib:format("~.*f",[L,N-trunc(N)])))),
 + lists:flatten([partition_integer(trunc(N),P),FP,F]).
 +</code>
 + 
 +Here's an example of the above in action:
 +<code>
 +1> partition_integer(1918928282, $,).
"1,918,928,282" "1,918,928,282"
-2> partition_number(1982928828, "'"). +2> partition_integer(1982928828, $').
"1'982'928'828" "1'982'928'828"
-3> partition_number(1982928828.12345, "'").+3> partition_float(1982928828.12345, $', $., 6).
"1'982'928'828.123450" "1'982'928'828.123450"
-</code>+</code>
- +
-In addition to Erlang's various other string-related ills (mainly poor memory and time performance), it has a very limited set of regular expressions. For example, our implementation of partition_number is over twice as long as the [[http://schemecookbook.org/view/Cookbook/NumberRecipeCommas][Scheme alternative] because it has no look-ahead (or negative look-ahead) searching functionality. +
[[Category:CookBook]][[Category:StringRecipes]][[Category:ListRecipes]][[Category:NumberRecipes]] [[Category:CookBook]][[Category:StringRecipes]][[Category:ListRecipes]][[Category:NumberRecipes]]

Revision as of 12:43, 24 September 2006

Problem

You need to format a number for printing with commas (or other locale-dependant marks) in the appropriate places. This is important for creating human-legible print copies of reports.

Solution

Turn the number into a list and then use pattern-matching to insert commas where needed.

partition_integer(N,P) ->
    partition_integer(lists:reverse(integer_to_list(N)),P,[]).

partition_integer([A,B,C,D|T],P,Acc) ->
    partition_integer([D|T],P,[P,C,B,A|Acc]);
partition_integer(L,_,Acc) ->
    lists:reverse(L) ++ Acc.

Floating-point numbers generally require additional separator, and also an explicit fractional-part length:

partition_float(N,P,FP,L) ->
    F = tl(tl(hd(io_lib:format("~.*f",[L,N-trunc(N)])))),
    lists:flatten([partition_integer(trunc(N),P),FP,F]).

Here's an example of the above in action:

1> partition_integer(1918928282, $,).
"1,918,928,282"
2> partition_integer(1982928828, $'). 
"1'982'928'828"
3> partition_float(1982928828.12345, $', $., 6).
"1'982'928'828.123450"
Erlang/OTP Projects
Personal tools