Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group by with tuples in F#

Tags:

f#

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#.

like image 503
Peter Avatar asked Nov 27 '09 12:11

Peter


2 Answers

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
like image 22
Joshua Avatar answered Oct 07 '22 07:10

Joshua


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)
like image 70
Johan Kullbom Avatar answered Oct 07 '22 05:10

Johan Kullbom