Самый быстрый способ объединить списки, которые имеют общее поле?

Я изучаю F#, и я делаю и сервис сравнения шансов (аля 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>;    }

Итак, у меня есть несколько таких "событий" из нескольких источников. И мне нужен действительно быстрый способ объединения событий с одним и тем же Именем и Часом, и как только это будет сделано, объедините цены разных вариантов, имеющих одинаковое Имя.

Я думал о том, чтобы получить первый список, а затем выполнить поиск по одному в других списках, и, когда указанное поле совпадает, вернуть новый список, содержащий оба слитых списка.

Я хотел бы знать, есть ли более быстрый способ сделать это, поскольку производительность будет важна. Я уже видел это Объединение нескольких списков данных вместе по общему идентификатору в F#... И хотя это было полезно, я прошу лучшее решение с точки зрения производительности. Может быть, использовать любую другую структуру, которая не является списком, или каким-либо другим способом объединить их... поэтому любой совет будет принят с благодарностью.

Спасибо!

2 ответа

Решение

Как отметил в комментарии Даниэль, ключевой вопрос заключается в том, насколько лучше нужно сравнивать производительность с решением на основе стандарта. Seq.groupBy функционировать? Если у вас есть много данных для обработки, то для этой цели может быть проще использовать какую-либо базу данных.

Если вам нужно только что-то в 1,7 раза быстрее (или, возможно, больше, в зависимости от количества ядер:-)), тогда вы можете попробовать заменить Seq.groupBy с параллельной версией, основанной на Parallel LINQ, которая доступна в F# PowerPack. С помощью 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. В прошлый раз, когда я проверял, производительность была лучше, когда функции были помечены как inline, что не так в текущем исходном коде, так что вы можете проверить это тоже.

Я не проверял это, но я думаю, что это будет работать.

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
Другие вопросы по тегам