From 5ab4b75015fb114bca7bfe199e82644939fa55e1 Mon Sep 17 00:00:00 2001 From: Eduardo Cuducos Date: Sat, 11 Feb 2017 15:43:27 -0200 Subject: [PATCH] General refactor Most changes are related to readability and to removing formatInt --- src/FormatNumber.elm | 221 +++++++++++++++++-------------------------- src/Helpers.elm | 89 +++++++++-------- 2 files changed, 130 insertions(+), 180 deletions(-) diff --git a/src/FormatNumber.elm b/src/FormatNumber.elm index 44d3871..208d095 100644 --- a/src/FormatNumber.elm +++ b/src/FormatNumber.elm @@ -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 + ] diff --git a/src/Helpers.elm b/src/Helpers.elm index fe29507..a3ab72b 100644 --- a/src/Helpers.elm +++ b/src/Helpers.elm @@ -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'