-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Most changes are related to readability and to removing formatInt
- Loading branch information
Showing
2 changed files
with
130 additions
and
180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,172 +1,125 @@ | ||
module FormatNumber | ||
exposing | ||
( Locale | ||
, formatFloat | ||
, formatInt | ||
, usLocale | ||
, frenchLocale | ||
, spanishLocale | ||
) | ||
|
||
{-| This simple package formats numbers as pretty strings. It is flexible | ||
enough to deal with different number of decimals, different thousand | ||
separators and diffetent decimal separator. | ||
# Basic usage | ||
>>> formatFloat {frenchLocale | decimals = 4} pi | ||
"3,1416" | ||
>>> formatInt usLocale 42042 | ||
"42,042" | ||
>>> formatFloat spanishLocale e | ||
"2,718" | ||
module FormatNumber exposing (format) | ||
|
||
>>> formatFloat {decimals=3, thousandSeparator="", decimalSeparator=","} 123456.789 | ||
"123456,789" | ||
{-| This simple package formats `float` numbers as pretty strings. It is | ||
flexible enough to deal with different number of decimals, different thousand | ||
separators and diffetent decimal separator. | ||
# Full documentation | ||
## Locale | ||
@docs Locale, usLocale , frenchLocale, spanishLocale | ||
@docs format | ||
## Number formatting | ||
@docs formatFloat, formatInt | ||
## What about `Int` numbers? | ||
# Known bugs | ||
>>> import Locales exposing (usLocale) | ||
>>> format usLocale (toFloat 1234) | ||
"1,234.00" | ||
There are known bugs in how Elm handles large numbers: | ||
>>> import Locales exposing (usLocale) | ||
>>> format { usLocale | decimals = 0 } <| toFloat 1234 | ||
"1,234" | ||
* https://github.com/elm-lang/elm-compiler/issues/264 | ||
* https://github.com/elm-lang/elm-compiler/issues/1246 | ||
## Known bugs | ||
This library won't work with large numbers (over 2^31) until Elm itself is fixed: | ||
There are [known](https://github.com/elm-lang/elm-compiler/issues/264) | ||
[bugs](https://github.com/elm-lang/elm-compiler/issues/1246) in how Elm handles | ||
large numbers. This library cannot work with large numbers (over `2 ^ 31`) | ||
until Elm itself is fixed: | ||
>>> formatFloat usLocale 1e10 | ||
>>> format usLocale 1e10 | ||
"1,410,065,408.00" | ||
-} | ||
|
||
import String | ||
import Helpers exposing (..) | ||
|
||
|
||
-- Locales | ||
|
||
|
||
{-| Locale to configure the format options. | ||
-} | ||
type alias Locale = | ||
{ decimals : Int | ||
, thousandSeparator : String | ||
, decimalSeparator : String | ||
} | ||
|
||
|
||
|
||
-- Locales from | ||
-- https://docs.oracle.com/cd/E19455-01/806-0169/overview-9/index.html | ||
|
||
|
||
{-| Locale used in France, Canada, Finland, Sweden | ||
It uses a non-breakable thin space (U+202F) as thousandSeparator. | ||
>>> formatFloat frenchLocale 67295 | ||
"67 295,000" | ||
-} | ||
frenchLocale : Locale | ||
frenchLocale = | ||
Locale 3 "\x202F" "," | ||
|
||
|
||
{-| locale used in the United States, Great Britain, and Thailand | ||
>>> formatFloat usLocale 67295 | ||
"67,295.00" | ||
-} | ||
usLocale : Locale | ||
usLocale = | ||
Locale 2 "," "." | ||
|
||
{-| locale used in Spain, Italy and Norway | ||
>>> formatFloat spanishLocale 67295 | ||
"67.295,000" | ||
-} | ||
spanishLocale : Locale | ||
spanishLocale = | ||
Locale 3 "." "," | ||
|
||
|
||
|
||
-- Functions | ||
import Helpers | ||
import Locales | ||
import String | ||
|
||
|
||
{-| Format a float number as a pretty string: | ||
>>> formatFloat { decimals = 2, thousandSeparator = ",", decimalSeparator = "." } 1234.5567 | ||
>>> format { decimals = 2, thousandSeparator = ".", decimalSeparator = "," } 123456.789 | ||
"123.456,79" | ||
>>> format { decimals = 2, thousandSeparator = ",", decimalSeparator = "." } 1234.5567 | ||
"1,234.56" | ||
>>> formatFloat (Locale 3 "." ",") -7654.3210 | ||
>>> import Locales exposing (Locale) | ||
>>> format (Locale 3 "." ",") -7654.3210 | ||
"−7.654,321" | ||
>>> formatFloat (Locale 1 "," ".") -0.01 | ||
>>> import Locales exposing (Locale) | ||
>>> format (Locale 1 "," ".") -0.01 | ||
"0.0" | ||
>>> formatFloat (Locale 2 "," ".") 0.01 | ||
>>> import Locales exposing (Locale) | ||
>>> format (Locale 2 "," ".") 0.01 | ||
"0.01" | ||
>>> formatFloat (Locale 0 "," ".") 123.456 | ||
>>> import Locales exposing (Locale) | ||
>>> format (Locale 0 "," ".") 123.456 | ||
"123" | ||
>>> formatFloat (Locale 0 "," ".") 1e9 | ||
>>> import Locales exposing (Locale) | ||
>>> format (Locale 0 "," ".") 1e9 | ||
"1,000,000,000" | ||
>>> formatFloat (Locale 5 "," ".") 1.0 | ||
>>> import Locales exposing (Locale) | ||
>>> format (Locale 5 "," ".") 1.0 | ||
"1.00000" | ||
-} | ||
formatFloat : Locale -> Float -> String | ||
formatFloat locale num = | ||
(formatInt locale (truncate num)) | ||
++ (separator locale) | ||
++ (digits locale.decimals num) | ||
>>> import Locales exposing (usLocale) | ||
>>> format usLocale pi | ||
"3.14" | ||
{-| Format a integer number as a pretty string: | ||
>>> formatInt { decimals = 1, thousandSeparator = ",", decimalSeparator = "." } 0 | ||
"0" | ||
>>> formatInt (Locale 1 " " ".") 1234567890 | ||
"1 234 567 890" | ||
>>> formatInt (Locale 10 "," ".") -123456 | ||
"−123,456" | ||
-} | ||
formatInt : Locale -> Int -> String | ||
formatInt locale num = | ||
case compare num 0 of | ||
LT -> | ||
formatInt locale (-num) |> String.cons '−' | ||
|
||
EQ -> | ||
"0" | ||
>>> import Locales exposing (frenchLocale) | ||
>>> format { frenchLocale | decimals = 4 } pi | ||
"3,1416" | ||
GT -> | ||
splitThousands num |> String.join locale.thousandSeparator | ||
>>> import Locales exposing (frenchLocale) | ||
>>> format frenchLocale 67295 | ||
"67 295,000" | ||
>>> import Locales exposing (spanishLocale) | ||
>>> format spanishLocale e | ||
"2,718" | ||
{-| The separator, or "" | ||
>>> import Locales exposing (spanishLocale) | ||
>>> format spanishLocale 67295 | ||
"67.295,000" | ||
>> separator (Locale 10 "," ".") | ||
"." | ||
>>> import Locales exposing (usLocale) | ||
>>> format usLocale 67295 | ||
"67,295.00" | ||
>> separator (Locale 0 "," ".") | ||
"" | ||
-} | ||
separator : Locale -> String | ||
separator locale = | ||
if locale.decimals == 0 then | ||
"" | ||
else | ||
locale.decimalSeparator | ||
format : Locales.Locale -> Float -> String | ||
format locale num = | ||
let | ||
truncated : Int | ||
truncated = | ||
truncate num | ||
|
||
integers : String | ||
integers = | ||
case compare truncated 0 of | ||
GT -> | ||
truncated | ||
|> Helpers.splitThousands | ||
|> String.join locale.thousandSeparator | ||
|
||
EQ -> | ||
"0" | ||
|
||
LT -> | ||
-truncated | ||
|> toFloat | ||
|> format { locale | decimals = 0 } | ||
|> String.cons '−' | ||
in | ||
if locale.decimals == 0 then | ||
integers | ||
else | ||
String.concat | ||
[ integers | ||
, locale.decimalSeparator | ||
, Helpers.decimals locale.decimals num | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,61 @@ | ||
module Helpers exposing (..) | ||
module Helpers exposing (decimals, splitThousands) | ||
|
||
{-| Module containing helper functions | ||
@docs splitThousands, decimals | ||
-} | ||
|
||
|
||
{-| Returns the n first digits after the comma in a float | ||
{-| Split a `Int` in `List String` grouping by thousands digits: | ||
>>> splitThousands 12345 | ||
[ "12", "345" ] | ||
>>> splitThousands 12 | ||
[ "12" ] | ||
-} | ||
splitThousands : Int -> List String | ||
splitThousands num = | ||
if num >= 1000 then | ||
[ num % 1000 ] | ||
|> List.map toString | ||
|> List.map (String.padLeft 3 '0') | ||
|> List.append (splitThousands <| num // 1000) | ||
else | ||
[ toString num ] | ||
|
||
>>> digits 2 123.45 | ||
"45" | ||
|
||
>>> digits 0 125 | ||
"" | ||
{-| Returns the first n decimal digits: | ||
>>> digits 1 1.99 | ||
>>> decimals 2 123.45 | ||
"45" | ||
>>> decimals 1 1.99 | ||
"0" | ||
>>> digits 2 1.0 | ||
>>> decimals 2 1.0 | ||
"00" | ||
>>> digits 2 -1.0001 | ||
"00" | ||
>>> decimals 3 -1.0001 | ||
"000" | ||
>>> digits 2 0.01 | ||
>>> decimals 2 0.01 | ||
"01" | ||
>>> digits 2 0.10 | ||
>>> decimals 2 0.10 | ||
"10" | ||
-} | ||
digits : Int -> Float -> String | ||
digits digits f = | ||
let | ||
multiplicator = | ||
toFloat (10 ^ digits) | ||
|
||
fint = | ||
(round (f * multiplicator)) | ||
in | ||
splitThousands fint | ||
|> String.concat | ||
|> String.right digits | ||
|> String.padLeft digits '0' | ||
|
||
{-| Recursive helper to format an integer | ||
>>> splitThousands 12345 | ||
["12", "345"] | ||
-} | ||
splitThousands : Int -> List String | ||
splitThousands number = | ||
let | ||
-- Helper recursive function. | ||
-- Adds the last three digits of remainingNumber at the start of accumulator | ||
splitRemaining : Int -> List String -> List String | ||
splitRemaining remainingNumber accumulator = | ||
if remainingNumber >= 10 ^ 3 then | ||
splitRemaining | ||
(remainingNumber // 10 ^ 3) | ||
((remainingNumber % 10 ^ 3 |> toString |> String.padLeft 3 '0') :: accumulator) | ||
else | ||
(toString remainingNumber) :: accumulator | ||
in | ||
splitRemaining number [] | ||
decimals : Int -> Float -> String | ||
decimals digits num = | ||
digits | ||
|> toFloat | ||
|> (^) 10 | ||
|> (*) num | ||
|> round | ||
|> splitThousands | ||
|> String.concat | ||
|> String.right digits | ||
|> String.padLeft digits '0' |