2017-02-26 41 views
5

我想出这个简单的算法(转换的元组的列表键的地图集合列表),我需要在我的F#代码:此算法已有高阶函数吗?

let MergeIntoMap<'K,'V when 'K: comparison>(from: seq<'K*'V>): Map<'K,seq<'V>>= 
    let keys = from.Select(fun (k,v) -> k) 
    let keyValuePairs = seq { 
     for key in keys do 
      let valsForKey = from.Where(fun (k,v) -> key = k).Select(fun (k,v) -> v) |> seq 
      yield key,valsForKey 
    } 
    keyValuePairs |> Map.ofSeq 

例输入:

[ ("a", 1); ("b", 2), ("a", 3) ] 

输出:

dict [ ("a", [1; 3]), ("b", [2]) ] 

我在想这一定是已经存在于BCL或F#的高阶函数集中的东西吗?如果是的话,有人可以参考我吗?因为我敢肯定,我的代码是不是很有效,因为它是...

+0

@FoggyFinder:更新 – ympostor

回答

4

看来你想要得到的东西一样,

let toGroupMap x = 
    x 
    |> Seq.groupBy fst 
    |> Seq.map 
     (fun (k,v) -> k, v |> Seq.map snd |> Seq.toArray) 
    |> Map.ofSeq 

FSI:

val toGroupMap : x:seq<'a * 'b> -> Map<'a,'b []> when 'a : comparison 
val input : (string * int) list = [("a", 1); ("b", 2); ("a", 3)] 
val output : Map<string,int []> = map [("a", [|1; 3|]); ("b", [|2|])] 

编辑

正如在评论中写着Fyodor Soikin,那里是一个扩展方法ToLookup,它可能会做你所需要的。

open System.Linq 

let output = input.ToLookup(fst, snd) 

您可以阅读here约ILookup和IDictionary的接口之间的差异

+0

所以没有高阶函数已经这样做了?为什么你的解决方案比我的更好?你是否建议你更有效率? – ympostor

+5

这个代码比你原来的算法更好看。首先,'Seq.groupBy'是O(N),而你的是O(N^2),因为它贯穿每个键的整个'from'序列。其次,Foggy Finder的代码看起来更加习惯。也就是说,作为一名经验丰富的F#编码员,我可以阅读它并立即看到它在做什么。鉴于你使用LINQ的代码更多的是类C#和更加不透明的,而且要花费我相当长的时间才能看到它的功能。更容易阅读的代码可以更好地理解程序,从而导致长远而言更少的错误。 – rmunn

+5

等一下,是不是'.ToLookup'完成那件事? –