如何使用CSVProvider加载具有不同结构的csv?

时间:2018-06-12 20:21:57

标签: csv f#

任何想法?我目前的代码如下。

z = sum(d['blue'].values())

1 个答案:

答案 0 :(得分:4)

静态解析类型参数的问题是语法很麻烦。这是一个例子:

let inline snippet< ^T when ^T : (member Id : string)
                        and ^T : (member Country : string)
                        and ^T : (member Tax : float) >
                        xs =
    xs
    |> Seq.filter (fun row -> ( ^T : (member Id : string) row) <> "---")
    |> Seq.filter (fun row -> ( ^T : (member Country : string) row) <> "South America")
    |> Seq.filter (fun row -> ( ^T : (member Tax : float) row) <> 0.0)
    |> Seq.groupBy (fun row -> ( ^T : (member Country : string) row))
    |> Seq.map (fun (country, rows) -> country, (rows |> Seq.averageBy (fun row -> ( ^T : (member Tax : float) row))))
    |> List.ofSeq

(我修改了averageBy调用,因此它比问题中的调用更有意义。)

您实际上可以让编译器推断出约束而不是明确地声明它们:

let inline snippet xs =
    xs
    |> Seq.filter (fun row -> ( ^T : (member Id : string) row) <> "---")
    |> Seq.filter (fun row -> ( ^T : (member Country : string) row) <> "South America")
    |> Seq.filter (fun row -> ( ^T : (member Tax : float) row) <> 0.0)
    |> Seq.groupBy (fun row -> ( ^T : (member Country : string) row))
    |> Seq.map (fun (country, rows) -> country, (rows |> Seq.averageBy (fun row -> ( ^T : (member Tax : float) row))))
    |> List.ofSeq

作为替代方案,您可以要求调用者传递value-getter函数:

let snippet2 getId getCountry getTax xs =
    xs
    |> Seq.filter (fun row -> getId row <> "---")
    |> Seq.filter (fun row -> getCountry row <> "South America")
    |> Seq.filter (fun row -> getTax row <> 0.0)
    |> Seq.groupBy (fun row -> getCountry row)
    |> Seq.map (fun (country, rows) -> country, (rows |> Seq.averageBy (fun row -> getTax row))))
    |> List.ofSeq

这使您可以稍微简化语法:

let snippet2 getId getCountry getTax xs =
    xs
    |> Seq.filter (getId >> (<>) "---")
    |> Seq.filter (getCountry >> (<>) "South America")
    |> Seq.filter (getTax >> (<>) 0.0)
    |> Seq.groupBy getCountry
    |> Seq.map (fun (country, rows) -> country, (rows |> Seq.averageBy getTax))
    |> List.ofSeq

您可以使用部分应用声明函数的特定于类型的实例:

let snippetForCsvFile1 (xs : CsvFile1 seq) = xs |> snippet2 (fun r -> r.Id) (fun r -> r.Country) (fun r -> r.Tax)

然后你的开关看起来像这样:

let rawdata csvfile =
    match csvfile with
    | 1 -> snippetForCsvFile1 CsvFile1
    | 2 -> snippetForCsvFile2 CsvFile2
    | 3 -> snippetForCsvFile3 CsvFile3
    | 4 -> snippetForCsvFile4 CsvFile4
    | _ -> failwith "Not a File"

然后应该使用你的最后一个表达式:

let raw =
    [ 1 .. 4]
    |> List.collect rawdata

您的问题中的开关存在您在问题中未提及的问题:

let rawdata csvfile =
    let data = 
        match csvfile with
        | 1 -> CsvFile1
        | 2 -> CsvFile2
        | 3 -> CsvFile3
        | 4 -> CsvFile4
        | _ -> failwith "Not a File"

这里的问题是data的绑定没有单一类型。您可以使用接口(或基类)使用面向对象的方法来实现这一点,但对于F#而言,这不是特别惯用的。一个更惯用的方法是宣布一个有区别的联盟,但是如果你发布它的例子似乎没有必要。