F# — GroupBy и применение функции к каждому свойству внутри второго элемента кортежа

У меня есть список классов F#, для которых я использую свойства для доступа к данным (я использую библиотеку, разработанную на C#). Я хотел бы сгруппировать по одному свойству, а затем применить отдельную функцию к каждому свойству во втором элементе результирующего кортежа.

Пример:

let grouped = list  |> Seq.groupBy (fun x -> x.Year) //group by the year property. Results in Seq<int * seq<myClass>>
                    |> Seq.map (fun (a, b) -> (a, //How to map generic functions to each remaining property in the second tuple?  

Надеюсь, это будет иметь смысл для кого-то. Мой второй элемент кортежа — это последовательность, полученная из groupBy. К каждому оставшемуся свойству в MyClass должна применяться другая функция. В прошлом, чтобы суммировать свойство, я только что сделал что-то вроде:

|> Seq.map (fun (a, b) -> (a, b |> Seq.SumBy (fun x -> x.myProperty)))

Я хотел бы сделать что-то подобное, используя Seq.map для нескольких свойств.

Большое спасибо за любую помощь, Ричард


person Richard Todd    schedule 11.03.2013    source источник


Ответы (1)


Вам нужно как-то указать свойства, с которыми вы хотите работать — самый простой способ — создать список функций, которые считывают свойства. Предполагая, что ваш тип MyType, вы можете написать что-то вроде этого:

let properties = [ (fun (x:MyType) -> x.MyProperty) ]

После того, как вы создадите группы, вы можете перебрать все свойства в properties (используя List.map или понимание списка F#) и вычислить values |> Seq.sumBy prop, где values — это группа, а prop — текущее свойство:

let grouped = 
  list  
  |> Seq.groupBy (fun x -> x.Year) 
  |> Seq.map (fun (key, values) -> 
       (key, [for prop in properties -> values |> Seq.sumBy prop ])

Если вам нужно использовать другие функции агрегирования, кроме Seq.sumBy, вы можете создать список операций агрегирования, которые вам нужно выполнить (вместо списка свойств).

let properties = [ "MyPropSum", Seq.sumBy (fun (x:MyType) -> x.MyProperty);
                   "MyProp2Avg", Seq.averageBy (fun (x:MyType) -> x.MyProperty2) ]

Чтобы упростить дальнейшую обработку, я бы, наверное, построил словарь с результатами — это легко сделать, передав список с парами имя-значение в функцию dict:

let grouped = 
  list  
  |> Seq.groupBy (fun x -> x.Year) 
  |> Seq.map (fun (key, values) -> 
       (key, dict [for name, aggregate in properties -> name, aggregate values ])
person Tomas Petricek    schedule 11.03.2013