F# Feliz.Bulma dropdown/combobox - f#

My question is very simple - how to create combobox (dropdown) in SAFE project (using Feliz.Bulma) and populate it programmatically.
In the docs there is no such component. This is the only example (Fulma) I found and can not incorporate it in my project.

For a simple dropdown you can use Bulma.select in combination with Html.option:
let dropDownValues = [
0, "Zero"
1, "One"
2, "Two" ]
let dropDown =
Bulma.select [
prop.onChange (fun (v: int) -> dispatch ...)
prop.children [
for (value, text) in dropDownValues do
Html.option [
prop.value value
prop.text text ] ] ]

I end up using Fulma code:
let createManufacturerDropdown (dispatch: Msg2 -> unit) =
let cases = FSharpType.GetUnionCases typeof<Manufacturer>
Dropdown.dropdown [ Dropdown.IsHoverable
] [
Dropdown.trigger [] [
Button.button [] [
span [] [ str "Select Maker" ]
Icon.icon [ Icon.Size IsSmall ] [
Fa.i [ Fa.Solid.AngleDown ] []
]
]
]
Dropdown.menu [ ] [
Dropdown.content [] [
for m in cases ->
Dropdown.Item.a [ Dropdown.Item.Props[ OnClick (fun _ -> SelectedManufacturerName m.Name |> BikeScreenMsg |> dispatch) ] ]
[
str m.Name
]
]
]
]

Related

Testing if a projection is uniform over an F# sequence

I want to check if a projection over a sequence has a uniform value in F#.
Here's what I have:
module Seq =
let isUniformBy (f) (xs : seq<_>) =
let l =
xs
|> Seq.map f
|> Seq.distinct
|> Seq.truncate 2
|> Seq.length
l < 2
let isUniform xs = isUniformBy id xs
printfn "%b" <| Seq.isUniformBy id [ 1; 2; 3 ] // false
printfn "%b" <| Seq.isUniformBy id [ 1; 1; 1 ] // true
printfn "%b" <| Seq.isUniformBy id [ ] // true
printfn "%b" <| Seq.isUniformBy id [ 1; 1 ] // true
printfn "%b" <| Seq.isUniformBy id [ 1; 1; 2 ] // false
printfn "%b" <| Seq.isUniformBy id [ 1; 2 ] // false
printfn "%b" <| Seq.isUniformBy (fun x -> x % 2) [ 2; 4; 6; 8 ] // true
I was wondering if there was a built-in function for this already?
And if not, what is the best way to implement this?
We can reduce the problem to comparing adjacent elements - because for uniformity, we can't have any element that's not the same as one preceding it.
This means we only need to check if there's one such pair - we only need to enumerate the sequence until we find the pair.
let isUniform xs =
xs
|> Seq.pairwise
|> Seq.exists (fun (a, b) -> a <> b)
|> not
let isUniformBy (f) (lst : seq<_>) =
lst |> Seq.map f |> isUniform
The base method is isUniform, so we can pass a projected sequence to it from isUniformBy, avoiding a pass through id. Additionally, we only use O(1) space.
Tests
assert( Seq.isUniformBy id [ 1; 2; 3 ] = false)
assert( Seq.isUniformBy id [ 1; 1; 1 ] = true)
assert( Seq.isUniformBy id [ ] = true)
assert( Seq.isUniformBy id [ 1; 1 ] = true)
assert( Seq.isUniformBy id [ 1; 1; 2 ] = false)
assert( Seq.isUniformBy id [ 1; 2 ] = false)
assert( Seq.isUniformBy (fun x -> x % 2) [ 2; 4; 6; 8 ] = true)
You can combine the first two calls using Seq.distinctBy (link), and then simplify slightly more via Seq.tryExactlyOne (link), although it will then report false for an empty sequence:
let isUniformBy f xs =
xs
|> Seq.distinctBy f
|> Seq.tryExactlyOne
|> Option.isSome
If this is not about the best (that is, most readable, and efficient), but the shortest way, I'd follow OP's approach and optimize it a bit:
module Seq =
let isUniformBy f x = Seq.groupBy f x |> Seq.length < 2
// val isUniformBy : f:('a -> 'b) -> x:seq<'a> -> bool when 'b : equality
[ id, [ 1; 2; 3 ] // false
id, [ 1; 1; 1 ] // true
id, [ ] // true
id, [ 1; 1 ] // true
id, [ 1; 1; 2 ] // false
id, [ 1; 2 ] // false
(fun x -> x % 2), [ 2; 4; 6; 8 ]] // true
|> Seq.iter ((<||) Seq.isUniformBy >> printfn "%b")

I have two list (list a and list b) , with different lengths, how do i get a new list with all the unique elements of list a?

I have this issue where i need a new list with, all the unique elements from list a, but the issue is that the list have different lengths.
[ 1; 2; 4; 5 ] |> List.except [ 2; 3; 5 ] // [ 1; 4 ]
That is, the following should do what you want
let c = a |> List.except b
Update
As #rcoy pointed out, List.except returns distinct elements only, i.e.
[ 1; 1 ] |> List.except [] = [ 1 ] // true
To keep duplicates, a straightforward way is
let b = [ 2 ]
let toRemove x = not(List.contains x b)
[ 1; 1; 2 ] |> List.filter toRemove // [ 1; 1 ]
Depending on the size of the b list and equivalency function (structural vs. referential), i.e. the cost of traversing and comparing, changing the datastructures might be beneficial. E.g.
open System.Collections.Generic
let b = [ 2 ]
let toRemove = HashSet(b)
[ 1; 1; 2 ] |> List.filter (fun x -> not(toRemove.Contains(x))) // [ 1; 1 ]
which is similar to what List.except does internally.

How capture a dynamic sub-path like "/COMPANY/rest..." with Suave and nest the routes?

I have several routes that must be grouped by "company", plus some that are valid for all:
/login
/logout
/demo1/customers
/demo1/products
So I try with suave:
let doReqScan action =
warbler (fun r ->
LOG.Debug("ROUTE: {route}", r.request.url)
match r.request |> getCompany with
| Some(x) ->
let isAuth, validDb = isValidDb(x)
if validDb then
if isAuth then
action(r.request) |> _DoSet
else
jSonNoAuth
else
never
| _ -> never
)
let doReq(route:string, action) =
path route >=> doReqScan action
let appCompany company =
choose [
path "/ws" >=> handShake ws
GET >=> choose
[
doReq("/customers", (fun r -> queryCustomers(r)))
pathScan "/customers/%d" (fun the_id -> doReqScan (fun r -> queryCustomer(r, the_id)))
]
]
let app =
choose
[ allow_cors
pathScan "/%s" (fun company ->
choose
[
appCompany company
GET >=> choose
[
_doReq( "/config", (fun r -> config(r)) )
]
POST >=> choose
[
_doReq( "/login", (fun r -> login(r)))
_doReq( "/logout", (fun r -> logout(r)))
]
]
)
GET >=> Files.browseHome
jSonNotFound
]
However the pathScan capture all the url "/demo1/customers" instead of only "/demo1".
If I understand correctly:
All common urls are in the form: /something
All company urls are in the form: /companyName/something
Then you can try this:
let appCompany company =
let com x = "/" + company + x
choose [
path (com "/ws") >=> handShake ws
GET >=> choose [
doReq(com "/customers", (fun r -> queryCustomers(r)))
pathScan "/%s/customers/%d" (fun _ the_id -> doReqScan (fun r -> queryCustomer(r, the_id)))
]
]
let app =
choose [
allow_cors
GET >=> choose [
_doReq( "/config", (fun r -> config(r)) )
]
POST >=> choose [
_doReq( "/login", (fun r -> login(r)))
_doReq( "/logout", (fun r -> logout(r)))
]
pathScan "/%s/%s" (fun company _ -> appCompany company)
GET >=> Files.browseHome
jSonNotFound
]
Looking at what is proposed in Giraffe documentation (similar to Suave in spirit). They use a pathScan with all parts at once named subroutef
So it would be something like :
pathScan "/%s/customers" queryCustomers
pathScan "/%s/products" queryProducts
...

NetLogo: break out of nested foreach loop

I'm trying to break out of a nested foreach loop using 2 lists of sorted turtles.
But instead of just leaving the inner loop, netlogo breaks out of the whole procedure.
I have a code like the one below (this one is made up, but has exactly the same structure):
to do-something
let xturtles sort-by [ [a b] -> [birthday] of a > [birthday] of b ] turtles
;; construct an ordered set
foreach xturtles [ the-xturtle ->
ask the-xturtle [
let xage birthday
let yturtles sort-by [ [a b] -> [birthday] of a > [birthday] of b ] turtles with [birthday < xage]
;; construct a second ordered set
foreach yturtles [ the-yturtle ->
let breakout-condition? false
ask the-yturtle [
if (hidden? ) [
set breakout-condition? true
]
]
if breakout-condition? [ stop ]
]
]
]
end
However, when the stop condition is reached, netlogo breaks out of the whole procedure, instead of continuing with the outer loop (the xturtles loop)?
Is that the expected behavior? If so, what is a good practice in this case?
Thanks!
Felix
It looks even nesting the stop within an extra ask procedure in the same procedure doesn't help. However, if you need a quick fix I think you can replace the second foreach loop with a standalone procedure that contains the stop as a workaround. For example, this procedure follows a similar format to yours and the same problem comes up- as soon as stop is called the broader foreach is exited.
to nest-foreach-example
ca
crt 1
let xs [ 1 2 3 4 ]
let ys [ 1 2 3 4 ]
foreach xs [
x ->
foreach ys [
y ->
ask turtles [
if y > x [
stop
]
]
print word x y
]
]
end
This prints out 11.
However, if you make a custom procedure to take the place of your "y" foreach loop, it works (with or without the ask turtles):
to nest-foreach-example-turtles
ca
crt 1
let xs [ 1 2 3 4 ]
let ys [ 1 2 3 4 ]
foreach xs [
x ->
for-y x ys
]
end
to for-y [ x_ ys ]
foreach ys [
y ->
ask turtles [
if y > x_ [
stop
]
]
print word x_ y
]
end
Outputs:
11
21
22
31
32
33
41
42
43
44

Netlogo: Using foreach to iterate over two lists filled with variables

This may be a beginner question, but after using the NetLogo Progamming Guide I'm unable to find a solution...
I am trying to iterate over a pair of lists and conditionally update the values based on a test condition.
This thread in a Netlogo Forum which gave me the hint to use the LIST primitives reporter but I still can't get the expected output.
Here is a simplified example which describes my problem.
Please note, that listA and listB are both filled with variables.
to test
let a1 1
let a2 5
let listA (list a1 a2)
let b1 6
let b2 3
let listB (list b1 b2)
(foreach (list listA) (list listb) [
[a b] -> ifelse a < b [set a "a"][set b "b"]])
show lista
show listb
end
;expected Output
;observer: [a 5]
;observer: [6 b]
Could someone give me a hint? What am I doing wrong?
Lists in NetLogo are immutable- you can't change the values quite like this approach. map might be more suitable for this:
to test2
let a1 1
let a2 5
let listA (list a1 a2)
let b1 6
let b2 3
let listB (list b1 b2)
show ( map [ [ a b ] ->
ifelse-value ( a < b ) [ "a" ] [ a ] ]
listA listB ;; pass the lists [ 1 5 ] and [ 6 3 ]
)
show ( map [ [ a b ] ->
ifelse-value ( a > b ) [ "b" ] [ b ] ]
listA listB ;; pass the lists [ 1 5 ] and [ 6 3 ]
)
end
Note that I think your expected output for lista should be ["a" 5] not ["a" 0]- is that correct?
If you'd like to do this with foreach to modify the original lists, I would create an index ( 0 to the length of the list ) to pass to replace-item:
to test3
let a1 1
let a2 5
let listA (list a1 a2)
let b1 6
let b2 3
let listB (list b1 b2)
let indexer ( range 0 length listA )
foreach indexer [ ind ->
let current_a item ind listA
let current_b item ind listB
ifelse current_a < current_b [
set listA replace-item ind listA "a"
] [
set listB replace-item ind listB "b"
]
]
print listA
print listB
end

Resources