module List =
///
/// fold by key combinator
///
/// Key projection function.
/// Folding function.
/// Initial state function.
/// Input list.
let foldBy (proj : 'T -> 'Key) (folder : 'State -> 'T -> 'State) (init : unit -> 'State) (ts : 'T list) =
let dict = new System.Collections.Generic.Dictionary<'Key, 'State ref>()
let rec aux = function
| [] -> ()
| t :: ts ->
let k = proj t
let ok, cached = dict.TryGetValue k
let state =
if ok then cached
else
let state = ref <| init ()
dict.[k] <- state
state
state := folder state.Value t
aux ts
aux ts
dict |> Seq.map (fun kv -> kv.Key, kv.Value.Value) |> Seq.toList
// example: implementing other known combinators in terms of foldBy
let groupBy (proj : 'T -> 'Key) (ts : 'T list) =
foldBy proj (fun ts t -> t :: ts) (fun () -> []) ts
let countBy (proj : 'T -> 'Key) (ts : 'T list) =
foldBy proj (fun n _ -> n + 1) (fun _ -> 0) ts