5 people like it.
Like the snippet!
CSV writer
A simple CSV writer implementation as two type extensions for the Seq module. Use it with Records, Classes and Tuples. Have a look at the modified CSV reader sample from Don Symes Expert F# too http://fssnip.net/3T in order to advance this snippet using the ColumnAttribute
This version adds quote enclosure support and support for seq datatype.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
|
module Csv
open System.IO
open Microsoft.FSharp.Reflection
type Array =
static member join delimiter xs =
xs
|> Array.map (fun x -> x.ToString())
|> String.concat delimiter
type Seq =
static member write (path:string) (data:seq<'a>): 'result =
use writer = new StreamWriter(path)
data
|> Seq.iter writer.WriteLine
static member csv (separator:string) (headerMapping:string -> string) ( data:seq<'a>) =
seq {
let dataType = typeof<'a>
let header =
match dataType with
| ty when FSharpType.IsRecord ty ->
FSharpType.GetRecordFields dataType
|> Array.map (fun info -> headerMapping info.Name)
| ty when FSharpType.IsTuple ty ->
FSharpType.GetTupleElements dataType
|> Array.mapi (fun idx info -> headerMapping(string idx) )
| _ -> dataType.GetProperties()
|> Array.map (fun info -> headerMapping info.Name)
yield header |> Array.join separator
let lines =
match dataType with
| ty when FSharpType.IsRecord ty ->
data |> Seq.map FSharpValue.GetRecordFields
| ty when FSharpType.IsTuple ty ->
data |> Seq.map FSharpValue.GetTupleFields
| _ ->
let props = dataType.GetProperties()
data |> Seq.map ( fun line ->
props |> Array.map ( fun prop ->
prop.GetValue(line, null) ))
yield! lines |> Seq.map (Array.join separator)
}
//Example
type Test(colA:string, colB:int) =
member x.ColA = colA
member x.ColB = colB
let testData = seq { for i in 1..10 -> new Test("col"+string(i), i) }
// using all public class properties for serialization
testData
|> Seq.csv "\t" (fun propertyName -> propertyName)
|> Seq.write "test_with_class_properties.csv"
// using a tuple projection
testData
|> Seq.distinctBy (fun testInstance -> testInstance.ColA)
|> Seq.map (fun probe -> (probe.ColB) )
|> Seq.csv "\t" (fun columnName ->
match columnName with
| "0" -> "ColB"
| _ -> columnName)
|> Seq.write "test_with_tuple_projection.csv"
|
module Csv
namespace System
namespace System.IO
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Reflection
Multiple items
module Array
from Microsoft.FSharp.Collections
--------------------
type Array =
static member join : delimiter:string -> xs:'a [] -> string
Full name: Csv.Array
static member Array.join : delimiter:string -> xs:'a [] -> string
Full name: Csv.Array.join
val delimiter : string
val xs : 'a []
val map : mapping:('T -> 'U) -> array:'T [] -> 'U []
Full name: Microsoft.FSharp.Collections.Array.map
val x : 'a
System.Object.ToString() : string
module String
from Microsoft.FSharp.Core
val concat : sep:string -> strings:seq<string> -> string
Full name: Microsoft.FSharp.Core.String.concat
Multiple items
module Seq
from Microsoft.FSharp.Collections
--------------------
type Seq =
static member csv : separator:string -> headerMapping:(string -> string) -> data:seq<'a> -> seq<string>
static member write : path:string -> data:seq<'a> -> unit
Full name: Csv.Seq
static member Seq.write : path:string -> data:seq<'a> -> unit
Full name: Csv.Seq.write
val path : string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
val data : seq<'a>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Core.Operators.seq
--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>
Full name: Microsoft.FSharp.Collections.seq<_>
val writer : StreamWriter
Multiple items
type StreamWriter =
inherit TextWriter
new : stream:Stream -> StreamWriter + 6 overloads
member AutoFlush : bool with get, set
member BaseStream : Stream
member Close : unit -> unit
member Encoding : Encoding
member Flush : unit -> unit
member Write : value:char -> unit + 3 overloads
static val Null : StreamWriter
Full name: System.IO.StreamWriter
--------------------
StreamWriter(stream: Stream) : unit
StreamWriter(path: string) : unit
StreamWriter(stream: Stream, encoding: System.Text.Encoding) : unit
StreamWriter(path: string, append: bool) : unit
StreamWriter(stream: Stream, encoding: System.Text.Encoding, bufferSize: int) : unit
StreamWriter(path: string, append: bool, encoding: System.Text.Encoding) : unit
StreamWriter(path: string, append: bool, encoding: System.Text.Encoding, bufferSize: int) : unit
val iter : action:('T -> unit) -> source:seq<'T> -> unit
Full name: Microsoft.FSharp.Collections.Seq.iter
TextWriter.WriteLine() : unit
(+0 other overloads)
TextWriter.WriteLine(value: obj) : unit
(+0 other overloads)
TextWriter.WriteLine(value: string) : unit
(+0 other overloads)
TextWriter.WriteLine(value: decimal) : unit
(+0 other overloads)
TextWriter.WriteLine(value: float) : unit
(+0 other overloads)
TextWriter.WriteLine(value: float32) : unit
(+0 other overloads)
TextWriter.WriteLine(value: uint64) : unit
(+0 other overloads)
TextWriter.WriteLine(value: int64) : unit
(+0 other overloads)
TextWriter.WriteLine(value: uint32) : unit
(+0 other overloads)
TextWriter.WriteLine(value: int) : unit
(+0 other overloads)
static member Seq.csv : separator:string -> headerMapping:(string -> string) -> data:seq<'a> -> seq<string>
Full name: Csv.Seq.csv
val separator : string
val headerMapping : (string -> string)
val dataType : System.Type
val typeof<'T> : System.Type
Full name: Microsoft.FSharp.Core.Operators.typeof
val header : string []
val ty : System.Type
type FSharpType =
static member GetExceptionFields : exceptionType:Type * ?bindingFlags:BindingFlags -> PropertyInfo []
static member GetFunctionElements : functionType:Type -> Type * Type
static member GetRecordFields : recordType:Type * ?bindingFlags:BindingFlags -> PropertyInfo []
static member GetTupleElements : tupleType:Type -> Type []
static member GetUnionCases : unionType:Type * ?bindingFlags:BindingFlags -> UnionCaseInfo []
static member IsExceptionRepresentation : exceptionType:Type * ?bindingFlags:BindingFlags -> bool
static member IsFunction : typ:Type -> bool
static member IsModule : typ:Type -> bool
static member IsRecord : typ:Type * ?bindingFlags:BindingFlags -> bool
static member IsTuple : typ:Type -> bool
...
Full name: Microsoft.FSharp.Reflection.FSharpType
static member FSharpType.IsRecord : typ:System.Type * ?allowAccessToPrivateRepresentation:bool -> bool
static member FSharpType.IsRecord : typ:System.Type * ?bindingFlags:System.Reflection.BindingFlags -> bool
static member FSharpType.GetRecordFields : recordType:System.Type * ?allowAccessToPrivateRepresentation:bool -> System.Reflection.PropertyInfo []
static member FSharpType.GetRecordFields : recordType:System.Type * ?bindingFlags:System.Reflection.BindingFlags -> System.Reflection.PropertyInfo []
val info : System.Reflection.PropertyInfo
property System.Reflection.MemberInfo.Name: string
static member FSharpType.IsTuple : typ:System.Type -> bool
static member FSharpType.GetTupleElements : tupleType:System.Type -> System.Type []
val mapi : mapping:(int -> 'T -> 'U) -> array:'T [] -> 'U []
Full name: Microsoft.FSharp.Collections.Array.mapi
val idx : int
val info : System.Type
System.Type.GetProperties() : System.Reflection.PropertyInfo []
System.Type.GetProperties(bindingAttr: System.Reflection.BindingFlags) : System.Reflection.PropertyInfo []
static member Array.join : delimiter:string -> xs:'a [] -> string
val lines : seq<obj []>
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
Full name: Microsoft.FSharp.Collections.Seq.map
type FSharpValue =
static member GetExceptionFields : exn:obj * ?bindingFlags:BindingFlags -> obj []
static member GetRecordField : record:obj * info:PropertyInfo -> obj
static member GetRecordFields : record:obj * ?bindingFlags:BindingFlags -> obj []
static member GetTupleField : tuple:obj * index:int -> obj
static member GetTupleFields : tuple:obj -> obj []
static member GetUnionFields : value:obj * unionType:Type * ?bindingFlags:BindingFlags -> UnionCaseInfo * obj []
static member MakeFunction : functionType:Type * implementation:(obj -> obj) -> obj
static member MakeRecord : recordType:Type * values:obj [] * ?bindingFlags:BindingFlags -> obj
static member MakeTuple : tupleElements:obj [] * tupleType:Type -> obj
static member MakeUnion : unionCase:UnionCaseInfo * args:obj [] * ?bindingFlags:BindingFlags -> obj
...
Full name: Microsoft.FSharp.Reflection.FSharpValue
static member FSharpValue.GetRecordFields : record:obj * ?allowAccessToPrivateRepresentation:bool -> obj []
static member FSharpValue.GetRecordFields : record:obj * ?bindingFlags:System.Reflection.BindingFlags -> obj []
static member FSharpValue.GetTupleFields : tuple:obj -> obj []
val props : System.Reflection.PropertyInfo []
val line : 'a
val prop : System.Reflection.PropertyInfo
System.Reflection.PropertyInfo.GetValue(obj: obj, index: obj []) : obj
System.Reflection.PropertyInfo.GetValue(obj: obj, invokeAttr: System.Reflection.BindingFlags, binder: System.Reflection.Binder, index: obj [], culture: System.Globalization.CultureInfo) : obj
Multiple items
type Test =
new : colA:string * colB:int -> Test
member ColA : string
member ColB : int
Full name: Csv.Test
--------------------
new : colA:string * colB:int -> Test
val colA : string
val colB : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
val x : Test
member Test.ColA : string
Full name: Csv.Test.ColA
member Test.ColB : int
Full name: Csv.Test.ColB
val testData : seq<Test>
Full name: Csv.testData
val i : int
static member Seq.csv : separator:string -> headerMapping:(string -> string) -> data:seq<'a> -> seq<string>
val propertyName : string
static member Seq.write : path:string -> data:seq<'a> -> unit
val distinctBy : projection:('T -> 'Key) -> source:seq<'T> -> seq<'T> (requires equality)
Full name: Microsoft.FSharp.Collections.Seq.distinctBy
val testInstance : Test
property Test.ColA: string
val probe : Test
property Test.ColB: int
val columnName : string
More information