合并具有共同字段的列表的最快方法?

时间:2011-06-29 19:14:08

标签: f#

我正在学习F#,我正在做和赔率比较服务(ala www.bestbetting.com)将理论付诸实践。 到目前为止,我有以下数据结构:

type price = {    Bookie : string;    Odds : float32;    }

type selection = {
    Prices : list<price>;
    Name : string;
    }

type event = {    Name : string;    Hour : DateTime;    Sport : string;    Selections : list<selection>;    }

所以,我有几个来自几个来源的“事件”。而且我需要一种非常快速的方法来合并具有相同名称和小时的事件,并且一旦完成,就合并具有相同名称的不同选择的价格。

我考虑过获取第一个列表,然后在其他列表上进行逐个搜索,当指定的字段匹配时,返回一个包含两个列表合并的新列表。

我想知道是否有更快的方法,因为性能很重要。我已经看过Merge multiple lists of data together by common ID in F#了......虽然这很有帮助,但我要求的是性能最佳的解决方案。也许使用任何其他结构,它不是一个列表或其他合并方式...所以任何建议将不胜感激。

谢谢!

2 个答案:

答案 0 :(得分:4)

丹尼尔在评论中提到,关键问题是,与基于标准Seq.groupBy功能的解决方案相比,性能需要更好吗?如果要处理大量数据,那么为此目的使用某些数据库实际上可能更容易。

如果你只需要1.7倍的速度(或者可能更多,取决于内核的数量:-)),那么你可以尝试用基于F#中提供的Parallel LINQ的并行版本替换Seq.groupBy动力单元。使用PSeq.groupBy(以及其他PSeq函数),您可以编写如下内容:

#r "FSharp.PowerPack.Parallel.Seq.dll"
open Microsoft.FSharp.Collections

// Takes a collection of events and merges prices of events with the same name/hour
let mergeEvents (events:seq<event>) = 
  events 
  |> PSeq.groupBy (fun evt -> evt.Name, evt.Hour)
  |> PSeq.map (fun ((name, hour), events) ->
      // Merge prices of all events in the group with the same Selections.Name
      let selections = 
        events 
        |> PSeq.collect (fun evt -> evt.Selections)
        |> PSeq.groupBy (fun sel -> sel.Name)
        |> PSeq.map (fun (name, sels) ->
            { Name = name
              Prices = sels |> Seq.collect (fun s -> s.Prices) |> List.ofSeq } )
        |> PSeq.toList
      // Build new Event as the result - since we're grouping just using 
      // name & hour, I'm using the first available 'Sport' value 
      // (which may not make sense)
      { Name = name
        Hour = hour
        Sport = (Seq.head events).Sport
        Selections = selections })   
  |> PSeq.toList

我没有测试这个版本的性能,但我相信它应该更快。您也不需要引用整个程序集 - 您只需从PowerPack source code复制少数相关函数的源代码即可。上次我检查时,当函数被标记为inline时,性能会更好,这在当前源代码中并非如此,所以您可能也想检查它。

答案 1 :(得分:1)

我没有测试过,但我认为这样可行。

let events = List.init 10 (fun _ -> Unchecked.defaultof<event>) //TODO: initialize to something meaningful

for ((name, hour), evts) in (events |> Seq.groupBy (fun e -> e.Name, e.Hour)) do
  printfn "Name: %s, Hour: %A" name hour
  let prices = 
    seq {
      for e in evts do
        for s in e.Selections do
          for p in s.Prices do
            yield s.Name, p 
    }
    |> Seq.groupBy fst

  for (selectionName, p) in prices do
    printfn "  Selection Name: %s" selectionName
    for (_, price) in p do
      printfn "    %A" price