Самый быстрый способ объединить списки, которые имеют общее поле?
Я изучаю 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