How do I "convert" a Dictionary into a sequence so that I can sort by key value?
let results = new Dictionary()
results.Add("George", 10)
results.Add("Peter", 5)
results.Add("Jimmy", 9)
results.Add("John", 2)
let ranking =
results
???????
|> Seq.Sort ??????
|> Seq.iter (fun x -> (... some function ...))
A System.Collections.Dictionary<K,V> is an IEnumerable<KeyValuePair<K,V>>, and the F# Active Pattern 'KeyValue' is useful for breaking up KeyValuePair objects, so:
open System.Collections.Generic
let results = new Dictionary<string,int>()
results.Add("George", 10)
results.Add("Peter", 5)
results.Add("Jimmy", 9)
results.Add("John", 2)
results
|> Seq.sortBy (fun (KeyValue(k,v)) -> k)
|> Seq.iter (fun (KeyValue(k,v)) -> printfn "%s: %d" k v)
You may also find the dict function useful. Let F# do some type inference for you:
let results = dict ["George", 10; "Peter", 5; "Jimmy", 9; "John", 2]
> val results : System.Collections.Generic.IDictionary<string,int>
Another option, which doesn't need a lambda until the end
dict ["George", 10; "Peter", 5; "Jimmy", 9; "John", 2]
|> Seq.map (|KeyValue|)
|> Seq.sortBy fst
|> Seq.iter (fun (k,v) -> ())
with help from https://gist.github.com/theburningmonk/3363893
Related
I am trying to use FSharp.Data's HTML Parser to extract a string List of links from href attributes.
I can get the links printed out to console, however, i'm struggling to get them into a list.
Working snippet of a code which prints the wanted links:
let results = HtmlDocument.Load(myUrl)
let links =
results.Descendants("td")
|> Seq.filter (fun x -> x.HasClass("pagenav"))
|> Seq.map (fun x -> x.Elements("a"))
|> Seq.iter (fun x -> x |> Seq.iter (fun y -> y.AttributeValue("href") |> printf "%A"))
How do i store those strings into variable links instead of printing them out?
Cheers,
On the very last line, you end up with a sequence of sequences - for each td.pagenav you have a bunch of <a>, each of which has a href. That's why you have to have two nested Seq.iters - first you iterate over the outer sequence, and on each iteration you iterate over the inner sequence.
To flatten a sequence of sequences, use Seq.collect. Further, to convert a sequence to a list, use Seq.toList or List.ofSeq (they're equivalent):
let a = [ [1;2;3]; [4;5;6] ]
let b = a |> Seq.collect id |> Seq.toList
> val b : int list = [1; 2; 3; 4; 5; 6]
Applying this to your code:
let links =
results.Descendants("td")
|> Seq.filter (fun x -> x.HasClass("pagenav"))
|> Seq.map (fun x -> x.Elements("a"))
|> Seq.collect (fun x -> x |> Seq.map (fun y -> y.AttributeValue("href")))
|> Seq.toList
Or you could make it a bit cleaner by applying Seq.collect at the point where you first encounter a nested sequence:
let links =
results.Descendants("td")
|> Seq.filter (fun x -> x.HasClass("pagenav"))
|> Seq.collect (fun x -> x.Elements("a"))
|> Seq.map (fun y -> y.AttributeValue("href"))
|> Seq.toList
That said, I would rather rewrite this as a list comprehension. Looks even cleaner:
let links = [ for td in results.Descendants "td" do
if td.HasClass "pagenav" then
for a in td.Elements "a" ->
a.AttributeValue "href"
]
How do I identify the max length from a Map's value set?
let numbers = [1;2;2;3;3;3;4;5;5]
let map = numbers |> Seq.groupBy id
|> Map.ofSeq
I want to do this:
map.Values |> List.max
or...
let longestSequence = Map.map (fun (k, v) -> List.max(List.ofSeq(v)));
you can get something similar to Dictionary.Values with Map.toSeq >> Seq.map snd so you can get the largest collected sequence in your map like this:
> map |> Map.toSeq |> Seq.map snd |> Seq.maxBy Seq.length;;
val it : seq<int> = seq [3; 3; 3]
of course when your list is already in a sorted stage it seems strange to take the detour over Map as
> numbers |> Seq.groupBy id |> Seq.map snd |> Seq.maxBy Seq.length;;
val it : seq<int> = seq [3; 3; 3]
will do the same ;)
also if you think about the problem here can write a List.fold (with a additional map of the result) doing this as well which will only require to traverse the (sorted) list once ... maybe you can try to do this yourself ^^
Hi I m trying to expand a seq.groupby function. On a single argument this works and was discussed here before here, key code repeated:
let group_fold key value fold acc seq =
seq |> Seq.groupBy key
|> Seq.map (fun (key, seq) -> (key, seq |> Seq.map value |> Seq.fold fold acc))
let tuples = [("A",12); ("A",10); ("B",1); ("C",2); ("C",1)]
let regular = group_fold fst snd (+) 0 tuples
let piped = tuples |> group_fold fst snd (+) 0
I would like to do the same but with multiple grouping arguments. This is what I tried:
let tuples = [("A", "B", "C", 12); ("A", "B", "C", 10); ("B","B","B",1); ("C","B","B",2); ("C","B","B", 1)]
let group_fold key1 key2 key3 value fold acc seq =
seq |> Seq.groupBy (key1 & key2 & key3)
|> Seq.map (fun (key1, key2, key3, seq) -> (key1, key2, key3, seq |> Seq.map value |> Seq.fold fold acc))
let piped = tuples |> group_fold fst snd trd fth (+) 0
This groupby multiple items does not seem to work. i know in c# I would do sth like this:
tuples.GroupBy(a => new { a.fst, a.snd, a.trd})
How can I do this in Fsharp?
Like this:
let group_fold keys value fold acc seq =
seq |> Seq.groupBy keys
|> Seq.map (fun ((key1, key2, key3), seq) ->
(key1, key2, key3, seq |> Seq.map value |> Seq.fold fold acc))
let piped = tuples |> group_fold (fun (k1, k2, k3, _) ->
k1, k2, k3) (fun (_, _, _, v) -> v) (+) 0
if I have array A, and I have another bool array isChosen with the same length of A how can I build a new array from A where isChosen is true? something like A.[isChosen]? I cannot use Array.filter directly since isChosen is not a function of A elements and there is no Array.filteri like Array.mapi.
zip should help:
let l = [|1;2;3|]
let f = [|true; false; true|]
let r = [| for (v, f) in Seq.zip l f do if f then yield v|]
// or
let r = (l, f) ||> Seq.zip |> Seq.filter snd |> Seq.map fst |> Seq.toArray
Try the zip operator
seq.zip A isChosen
|> Seq.filter snd
|> Seq.map fst
|> Array.ofSeq
This will create a sequence of tuples where one value is from A and the other is from isChosen. This will pair the values together and make it very easy to filter them out in a Seq.filter expression
It's not as elegant or 'functional' as the other answers, but every once in a while I like a gentle reminder that you can use loops and array indices in F#:
let A = [|1;2;3|]
let isChosen = [|true; false; true|]
let r = [| for i in 0..A.Length-1 do
if isChosen.[i] then
yield A.[i] |]
printfn "%A" r
:)
And here are two more ways, just to demonstrate (even) more F# library functions:
let A = [|1;2;3|]
let isChosen = [|true;false;true|]
let B = Seq.map2 (fun x b -> if b then Some x else None) A isChosen
|> Seq.choose id
|> Seq.toArray
let C = Array.foldBack2 (fun x b acc -> if b then x::acc else acc) A isChosen []
|> List.toArray
My personal favorite for understandability (and therefore maintainability): desco's answer
let r = [| for (v, f) in Seq.zip l f do if f then yield v|]
Suppose I have a list of tupples like these :
[("A",12); ("A",10); ("B",1); ("C",2); ("C",1)]
And I would like to do some kind of groupby how do I handle that?
In pseudocode-SQL it should look something like this :
SELECT fst(tpl), sum(lst(tpl)) FROM [TupplesInList] GROUP BY fst(tpl)
yielding
[("A",22); ("B",1); ("C",3)]
I could make a Dictionary and add the ints if the key exist, but I can hardly believe that would be the best solution in a language as expressive as F#.
One solution:
let tuples = [("A",12); ("A",10); ("B",1); ("C",2); ("C",1)]
tuples
|> Seq.groupBy fst
|> Seq.map (fun (key, values) -> (key, values |> Seq.sumBy snd))
Edit: ...or without piping:
let tuples = [("A",12); ("A",10); ("B",1); ("C",2); ("C",1)]
Seq.map (fun (key, group) -> key, Seq.sumBy snd group)
(Seq.groupBy fst tuples)
To expand on Johan's answer, I tend to do this sort thing alot and so have made the following generalized function.
let group_fold key value fold acc seq =
seq |> Seq.groupBy key
|> Seq.map (fun (key, seq) -> (key, seq |> Seq.map value |> Seq.fold fold acc))
Which works for your tuple case as seen below
let tuples = [("A",12); ("A",10); ("B",1); ("C",2); ("C",1)]
let regular = group_fold fst snd (+) 0 tuples
let piped = tuples |> group_fold fst snd (+) 0
but will also work with other seqences like a list of strings
let strings = ["A12"; "A10"; "B1"; "C2"; "C1"]
let regular = group_fold (fun (x : string) -> x.[0]) (fun (x : string) -> int x.[1..]) (+) 0 strings
let piped = strings |> group_fold (fun x -> x.[0]) (fun x -> int x.[1..]) (+) 0