I'm dealing with a JSON API that returns a number as a string. For example "12", however, this field value could also be non-numeric, for example: "-".
I've parsed the JSON data into a map and I want to extract this field to as an integer in elixir, and then perform some operation depending on where the integer falls in a certain range.
There seem to be multiple ways to do this the two main being Integer.parse/1, which returns a tuple or :error, and String.to_integer/1, which returns the integer or raises an exception.
What is the best way to perform some operation based on where the numeric value contained inside a string falls in a certain range, handling parsing errors appropriately?
If one expects the value can be not an integer (under normal, not exceptional circumstances,) one should use Integer.parse/2:
analysis =
case Integer.parse(string_value_from_json) do
{i, ""} when i in 1..10 ->
"integer #{i} between 1 and 10"
{i, ""} when i in 11..20 ->
"integer #{i} is between 11 and 20"
{i, ""} ->
"integer #{i} was out of range"
_ -> "integer could not be parsed"
end
Throwing and handling an exception should be left as a last resort and is discouraged to be used to control a flow.
The documentation for Integer.parse says to use String.to_integer for this purpose, so it's probably best to follow the guidelines:
If you want to convert a string-formatted integer directly to a
integer, String.to_integer/1 or String.to_integer/2 can be used
instead.
However, String.to_integer raises an error if the string cannot be parsed. Error cases can be caught and assigned to nil, then the result pattern-matched over:
integer_value =
try do
String.to_integer(string_value_from_json)
rescue
_ -> nil
end
analysis =
case integer_value do
nil -> "integer could not be parsed"
integer_value when integer_value in 1..10 -> "integer #{integer_value} between 1 and 10"
integer_value when integer_value in 11..20 -> "integer #{integer_value} is between 11 and 20"
integer_value -> "integer #{integer_value} was out of range"
end
IO.puts analysis
Related
In F# I have an optional function with signature type func= (string-> string list) option. How to pass an optional string parameter of signature type param= string option to the func method.
Is there any way to do this without using nested if else.
I didn't notice right away that the function itself is also wrapped in option. I'll keep the original answer to misread question below for reference, but here's the answer for the actual question:
First, you still have to decide what you want to do when there is no function and/or no string (see below). Once you have the decision, you can just match on the tuple:
let resultingList =
match func, param with
| Some f, Some p -> f p // When both are present, apply
| _ -> [] // Otherwise, return empty list
Here, I collapse all cases when either one or both are absent. But if you need different behavior for these cases, you can enumerate all or some of them explicitly:
let resultingList =
match func, param with
| Some f, Some p -> f p
| Some f, None -> f ""
| None, Some _ -> [ "there was no function" ]
| None, None -> [ "there was no string AND no function" ]
The original answer to the misread question
The type string option means "There may be a string here, or not". So the first question to ask yourself is: what do you want to do when there is no string? I can't help you decide on this one, because this depends on your larger problem (i.e. what you are ultimately trying to do).
The function string -> string list takes a string and returns a string list. So if you have a string, then you get back a string list. But what if you don't have a string? Should you get back a "nothing" (i.e. "there is no list", just like "there was no string" to begin with)? Or should you get an empty list? Or should you get some predefined value?
For the sake of gradual learning, let's just assume that, when there is no string, you want to get back an empty list. To do this, use pattern matching:
let resultingList =
match param with
| Some s -> func s
| None -> []
This program literally says: "if there is a string, call that string s and apply func to it; and if there is no string, return an empty list".
Same thing would apply to getting some other predefined list instead of the empty one:
let resultingList =
match param with
| Some s -> func s
| None -> [ "oopsie, there was no string!" ]
But if you want the other option - the "there is no list" one, - then your resulting value will need to be string list option, just like your input is string option. The option type has two constructors: Some to create values that are present, and None to denote "there is no spoon value".
let maybeResultingList =
match param with
| Some s -> Some (func s)
| None -> None
This literally says: "if there is a string, call it s, apply func to it, and wrap the result as Some, which means "there is a list here"; and if there is no string, just return None, which means "there is no list".
As luck would have it, this "apply function to the value, unless it's not there" operation is so common that there is a standard library function for it. It's called Option.map:
let maybeResultingList = Option.map func param
Another interesting way to look at it is this: first we pass func to Option.map and get back another function, and then we apply that function to param:
let maybeFunc = Option.map func
let maybeResultingList = maybeFunc param
This way of looking at it turns out to be very useful in practice. The venerable Scott Wlaschin has a totally kick-ass series on the concept. Check it out here: https://fsharpforfunandprofit.com/posts/elevated-world/
Right now I have two types:
type Rating = (String, Int)
type Film = (String, String, Int, [Rating])
I have a file that has this data in it:
"Blade Runner"
"Ridley Scott"
1982
("Amy",5), ("Bill",8), ("Ian",7), ("Kevin",9), ("Emma",4), ("Sam",7), ("Megan",4)
"The Fly"
"David Cronenberg"
1986
("Megan",4), ("Fred",7), ("Chris",5), ("Ian",0), ("Amy",6)
How can I look through then file storing all of the entries into something like FilmDatabase = [Film] ?
Haskell provides a unique way of sketching out your approach. Begin with what you know
module Main where
type Rating = (String, Int)
type Film = (String, String, Int, [Rating])
main :: IO ()
main = do
films <- readFilms "ratings.dat"
print films
Attempting to load this program into ghci will produce
films.hs:8:12: Not in scope: `readFilms'
It needs to know what readFilms is, so add just enough code to keep moving.
readFilms = undefined
It is a function that should do something related to Film data. Reload this code (with the :reload command or :r for short) to get
films.hs:9:3:
Ambiguous type variable `a0' in the constraint:
(Show a0) arising from the use of `print'
...
The type of print is
Prelude> :t print
print :: Show a => a -> IO ()
In other words, print takes a single argument that, informally, knows how to show itself (that is, convert its contents to a string) and creates an I/O action that when executed outputs that string. It’s more-or-less how you expect print to work:
Prelude> print 3
3
Prelude> print "hi"
"hi"
We know that we want to print the Film data from the file, but, although good, ghc can’t read our minds. But after adding a type hint
readFilms :: FilePath -> Film
readFilms = undefined
we get a new error.
films.hs:8:12:
Couldn't match expected type `IO t0'
with actual type `(String, String, Int, [Rating])'
Expected type: IO t0
Actual type: Film
In the return type of a call of `readFilms'
In a stmt of a 'do' expression: films <- readFilms "ratings.dat"
The error tells you that the compiler is confused about your story. You said readFilms should give it back a Film, but the way you called it in main, the computer should have to first perform some I/O and then give back Film data.
In Haskell, this is the difference between a pure string, say "JamieB", and a side effect, say reading your input from the keyboard after prompting you to input your Stack Overflow username.
So now we know we can sketch readFilms as
readFilms :: FilePath -> IO Film
readFilms = undefined
and the code compiles! (But we can’t yet run it.)
To dig down another layer, pretend that the name of a single movie is the only data in ratings.dat and put placeholders everywhere else to keep the typechecker happy.
readFilms :: FilePath -> IO Film
readFilms path = do
alldata <- readFile path
return (alldata, "", 0, [])
This version compiles, and you can even run it by entering main at the ghci prompt.
In dave4420’s answer are great hints about other functions to use. Think of the method above as putting together a jigsaw puzzle where the individual pieces are functions. For your program to be correct, all the types must fit together. You can make progress toward your final working program by taking little babysteps as above, and the typechecker will let you know if you have a mistake in your sketch.
Things to figure out:
How do you convert the whole blob of input to individual lines?
How do you figure out whether the line your program is examining is a title, a director, and so on?
How do you convert the year in your file (a String) to an Int to cooperate with your definition of Film?
How do you skip blank or empty lines?
How do you make readFilms accumulate and return a list of Film data?
Is this homework?
You might find these functions useful:
readFile :: FilePath -> IO String
lines :: String -> [String]
break :: (a -> Bool) -> [a] -> ([a], [a])
dropWhile :: (a -> Bool) -> [a] -> [a]
null :: [a] -> Bool
read :: Read a => String -> a
Remember that String is the same as [Char].
Some clues:
dropWhile null will get rid of empty lines from the start of a list
break null will split a list into the leading run of non-empty lines, and the rest of the list
Haskell has a great way of using the types to find the right function. For instance: In Gregs answer, he wants you to figure out (among other things) how to convert the year of the film from a String to an Int. Well, you need a function. What should be the type of that function? It takes a String and returns an Int, so the type should be String -> Int. Once you have that, go to Hoogle and enter that type. This will give you a list of functions with similar types. The function you need actually has a slightly different type - Read a => String -> a - so it is a bit down the list, but guessing a type and then scanning the resulting list is often a very useful strategy.
Recently I came across a code snippet which take string as input and returns float value but I'm getting confused with lines inside 3 and 4 construct. Anyone please, explain it.
as_number(S) ->
case string:to_float(S) of
{error, no_float} -> list_to_integer(S);
{N, _} -> N
end.
The function string:to_float takes in a string (which is a list in erlang) and tries to convert it to a float. It expects valid text that represents a float (ASCII digits), followed by the rest of the string. The return is a tuple of {Float, Rest} or {error, Reason}, Rest is the remaining part of the string which is not ASCII digits. In this instance, if the string can not be converted to a float, it tries to convert the list to an integer, which may not work, depending on the contents of the string.
For
subs
|> Array.map (fun x ->
let t, s, _ = x
getAuditEventsAsync s t (now - TimeSpan.FromDays(7.0)) now)
the compiler says for t when applying getAuditEventsAsync:
The type 'string' does not match the type 'string * string'
getAuditEventsAsync is a string -> string -> DateTime -> DateTime -> Async<string> and t and s are strings.
Any hints on why the compiler think I'm attempting to bind a string to a tuple of strings?
Update:
The root cause was actually in getAuditEventsAsync which does
async {
return! Http.AsyncRequestString
( url, httpMethod="GET",
headers = [ Accept HttpContentTypes.Json;
Authorization "Bearer " + t ])
}
for some url and token t where both are strings.
Operator precedence means that Authorization "Bearer " + t is interpreted as (Authorization "Bearer ") + t. Authorization returns string * string, (a non-overloaded) + is thus applied to a string * string and string which confuses the compiler.
The solution was to change Authorization "Bearer " + t to Authorization ("Bearer " + t).
So the question is now: why is the compiler error thrown at the application of getAuditEventsAsync rather than Authorization? :)
Here is a minimal example giving the same error message:
let Accept a = "Accept", a
let foo x = Accept "hoo" + x
foo "z"
The compiler and editor report two error messages, but (for some reason unclear to me) F# Interactive reports only the one you were getting. One of the errors is for + inside foo and the other is when calling foo.
If you comment out the last line, you get just one error on +:
error FS0071: Type constraint mismatch when applying the default type '(string * string)' for a type inference variable. Expecting a type supporting the operator '+' but given a tuple type Consider adding further type constraints
This makes sense and it says that + cannot be applied to tuples. Now, the compiler uses some defaulting mechanism so that it can continue type-checking and it decides that the argument of foo is also string * string. This then gives you the later error message when calling foo:
error FS0001: The type 'string' does not match the type 'string * string'
The confusing thing is that when you uncomment the last line again, the error message for the body of foo also changes to:
error FS0001: The type 'string' does not match the type 'string * string'
This is happening because the compiler now sees that foo is called with string as an argument and so the default guess x : string * string is replaced with x : string. I think this only affects the first error message though and so the second one still uses the default guess that foo takes string * string.
This is not entirely sensible behavior, but I guess that's the best the compiler can do given code with multiple errors - it has to use some default resolution for the error.
I want to use erlang datetime values in the standard format {{Y,M,D},{H,Min,Sec}} in a MNESIA table for logging purposes and be able to select log entries by comparing with constant start and end time tuples.
It seems that the matchspec guard compiler somehow confuses tuple values with guard sub-expressions. Evaluating ets:match_spec_compile(MatchSpec) fails for
MatchSpec = [
{
{'_','$1','$2'}
,
[
{'==','$2',{1,2}}
]
,
['$_']
}
]
but succeeds when I compare $2 with any non-tuple value.
Is there a restriction that match guards cannot compare tuple values?
I believe the answer is to use double braces when using tuples (see Variables and Literals section of http://www.erlang.org/doc/apps/erts/match_spec.html#id69408). So to use a tuple in a matchspec expression, surround that tuple with braces, as in,
{'==','$2',{{1,2}}}
So, if I understand your example correctly, you would have
22> M=[{{'_','$1','$2'},[{'==','$2',{{1,2}}}],['$_']}].
[{{'_','$1','$2'},[{'==','$2',{{1,2}}}],['$_']}]
23> ets:match_spec_run([{1,1,{1,2}}],ets:match_spec_compile(M)).
[{1,1,{1,2}}]
24> ets:match_spec_run([{1,1,{2,2}}],ets:match_spec_compile(M)).
[]
EDIT: (sorry to edit your answer but this was the easiest way to get my comment in a readable form)
Yes, this is how it must be done. An easier way to get the match-spec is to use the (pseudo) function ets:fun2ms/1 which takes a literal fun as an argument and returns the match-spec. So
10> ets:fun2ms(fun ({A,B,C}=X) when C == {1,2} -> X end).
[{{'$1','$2','$3'},[{'==','$3',{{1,2}}}],['$_']}]
The shell recognises ets:fun2ms/1. For more information see ETS documentation. Mnesia uses the same match-specs as ETS.