Seq.map from one list to populate another list of

2019-07-15 21:18发布

问题:

This question is in follow up to an earlier question, Preserving Field names across the F#/C# boundary

Because of the current limitation encountered with F# type providers (see the earlier question), I want to map the type-provider-generated list to my own list of records, in which the record is, in part,

type inspection = {
    inspectionID : string;
    inspectorID : int;
    EstablishmentID : string;
    EstablishmentName : string;  // other members elided
}

I think the way to do this will use Seq.map, but I am not certain. (Recall I am doing a learning exercise.) So here is what I tried:

type restaurantCsv = CsvProvider<"C:\somepath\RestaurantRatings2013.csv",HasHeaders=true> 
// which generates a type, but it is an "erased" type, so member names do not propogate
//    over to C#.

type RawInspectionData(filename : string) = 
    member this.allData = restaurantCsv.Load(filename)  // works fine

    member this.allInspections = 
        this.allData.Data
            |> Seq.map(fun rcrd -> new inspection[{inspectionID = rcrd.InspectionID;}])

and, of course, the complete statement would have the other member names as part of the inspection, here elided for brevity. Someone pointed me to p 43 of F# For Scientists, which is why I thought to use this format with the curly braces. But this yields a syntax error, "Unexpected symbol '{' in expression. Expected ',', ']' or other token."

Hopefully, though, this snippet is adequate to show what I would like to do, create a Generated Type from the Erased Type. How can I accomplish this?

回答1:

Your code is going in the right direction. When using Seq.map (which is like Select in LINQ), you need to turn a single element of the original sequence into a single element of the new sequence. So the lambda function just needs to create a single instance of the record.

A record is constructed using { Field1 = value1; Field2 = value2; ... } so you need:

type RawInspectionData(filename : string) = 
  let allData = restaurantCsv.Load(filename)  // works fine
  member this.allInspections = 
    allData.Data
    |> Seq.map(fun rcrd -> {inspectionID = rcrd.InspectionID}) 

I also changed allData from a member to a local let definition (which makes it private field of the class). I suppose that your original code new inspection[{...}] tried to create a singleton array with the element - to create an array you'd write [| { Field = value; ... } |] (and the compiler would infer the type of the array for you). But in this case, no arrays are needed.