I am using elm-spa-example model and I have a Session that contains a List Recipe as well as a Model that contains a recipeID
I'd like to build a SelectList that would select the recipe that has got the RecipeID in the list.
Ideally I would use something like:
SelectList.selectFromList : (a -> Bool) -> List a -> SelectList a
I my case I would do:
SelectList.selectFromList (\recipe -> recipe.id == recipeID) session.recipes
I did something like that:
selectFromList : (a -> Bool) -> List a -> Maybe (SelectList a)
selectFromList isSelectable list =
case list of
first :: rest ->
SelectList.fromLists [] first rest
|> SelectList.select isSelectable
|> Just
[] ->
Nothing
I also added:
prev : SelectList a -> Maybe a
prev list =
SelectList.before list
|> List.reverse
|> List.head
next : SelectList a -> Maybe a
next list =
SelectList.after list
|> List.head
I put together this quick ellie that illustrates what I think are the steps needed to achieve what you want. It's certainly not optimized or even idiomatic.
https://ellie-app.com/4TJVgSCwXa1/0
firstPartialList = takeWhile condition myList
selected = Maybe.withDefault "" (getAt (length firstPartialList) myList)
secondPartialList = drop ((length firstPartialList) + 1) myList
mySelectList = SelectList.fromLists firstPartialList selected secondPartialList
condition = (\item -> item /= otherItem)
myList = ["a", "b", "c", "d"]
otherItem = "b"
SelectList doesn't expose selectFromList are you sure the link is right?
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."
)
I am currently trying to figure out the best way of traversing a List.
What do I mean by traversing?
Example:
I have a List of Users:
userList : List User
userList =
[user, user, user, user]
and I have a currentUser, which must be a user out of the userList
So what I want to achieve is:
I want to have something like List.getNext which takes the userList and the current user and returns the next user in the list, relative to the currentUser
Here is my implementation. I think it is very complicated - so has anyone an idea how to do this in a better style?
traverseList : List a -> a -> Maybe (Maybe a)
traverseList list currentElement =
let
indexList =
List.indexedMap
(\index element ->
if element == currentElement then
index
else
-1
)
list
currentAsIndex =
let
mayBeIndex =
List.maximum indexList
in
case mayBeIndex of
Just index ->
index
Nothing ->
0
getWanted =
List.map
(\( id, element ) ->
if id == (currentAsIndex + 1) then
Just element
else
Nothing
)
(List.indexedMap (,) list)
|> List.filter
(\element ->
element /= Nothing
)
|> List.head
in
getWanted
Explanation:
My approach is to get the list, make an index list of the given list (looks like this [-1, -1, -1, 3, -1, -1])
Then I get the maximum of this list - as this gives me the position of the current user in a List.indexedMap.
Then I iterate the original as an List.indexedMap and figure out the next one (in our case No. 4) and return that element. Else I return nothing.
Then I filter this List of Nothings and just one user and extract the user from the list by using List.head.
The Result is a Maybe (Maybe user)... that is not so nice... or?
Thanks for any ideas to do something like this in a better functional way.
I really try to get better in functional programming..
Here is a pretty naive, recursive solution:
getWanted : List a -> a -> Maybe a
getWanted list currentElement =
let findNextInList l = case l of
[] -> Nothing
x :: [] -> if x == currentElement
then List.head list
else Nothing
x :: y :: rest -> if x == currentElement
then Just y
else findNextInList (y :: rest)
in
findNextInList list
The idea here is look at the first two elements of the list, and if the first is the current element, take the second. If not, try again with the tail of the list.
Corner cases must be handled (you can write at least 4 unit tests for this function):
current element not found at all
current element is the last in the list
the list might be empty
Maybe there is a more elegant solution, but recursion is a pretty common technique in functional programming, so I wanted to share this approach.
If you are willing to import list-extra you can use
import List.Extra as LE exposing ((!!))
getNext : a -> List a -> Maybe a
getNext item list =
list
|> LE.elemIndex item
|> Maybe.map ((+) 1)
|> Maybe.andThen ((!!) list)
If the found element is the last in the list it returns Nothing
Another possible solution:
getNext : List a -> a -> Maybe a
getNext list element =
list
|> List.drop 1
|> zip list
|> List.filter (\(current, next) -> current == element)
|> List.map (\(current, next) -> next)
|> List.head
zip : List a -> List b -> List (a,b)
zip = List.map2 (,)
We drop the first element of the list and then zip it with the original list to get a list of tuples. Each element of the tuple contains the current element and the next element. We then filter that list, take the 'next' element from the tuple and take the head of the final list.
I like farmio's library-based approach best, and is what I'll be using in future.
That said, for being new to elm and not looking over its libraries, my erstwhile approach has been as in the following: i.e. index the list, find index if any of searched-for item, get item at next index if any.
nextUser : List User -> User -> Maybe User
nextUser lst usr =
let
numberedUsers =
List.map2 (\idx u -> ( idx, u )) (List.range 1 (List.length lst)) lst
usrIdx =
List.filter (\( idx, u ) -> u.name == usr.name) numberedUsers
|> List.head
|> Maybe.map Tuple.first
|> Maybe.withDefault -1
in
List.filter (\( idx, u ) -> idx == usrIdx + 1) numberedUsers
|> List.head
|> Maybe.map Tuple.second
Another way with as much fiddlery would be to use folds.
Maybe instead of List just use Array, which provides index-based access.
There are some good answers to the concrete question. I'd like to suggest an alternative model, since this approach has some downsides:
You rely on checking equality of users. This limits the user type, e.g., there can't be functions in there.
Looking up a user in a list that doesn't guarantee that all users are different is fragile: If the list happens to end up as [user1, user2, user1, user3], the rotation will never reach user3.
Nothing guarantees that the current user is in the list, hence the Maybe in the return type of the other answers which is hard to deal with correctly.
Thus, let's assume your current model is
type alias Model =
{ currentUser : User
, users : [User]
}
and what you're asking for is
rotateUser : Model -> Model
rotateUser model =
let
nextUser = Maybe.withDefault
model.currentUser
(getNext model.users model.currentUser)
in
{ model | currentUser = nextUser }
getNext : List a -> a -> Maybe a
...
How about instead of implementing getNext, we change the model to make life easier:
type alias Model =
{ currentUser : User
, otherUsers : [User]
}
rotateUser : Model -> Model
rotateUser model =
case model.otherUsers of
[] -> model
(nextUser::rest) ->
{ model
| currentUser = nextUser
, otherUsers = rest ++ [model.currentUser]
}
Can I write
\(a, b) -> ...
and expect a tuple parameter to be destructured?
Yes, you can...
For example, following code works.
import Html exposing (text)
main =
("Hello", "World!")
|> \(a,b) -> a ++ " " ++ b
|> text
In order to specify additional internal company Nuget feeds, I am currently using RestorePackages like this:
let RestorePackages() =
!! "./**/packages.config"
|> Seq.iter (RestorePackage(fun p -> {p with Sources = "http://internal.example.com/nuget" :: p.Sources}))
Which works great. How can I get the script to restore certain packages with the IncludePreRelease option set?
I have tried matching on the package like this:
let RestorePackages() =
!! "./**/packages.config"
|> Seq.iter (fun item ->
match item with
| "Example" -> RestorePackageId(fun p -> {p with IncludePreRelease = true}) "Example"
| item -> RestorePackage(fun p -> {p with Sources = "http://internal.example.com/nuget" :: p.Sources}))
But that doesn't work. The default match to call RestorePackage says "This expression was expected to have type unit but here has type string -> unit".
Looks like you're missing a string at the end of your last code line. I think you should pass item variable there.
let RestorePackages() =
!! "./**/packages.config"
|> Seq.iter (fun item ->
match item with
| "Example" -> RestorePackageId(fun p -> {p with IncludePreRelease = true}) "Example"
| item -> RestorePackage(fun p -> {p with Sources = "http://internal.example.com/nuget" :: p.Sources}) item)