Reputation: 12414
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
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
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
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
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
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