mamu
mamu

Reputation: 12414

F# working with while loop

I have a datareader and i want to return collection of rows from it, after reading books for like a day i am not able to find out best way to do it in f#. I can do it normal C# way in F# but that is not why i am using f#

Here is what i am trying to achieve

let values =
    while reader.Read() do
        yield reader.GetString(0), reader.GetInt64(1)

Above is how i am trying to do

What could be the best way to achieve this

Upvotes: 10

Views: 4644

Answers (5)

Joel Mueller
Joel Mueller

Reputation: 28764

I might approach this problem more generically by adding an extension property to IDataReader that turns any datareader into a seq<IDataRecord>...

[<AutoOpen>]
module DataReaderEx =
    open System.Data

    type IDataReader with
        member this.toSeq =
            seq { while this.Read() do yield this :> IDataRecord }

This would allow you to use ordinary functions from the Seq module on any datareader:

reader.toSeq
|> Seq.map (fun row -> row.GetString(0), row.GetInt64(1))

Upvotes: 2

gradbot
gradbot

Reputation: 13862

F# also provides list comprehension for arrays and sequences.

let records_as_list = 
    [
        while reader.Read() 
            do yield (reader.GetString(0), reader.GetInt64(1)) 
    ]

let records_as_array = 
    [|
        while reader.Read() 
            do yield (reader.GetString(0), reader.GetInt64(1)) 
    |]

let records_as_sequence = 
    seq {
        while reader.Read() 
            do yield (reader.GetString(0), reader.GetInt64(1)) 
    }

F# has a convenient dictionary function built in called dict

let records_as_IDictionary = dict records_as_sequence

Upvotes: 17

Mau
Mau

Reputation: 14478

Well we are not talking about files here, however, in earlier .NET versions you can do (with small files):

reader.ReadToEnd().Split(System.Environment.NewLine.ToCharArray())

Where reader is a TextReader.

Upvotes: 0

Yin Zhu
Yin Zhu

Reputation: 17119

For this kind of task, I'd like to first transfer the input into a sequence of strings.

.Net 4.0 provides ReadLines, the type of its return value is seq<string>:

   open System.IO
   let readLinesSeq = File.ReadLines

In lower versions of .Net, one needs to implement this function:

   let readLines filePath = seq {
    use sr = new StreamReader (filePath)
    while not sr.EndOfStream do
     yield sr.ReadLine ()
   }

Upvotes: 3

Mau
Mau

Reputation: 14478

You can use sequence expressions to implement enumerators:

let records = seq { while reader.NextResult() do yield (reader.GetString(0), reader.GetInt64(1)) }

If you need more than two fields, you can yield maps of column index (or name) -> field values (untested code):

let dbSchema = reader.GetSchemaTable()

let makeMap (rdr : IDataReader) (schema : DataTable) =
    schema.Columns |> Seq.cast<DataColumn> |> Seq.map (fun col -> (col.ColumnName, rdr.[col.ColumnName])) |> Map.ofSeq

let records = seq { while reader.NextResult() do yield (makeMap reader dbSchema) }

Upvotes: 7

Related Questions