I'd like to loop through the characters of a string and see if each is contained in another string. However, String.toList returns a list of Chars, not Strings, which isn't allowed by the String.contains function. Passing the Char to toString doesn't help, is there another way to achieve this goal, or do I just need another approach altogether?
> String.contains (toString 'a') "asdf"
False : Bool
Use String.fromChar to convert a character to string.
String.fromChar 'a' -- "a"
String.contains (String.fromChar 'a') "asdf" -- True
Use lists of characters directly:
> standard = String.toList "asdf"
['a','s','d','f'] : List Char
> candidateGood = String.toList "asd"
['a','s','d'] : List Char
> candidateBad = String.toList "abc"
['a','b','c'] : List Char
> List.all (\x -> List.member x standard) candidateGood
True : Bool
> List.all (\x -> List.member x standard) candidateBad
False : Bool
Related
I have a list of string and generate it to HTML dynamically with li tag. I want to assign that value to id attribute as well. But the problem is the string item has some special characters like :, ', é, ... I just want the output to include the number(0-9) and the alphabet (a-z) only.
// Input:
listStr = ["Pop & Suki", "PINK N' PROPER", "L'Oréal Paris"]
// Output:
result = ["pop_suki", "pink_n_proper", "loreal_paris"] ("loral_paris" is also good)
Currently, I've just lowercased and replace " " to _, but don't know how to eliminate special character.
Many thanks!
Instead of thinking of it as eliminating special characters, consider the permitted characters – you want just lower-case alphanumeric characters.
Elm provides Char.isAlphaNum to test for alphanumeric characters, and Char.toLower to transform a character to lower case. It also provides the higher function String.foldl which you can use to process a String one Char at a time.
So for each character:
check if it's alphanumeric
if it is, transform it to lower case
if not and it is a space, transform it to an underscore
else drop the character
Putting this together, we create a function that processes a character and appends it to the string processed so far, then apply that to all characters in the input string:
transformNextCharacter : Char -> String -> String
transformNextCharacter nextCharacter partialString =
if Char.isAlphaNum nextCharacter then
partialString ++ String.fromChar (Char.toLower nextCharacter)
else if nextCharacter == ' ' then
partialString ++ "_"
else
partialString
transformString : String -> String
transformString inputString =
String.foldl transformNextCharacter "" inputString
Online demo here.
Note: This answer simply drops special characters and thus produces "loral_paris" which is acceptable as per the OP.
The answer that was ticked is a lot more efficient than the code I have below. Nonetheless, I just want to add my code as an optional method.
Nonetheless, if you want to change accents to normal characters, you can install and use the elm-community/string-extra package. That one has the remove accent method.
This code below is inefficient as you keep on calling library function on the same string of which all of them would go through your string one char at a time.
Also, take note that when you remove the & in the first index you would have a double underscore. You would have to replace the double underscore with a single underscore.
import Html exposing (text)
import String
import List
import String.Extra
import Char
listStr = ["Pop & Suki", "PINK N' PROPER", "L'Oréal Paris"]
-- True if alpha or digit or space, otherwise, False.
isDigitAlphaSpace : Char -> Bool
isDigitAlphaSpace c =
if Char.isAlpha c || Char.isDigit c || c == ' ' then
True
else
False
main =
List.map (\x -> String.Extra.removeAccents x --Remove Accents first
|> String.filter isDigitAlphaSpace --Remove anything that not digit alpha or space
|> String.replace " " "_" --Replace space with _
|> String.replace "__" "_" --Replace double __ with _
|> String.toLower) listStr --Turn the string to lower
|> Debug.toString
|> Html.text
How can I find out the type of an Elm expression or a subexpression in elm-repl ?
Haskell's :type or :t equivalent in Elm REPL?
The Elm REPL automatically prints the type of whatever you enter. For example:
> "foo"
"foo" : String
> f = \a b c -> (a + 1, b ++ "!", c || False)
<function> : number -> String -> Bool -> ( number, String, Bool )
> f
<function> : number -> String -> Bool -> ( number, String, Bool )
> f2 a b c = (a + 1, b ++ "!", c || False)
<function> : number -> String -> Bool -> ( number, String, Bool )
As #amalloy points out, without an equivalent to GHCi's :type command, Elm REPL (as of 0.18) forces evaluation of an expression prior to showing you the type, which may be undesirable for expensive function calls. In its current version, there is no way around this.
Elm supports [1..100], but if I try ['a'..'z'], the compiler gives me a type mismatch (expects a number, gets a Char). Is there any way do make this work?
Just create a range of numbers and map it to chars:
List.map Char.fromCode [97..122]
Edit, or as a function:
charRange : Char -> Char -> List Char
charRange from to =
List.map Char.fromCode [(Char.toCode from)..(Char.toCode to)]
charRange 'a' 'd' -- ['a','b','c','d'] : List Char
Edit, from elm 0.18 and up, List.range is finally a function:
charRange : Char -> Char -> List Char
charRange from to =
List.map Char.fromCode <| List.range (Char.toCode from) (Char.toCode to)
I am trying to convert a string to integer using String.toInt. However, when I want to bind the result to a variable and then do some simple math with it I get this error:
Function add is expecting the 2nd argument to be:
Int
But it is:
Result String Int
How can I just extract the integer part of the result?
Here's how to supply the conversion with a default value in case the parsing fails.
String.toInt "5" |> Result.toMaybe |> Maybe.withDefault 0
toInt can fail in parsing. You need to check it using a case statement:
case toInt str of
Err msg -> ... -- do something with the error message
Ok val -> ... -- val is an Int which you can add
More about Result here
The integer can also be pulled out using
Result.withDefault 0 (String.toInt "2")
You can read more about it here
According to the Elm String reference documentation, if you are extracting a number from some raw user input, you will typically want to use Result.withDefault to handle bad data in case parsing fails. You can chain this operation using pipes for cleaner code:
String.toInt "5" |> Result.withDefault 0
Maybe.withDefault 0 (String.toInt "42")
Use map:
answer = Result.map2 (+) (String.toInt "1") (String.toInt "2")
map2:
Apply a function to two results, if both results are Ok. If not, the
first argument which is an Err will propagate through.
to have the add result as a string
resultAsString r =
case r of
Err msg -> msg
Ok value -> toString value
resultAsString answer
to make things easier you can create an addStrings function:
addStrings : String -> String -> Result String Int
addStrings a b =
Result.map2 (+) (String.toInt a) (String.toInt b)
You can even get away with the Result type altogether:
addStrings : String -> String -> String
addStrings a b =
let
r =
Result.map2 (+) (String.toInt a) (String.toInt b)
in
case r of
Err msg ->
msg
Ok value ->
toString value
Testing
import Html exposing (Html, text)
main : Html msg
main =
text (addStrings "1" "2")
output 3
The withDefault method forces you to define a value that can be used for calculations but it is not always possible to establish a value that is significant for errors. Most often you need all the possible values, and default is not fit. Here I provide a result type check function you can use to decide if you use or not the converted value:
isErrorResult r =
case r of
Err msg ->
True
Ok value ->
False
You can use it like this:
r = String.toInt "20b"
if isErrorResult r then
-- not a valid Interger, abort or whatever
else
-- a good integer, extract it
a = Result.withDefault 0 r
-- and make good use of it
the default value (0 in this case) passed to withDefault is meaningless, because we made sure that r is not an Err.
You can do this as below.
---- Elm 0.19.0 ----------------------------------------------------------------
Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
--------------------------------------------------------------------------------
> parseInt string = String.toInt string
<function> : String -> Maybe Int
> resultParseInt string = \
| Result.fromMaybe ("error parsing string: " ++ string) (parseInt string)
<function> : String -> Result String Int
> resultParseInt "12"
Ok 12 : Result String Int
> resultParseInt "12ll"
Err ("error parsing string: 12ll") : Result String Int
>
I have changed guys answers a bit, since it appears to be of type Maybe
isErrorResult : String -> Bool
isErrorResult r =
case String.toInt r of
Nothing -> True
Just value -> False
I have the following code:
doSomething : (s : String) -> (not (s == "") = True) -> String
doSomething s = ?doSomething
validate : String -> String
validate s = case (not (s == "")) of
False => s
True => doSomething s
After checking the input is not empty I would like to pass it to a function which accepts only validated input (not empty Strings).
As far as I understand the validation is taking place during runtime
but the types are calculated during compile time - thats way it doesn't work. Is there any workaround?
Also while playing with the code I noticed:
:t (("la" == "") == True)
"la" == "" == True : Bool
But
:t (("la" == "") = True)
"la" == "" = True : Type
Why the types are different?
This isn't about runtime vs. compile-time, since you are writing two branches in validate that take care, statically, of both the empty and the non-empty input cases; at runtime you merely choose between the two.
Your problem is Boolean blindness: if you have a value of type Bool, it is just that, a single bit that could have gone either way. This is what == gives you.
= on the other hand is for propositional equality: the only constructor of the type(-as-proposition) a = b is Refl : a = a, so by pattern-matching on a value of type a = b, you learn that a and b are truly equal.
I was able to get your example working by passing the non-equality as a proposition to doSomething:
doSomething : (s : String) -> Not (s = "") -> String
doSomething "" wtf = void $ wtf Refl
doSomething s nonEmpty = ?doSomething
validate : String -> String
validate "" = ""
validate s = doSomething s nonEmpty
where
nonEmpty : Not (s = "")
nonEmpty Refl impossible
As far as I understand the validation is taking place during runtime
but the types are calculated during compile time - thats way it
doesn't work.
That's not correct. It doesn't work because
We need the with form to perform dependent pattern matching, i. e. perform substitution and refinement on the context based on information gained from specific data constructors.
Even if we use with here, not (s == "") isn't anywhere in the context when we do the pattern match, therefore there's nothing to rewrite (in the context), and we can't demonstrate the not (s == "") = True equality later when we'd like to call doSomething.
We can use a wrapper data type here that lets us save a proof that a specific pattern equals the original expression we matched on:
doSomething : (s : String) -> (not (s == "") = True) -> String
doSomething s = ?doSomething
data Inspect : a -> Type where
Match : {A : Type} -> {x : A} -> (y : A) -> x = y -> Inspect x
inspect : {A : Type} -> (x : A) -> Inspect x
inspect x = Match x Refl
validate : String -> String
validate s with (inspect (not (s == "")))
| Match True p = doSomething s p
| Match False p = s