I'm not sure how to get access to multiple query parameters.
There is no example in the source code for that.
I know how to get one query parameter using <?>:
routeParser : Url.Parser (Route -> a) a
routeParser =
Url.oneOf
[ Url.map HomeRoute Url.top
, Url.map SettingsRoute (Url.s "settings" <?> Url.stringParam "sortBy")
]
parseLocation : Location -> Route
parseLocation location =
location
|> Url.parsePath routeParser
|> Maybe.withDefault NotFoundRoute
With parsePath I can get Dict with query params, but Is there an elegant way using <?>?
Edit:
I've used this example in elm-repl:
> parsePath (s "blog" <?> stringParam "search" <?> stringParam "sortBy") (Location "" "" "" "" "" "" "/blog" "?search=cats&sortBy=name" "" "" "")
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm
The 1st argument to function `parsePath` is causing a mismatch.
5| parsePath (s "blog" <?> stringParam "search" <?> stringParam "sortBy") (Location "" "" "" "" "" "" "/blog" "?search=cats&sortBy=name" "" "" "")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Function `parsePath` is expecting the 1st argument to be:
Parser (Maybe String -> Maybe String) (Maybe String)
But it is:
Parser (Maybe String -> Maybe String -> Maybe String) (Maybe String)
Hint: It looks like a function needs 1 more argument.
You can chain <?> together for multiple query parameters. Let's say your SettingsRoute also expected a pageNumber integer argument:
type Route
= ...
| SettingsRoute (Maybe String) (Maybe Int)
Your parser could then look like this:
Url.map SettingsRoute (Url.s "settings" <?> Url.stringParam "sortBy" <?> Url.intParam "pageNumber")
The query parameters from the incoming URL don't need to be in the same order as your map statement. The following example will give the same results for the above route:
settings?sortBy=name&pageNumber=3
settings?pageNumber=3&sortBy=name
Edit
You added an example from the REPL. The problem you are experiencing in the REPL is because you aren't mapping correctly to something that takes two parameters. Consider this example for the REPL:
> type alias SearchParams = { search : Maybe String, sortBy : Maybe String }
> parsePath (map SearchParams (s "blog" <?> stringParam "search" <?> stringParam "sortBy")) (Location "" "" "" "" "" "" "/blog" "?search=cats&sortBy=name" "" "" "")
Just { search = Just "cats", sortBy = Just "name" }
: Maybe.Maybe Repl.SearchParams
Related
I have encountered alias sign inside RGL in this operation:
mkRoot3 : Str -> Root3 = \fcl -> case fcl of {
f#? + c#? + l => {f = f ; c = c ; l = l} ;
_ => error ("mkRoot3: too short root" ++ fcl)
} ;
What does it mean, and what's the use of it?
The character # is used in pattern matching. The expression foo#bar means that you match something with the regex bar, and bind the result to variable foo.
Let's first recap some ways you can pattern match a string without #:
example1 : Str -> Str = \s -> case s of {
"x" => "the string is exactly x" ;
"x" + _ => "the string starts with x" ;
? => "the string is one character long" ;
? + ? => "the string is two characters long" ;
("a"|"i"|"u") => "the string is a vowel" ;
_ => "whatever, I'm bored"
} ;
In all of these, we are not reusing the string in the right-hand side. If you want to do that, you can just bind it into a variable, like this—not yet using # , because we are simply matching the start and the end of a string:
example2 : Str -> Str = \s -> case s of {
"x" + rest => "the string starts with x and ends with" ++ rest ;
start + "x" => "the string starts with" ++ start ++ "and ends with x" ;
_ => "..." } ;
Finally, if you want to match something to a regular expression and use whatever matches the regex on the RHS, now you need to use #:
example3 : Str -> Str = \s -> case s of {
v#("a"|"i"|"u") => "the string is the vowel" ++ v ;
a#? => "the string is one character long:" ++ a ;
a#? + b#? => "the string is two characters long:" ++ a ++ "followed by" ++ b ;
_ => "..." } ;
If you just tried to match a + b => … , or any other pattern that only contains variables, it wouldn't match words that are exactly 2 characters long. Instead it would just match empty string to one of them and the full string to the other.
So matching the regex ?, which matches exactly one character, and then binding the result to a variable, is the only way you can match something with exact length, and then reuse the matched character/string on the right-hand side.
You can read more on pattern matching at http://www.grammaticalframework.org/doc/gf-refman.html#pattern-matching .
Yet another question on how to decode things with Elm...
So the problem is that I need to decode a value which can be either a string, for example
"price_unknown"
or it can be an array of 2 elements, where first one is a string and the second is a float:
["price", 50.5]
And for the eventual value I have a type:
type Something
= PriceUnknown
= Price Float
into which I need to convert the value from the json response.
I tried to use a bunch of things using somewhat between the lines of:
decode MyString
|> required "other_value" Json.Decode.string
|> required "price" JD.oneOf [JD.string, JD.list (JD.oneOf [JD.string, JD.float])] |> JD.map mapper
( I use json_decode_pipeline package here )
But obviously it complains about different values in the lists and whatnot so I am stuck.
Thank you in advance.
You are pretty close, but all of the Decoders in oneOf have to have the same type. Also, destructuring mixed lists can be a pain. This uses elm-community/json-extra to make a manual decoding step easier.
myDecoder : Decoder SomethingElse
myDecoder =
decode MyString
|> required "other_value" Json.Decode.string
|> required "price" priceDecoder
priceDecoder : Decoder Something
priceDecoder =
JD.oneOf
[ priceUnknownDecoder
, priceKnownDecoder
]
priceUnknownDecoder : Decoder Something
priceUnknownDecoder =
JD.string
|> JD.andThen
(\string ->
if string == "price_unknown" then
JD.succeed PriceUnknown
else
JD.fail "Invalid unknown price string."
)
priceKnownDecoder : Decoder Something
priceKnownDecoder =
listTupleDecoder
JD.string
JD.float
|> JD.andThen
(\(tag, price) ->
if tag == "price" then
JD.succeed (Price price)
else
JD.fail "Invalid tag string."
)
listTupleDecoder : Decoder a -> Decoder b -> Decoder (a, b)
listTupleDecoder firstD secondD =
JD.list JD.value
|> JD.andThen
(\values ->
case values of
[first, second] ->
Result.map2
(,)
JD.decodeValue firstD first
JD.decodeValue secondD second
|> JD.Extra.fromResult
_ ->
JD.fail "There aren't two values in the list."
)
--define person type
type alias Person ={name: String, age: Int}
--created list of persons named people
people = [{name = "John", age = 41}, {name = "Nancy", age = 37}]
List item
names: List Person -> List String
names peeps = List.map(\peep -> peep.name) peeps
findPerson : String -> List Person -> Maybe Person
--using foldl to iterate list, here I am getting compile time error
findPerson name peeps = List.foldl(\peep memo -> case memo of Just _ ->
Nothing -> if peep.name == name then Just peep else Nothing ) Nothing peeps
main = text <| toString <| findPerson "John" people
Elm is whitespace-sensitive. The lines you flagged have invalid Elm code.
Your case statements should be in the form of:
case something of
Just val -> "we have a valid value: " ++ val
Nothing -> "we have nothing"
Furthermore, a foldl is probably not what you want for finding something in a list. You could get by with this implementation, which filters a list and takes the first element, if it exists.
findPerson : String -> List Person -> Maybe Person
findPerson name peeps =
List.filter (\peep -> peep.name == name) peeps
|> List.head
I am doing a http request with elm and my response is an Array of Objects. Each object is as follows
obj = {
title: "Some Title",
words: [ "word1", "word2", "word3", "word4" ]
}
Here is my decoder so far:
type alias ThisRes = List ResObj
type alias ResObj =
title: String
words: List String
decoded : Decoder ThisRes
decoded =
decode ThisRes
I can't seem to get the decoder right and any help that can be provided would be appreciated.
obj =
"""
{
"title": "Some Title",
"words": [ "word1", "word2", "word3", "word4" ]
}
"""
type alias ResObj =
{ title : String, words : List String }
objDecoder =
map2 ResObj
(at [ "title" ] string)
(at [ "words" ] (list string))
headingFrom : Result String ResObj -> String
headingFrom result =
case result of
Ok resobj ->
resobj.title
Err reason ->
toString reason
main =
h1 [] [ text <| headingFrom <| decodeString objDecoder obj ]
Breaking this down:
obj is just a string representing some JSON, to illustrate the example.
You define the ResObj type alias, and get a type constructor for it. Now you can create ResObj values by calling ResObj "MyTitle" ["word1", "wordb", "wordFightingMongooses"].
objDecoder is a value that helps decode JSON into ResObj. It calls map2, which takes a type constructor, and then two decoded JSON fields. (at ["title"] string) says "turn the value at title in the JSON into a string" and you can guess what the next line does. So this evaluates to, ultimately, ResObj "SomeTitle" ["word1", "word2", "word3", "word4"] and creates your value.
Down in our main, we have the expression decodeString objDecoder obj. decodeString takes a decoder value, and a JSON string, and sees if it can decode it. But it doesn't return the ResObj itself -- because the decoding can fail, it returns a Result.
So we have to process that Result with our function headingFrom. If the decoding succeeded our result will be Ok resobj, and we can work with the resobj finally. If it failed it'll be Err reason.
I'm receiving a JSON that looks like this:
{ name: "NAME1", value: "true" }
I would like to create a json decoder that would create a record like this:
{ name: "NAME1", value: True }
I'm trying to make a decoder that transforms "true" into True. I did this so far:
userProperties : Json.Decode.Decoder Props
userProperties =
Json.Decode.object2 (,)
("name" := Json.Decode.string)
("value" := Json.Decode.string)
`andThen` \val ->
let
newVal = -- Do something here?
in
Json.Decode.succeed <| newVal
There are a few problems in your example so let's step through each of them.
You haven't shown the definition of Props so I'm assuming, based on your example, that it is this:
type alias Props = { name : String, value : Bool }
You are passing (,) as the first argument to object2 which indicates that you will be returning a Decoder of type tuple. That should probably be:
Json.Decode.object2 Props
Now, the way you are using andThen won't compile because of its order of precedence. If you were to parenthesize the whole thing, it would look like this:
userProperties =
(Json.Decode.object2 Props
("name" := Json.Decode.string)
("value" := Json.Decode.string))
`andThen` \val ->
let
newVal = -- Do something here?
in
Json.Decode.succeed <| newVal
That isn't going to be correct since the thing you want to andThen is the string "true" in the "value" field. To do that, I would recommend creating a decoder which provides that boolean decoder:
stringBoolDecoder : Json.Decode.Decoder Bool
stringBoolDecoder =
string `andThen` \val ->
case val of
"true" -> succeed True
"false" -> succeed False
_ -> fail <| "Expecting \"true\" or \"false\" but found " ++ val
I'm only guessing at the handling of "false" and the catch-all underscore. Change their implementation as suits your business case.
When building complex decoders, it is often best to break up decoder definitions into the smallest chunk possible like in the above.
Lastly, we can now redefine your userProperties decoder to use the stringBoolDecoder in the appropriate place:
userProperties : Json.Decode.Decoder Props
userProperties =
Json.Decode.object2 Props
("name" := Json.Decode.string)
("value" := stringBoolDecoder)