Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse formatted number string to number #41

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
24 changes: 21 additions & 3 deletions src/FormatNumber.elm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module FormatNumber exposing (format)
module FormatNumber exposing (format, parse)

{-| This simple package formats `Float` numbers as pretty strings. It is
flexible enough to deal with different number of decimals, different thousand
Expand All @@ -22,7 +22,7 @@ Just convert them to `Float` before passing them to `format`:
-}

import FormatNumber.Locales as Locales
import FormatNumber.Parser exposing (parse)
import FormatNumber.Parser as Parser
import FormatNumber.Stringfy exposing (stringfy)


Expand Down Expand Up @@ -139,5 +139,23 @@ import FormatNumber.Stringfy exposing (stringfy)
format : Locales.Locale -> Float -> String
format locale number_ =
number_
|> parse locale
|> Parser.parse locale
|> stringfy locale

{-| Parses a pretty string into `Maybe Float`:

import FormatNumber.Locales exposing (Decimals(..), Locale, base, frenchLocale, spanishLocale, usLocale)

parse { base | thousandSeparator = "," } "100,000.345"
--> Just 100000.345

parse { base | thousandSeparator = "," } "-100,000"
--> Just -100000

parse { base | thousandSeparator = ",", negativePrefix = "(", negativeSuffix = ")" } "(100,000.546)"
--> Just -100000.546

-}
parse : Locales.Locale -> String -> Maybe Float
parse locale str =
Parser.parseString locale str
39 changes: 39 additions & 0 deletions src/FormatNumber/Parser.elm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module FormatNumber.Parser exposing
, addZerosToFit
, classify
, parse
, parseString
, removeZeros
, splitInParts
, splitThousands
Expand All @@ -13,6 +14,7 @@ import Char
import FormatNumber.Locales exposing (Decimals(..), Locale)
import Round
import String
import Regex


{-| `Category` is a helper type and constructor to classify numbers in positive
Expand Down Expand Up @@ -378,3 +380,40 @@ parse locale original =
| prefix = locale.zeroPrefix
, suffix = locale.zeroSuffix
}

{-| Given a `Locale` parses a `String` into a `Maybe Float`:
-}
parseString : Locale -> String -> Maybe Float
parseString locale value =
let
isNegative : Bool
isNegative =
if String.left 1 value == locale.negativePrefix then
True
else
False
chrs : Regex.Regex
chrs =
["[", locale.negativePrefix, locale.negativeSuffix, locale.thousandSeparator, "]"]
|> String.concat
|> Regex.fromString
|> Maybe.withDefault Regex.never
splitValue : String -> List String
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we should use String -> (String, String) here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I change this, then I can move entire logic of joinParts into the parseString. I like the current structure as it cleanly segregates that out of parseString and allow me to test it separately.

Let me know if you still want it changed. I will do it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think making impossible states impossible worth the job : )

splitValue nbr =
nbr
|> Regex.replace chrs (\_ -> "")
|> String.split locale.decimalSeparator
sumNumber : Float -> Float -> Float -> Float
sumNumber i d pow =
if isNegative then
-1 * (i + d/(10^pow))
else
i + d/(10^pow)
in
case splitValue value of
integer :: decimal :: [] ->
Maybe.map3 (sumNumber) (String.toFloat integer) (String.toFloat decimal) (Just (toFloat (String.length decimal)))
integer :: [] ->
Maybe.map (\n -> if isNegative then -1 * n else n) (String.toFloat integer)
_ ->
Nothing