Группировать с помощью кортежей в F #

Предположим, у меня есть список таких как:

[("A",12); ("A",10); ("B",1);  ("C",2); ("C",1)]

И я хотел бы сделать что-то вроде groupby, как я могу это обработать?

В псевдокоде-SQL он должен выглядеть примерно так:

SELECT fst(tpl), sum(lst(tpl)) FROM [TupplesInList] GROUP BY fst(tpl)

дает

 [("A",22); ("B",1); ("C",3)]

Я могу сделать словарь и добавить ints, если ключ существует, но я вряд ли могу поверить, что это было бы лучшим решением на языке, как выразительным, как F #.

Ответы

Ответ 1

Одно решение:

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))

Изменить:... или без трубопроводов:

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)

Ответ 2

Чтобы развернуть ответ Йохана, я, как правило, делаю это много, и поэтому сделал следующую обобщенную функцию.

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

но также будет работать с другими seqences, такими как список строк

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