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
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"
]
I have an integer array in which I want to send every two elements from it to the constructor of another function.
Something like intArray |> Array.map (fun x, y -> new Point(x, y))
Is this possible? I'm new to F# and functional programming so I'm trying to avoid just looping through every 2 items in the array and adding the point to a list. I hope that's reasonable.
If using F# 4.0, use Gustavo's approach. For F# 3, you can do:
intArray
|> Seq.pairwise // get sequence of tuples of element (1,2); (2,3); (3,4); (4,5) etc
|> Seq.mapi (fun i xy -> i, xy) // combine the index with the tuple
|> Seq.filter (fun (i,_) -> i % 2 = 0) // Filter for only the even indices to get (1,2); (3,4)
|> Seq.map (fun xy -> Point xy) // make a point from the tuples
|> Array.ofSeq // convert back to array
You can use Array.chunkBySize:
intArray
|> Array.chunkBySize 2
|> Array.map (function
| [|x; y|] -> new Point (x, y)
| _ -> failwith "Array length is not even.")
An alternative solution to the existing answers, would be to write a custom function, that creates a list/an array of tuples using pattern matching:
let chunkify arr =
let rec chunkify acc lst =
if (List.length lst) > 1 then (* proceed if there are at least two elements *)
match lst with
(* save every constructed pair, until the input is not empty *)
| h1 :: h2 :: tail -> chunkify ([(h1, h2)] # acc) tail
| _ -> acc (* else return the list of pairs *)
else (* return the list of pairs *)
acc
chunkify List.empty (List.ofSeq arr) |> List.rev |> Array.ofSeq
The function can be then used like this:
// helper
let print = (fun (x:'a, y:'a) -> printfn "new Object(%A,%A)" x y)
// ints
[|1;2;3;4;5;6|]
|> chunkify
|> Array.iter print
// strings
[|"a";"b";"c";"d";"e"|]
|> chunkify
|> Array.map print
|> ignore
The output is:
new Object(1,2)
new Object(3,4)
new Object(5,6)
new Object("a","b")
new Object("c","d")
This solution/approach uses pattern matching with lists.
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|]
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