10

How can i can i create a csv file in f sharp and write the following record type in it?

    type test = { G:array<double>; P:array<double>; GG:array<double>; PP:array<double> } 

    let table = [for x in 0..(Un0.Length - 1) -> 
        let b = Un0.[x] in 
        if b=0.0 then {G=0.0; P=0.0; GG=0.0; PP=0.0}
        else {G=G_0.[x]/b; P=P0.[x]/b; GG=G0.[x]/b; PP=PP0.[x]/b}]
HaagenDaz
  • 411
  • 5
  • 15

3 Answers3

22

The CSV type provider from FSharp.Data is primarily known and used for reading CSVs (as the name suggests), but it's also quite capable of writing CSVs as well.

All you need to do is to define the type, either by providing a sample .CSV file

let titanic2 = CsvProvider<"../data/Titanic.csv", Schema="Fare=float,PClass->Passenger Class">.GetSample()

or by directly defining the schema

type MyCsvType = CsvProvider<Schema = "A (int), B (string), C (date option)", HasHeaders=false>

then you can create a record object and populate it (in a type-safe way!)

// you can build the rows themselves
let myCsv = new MyCsvType( [ MyCsvType.Row(1, "a", None)
                             MyCsvType.Row(2, "B", Some DateTime.Now) ])

// or, for your scenario, you probably want to define a conversion function
// from your record type to the CSV provider's type
let buildRowFromObject obj = MyCsvType.Row(obj.A, obj.B, obj.C)

let buildTableFromObjects = (Seq.map buildRowFromObject) >> Seq.toList >> MyCsvType

let myCsv = someSequenceOfObjects |> buildTableFromObjects

and finally, just call

myCsv.SaveToString()

to get the output in CSV format.

piaste
  • 2,038
  • 11
  • 20
  • 3
    Is there a way to infer the csv format from an existing type (sort of automatic mapping/schema definition)? – Răzvan Flavius Panda Feb 27 '16 at 21:17
  • where would this csv be saved? – SantiClaus Jun 30 '18 at 14:52
  • > where would this csv be saved? The `.SaveToString()` method returns a string, and saving that to a wherever is up to the caller. To save to a disk locally the `.Save()` function is available, which requires either a string with the path, or a TextWriter or a Stream argument where the contents will be written. See at: https://github.com/fsharp/FSharp.Data/blob/545c0eb89547e7ad29657bd18ec7a4df3b78c292/src/Csv/CsvRuntime.fs#L315 Note that internally `Save` and `SaveAsString` create a `StringWriter`/`StreamWriter` with default settings, thus it uses platform default newlines. – kódfodrász Oct 02 '20 at 19:27
3

To record in .csv is not necessary to use F# Data.

I changed the definition of test and added some values so that you can compile:

type test = { G:double; P:double; GG:double; PP:double }
            override this.ToString() = 
                sprintf "%f;%f;%f;%f\n" this.G this.P this.GG this.PP

let G_0  =  [|(0.0)..(10.0)|]
let Un0  =  [|(1.0)..(11.0)|]
let P0   =  [|(2.0)..(12.0)|]
let G0   =  [|(3.0)..(13.0)|]
let PP0  =  [|(4.0)..(14.0)|]

let table = [for x in 0..(Un0.Length - 1) -> 
                let b = Un0.[x] 
                if b=0.0 then {G=0.0; P=0.0; GG=0.0; PP=0.0}
                else {G=G_0.[x]/b; P=P0.[x]/b; GG=G0.[x]/b; PP=PP0.[x]/b}]

let wr = new System.IO.StreamWriter("Csv.csv")
table |> List.map(string) |> String.concat("") |> wr.Write
wr.Close()

Result:

enter image description here

FoggyFinder
  • 2,230
  • 2
  • 20
  • 34
  • where is this file "Csv.csv" saved? – HaagenDaz Oct 12 '15 at 11:47
  • 1
    @HaagenDaz, Near with .exe file. But you can specify any path: System.IO.StreamWriter(PathToFile) – FoggyFinder Oct 12 '15 at 12:04
  • can we use this on arrays rather than list? – HaagenDaz Oct 12 '15 at 12:09
  • 1
    @HaagenDaz, For random access by index, arrays will work much faster than lists. Therefore it is better to use arrays. – FoggyFinder Oct 12 '15 at 12:23
  • in your answer above if i need to create a record type table from the dummy arrays you created `G_0 Un0 P0 G0 PP0` would it be simply table={G=G_0;P=Un0...}? and also how can i clear the data in existing csv file before writing new data – HaagenDaz Oct 12 '15 at 12:40
  • @HaagenDaz, 1. If I understand the question, then, yes 2. By default, If the file already exists, it is overwritten. I see that you have a few simple questions, in my view, them are much easier to solve in the chat: http://chat.stackoverflow.com/rooms/51909/f – FoggyFinder Oct 12 '15 at 13:26
  • 1
    @FoggyFinder For general CSV writing it would preferable to use a library such as FSharp.Data as it will take care of escaping for you. For this specific data there is no problem though if we ignore localization. – Răzvan Flavius Panda Feb 27 '16 at 16:29
  • @Răzvan Panda, Thanks, could you tell more about this? – FoggyFinder Feb 27 '16 at 17:35
  • @FoggyFinder See this answer http://stackoverflow.com/a/1684717/750216 One of the comments has a link to the CSV specification. Some countries use , instead of . so printing floats containing a , will break the CSV format. – Răzvan Flavius Panda Feb 27 '16 at 19:41
  • @Răzvan Panda, thank you. You are right, so I used ';'. Need to see how recording is implemented in special libraries. – FoggyFinder Feb 27 '16 at 21:36
  • 2
    This is not an RFC 4180 compliant CSV. It does not handle text and special character escaping for example. Also even the separator character is not a coma. Decimal points are locale dependendt. It may work with Excel on your machine (which has a notoriously bad CSV implementation), but will not result in portable CSV output. – kódfodrász Sep 30 '20 at 19:01
  • @kódfodrász `This is not an RFC 4180 compliant CSV. It does not handle text and special character escaping for example` That's for sure. I personally would use library like `CsvHelper` but in trivial cases straightforward approach isn't that bad. – FoggyFinder Oct 01 '20 at 10:28
  • @kódfodrász `Also even the separator character is not a coma. Decimal points are locale dependendt.` That's exactly the reason why I used `;` as a separator. – FoggyFinder Oct 01 '20 at 10:30
  • In the stackoverflow post it cannot be seen what your CurrentCulture is. I had to deal with too much garbled data created by "trivial cases" that turned otut to be non-trivial, to never ever suggest the printf approach. If it is to be read by excel, it doesn't amtter, as Excel currently still cannot handle most of the corner cases solved by RFC compliant implementation anyway. (LibreOffice does handle CSV well) – kódfodrász Oct 02 '20 at 19:16
  • @kódfodrász `In the stackoverflow post it cannot be seen what your CurrentCulture is.` cause it's unrelated to the answer. But answering to your Q: CurrentCulture on my machine has a`,` as separator character for float numbers. – FoggyFinder Oct 02 '20 at 19:55
0

This is not exactly answer to your question, but it is similar and could be handy

How to EDIT csv file with f# CSV Type Provider

Solution:

#r "..\packages\FSharp.Data.2.3.3\lib\\net40\FSharp.Data.dll"
open System.IO
open FSharp.Data

type CsvTest = CsvProvider<"""C:\Users\User\Desktop\sample.csv""">
let textReader = File.OpenRead("""C:\Users\User\Desktop\sample.csv""")// need to use TextReader
let csvTest = CsvTest.Load(textReader)
let modified = csvTest.Append [CsvTest.Row(3,"bum") ]// add row to csv
textReader.Close()// closing file before edit...
modified.Save   """C:\Users\User\Desktop\sample.csv""" //save it

You may need to Reset Interactive Session (if you work with fsx)

Explanation:

Straightforward file loading like this:

let csvTest = CsvTest.Load("""C:\Users\User\Desktop\sample.csv""")

Does not work. If you try it you will get error:

System.IO.IOException: 'The process cannot access the file 'C:\Users\User\Desktop\sample.csv' because it is being used by another process.'

So I use TextReader, that can be closed..

Maybe there is easiest solution, but I didn't find any.

Community
  • 1
  • 1
Alamakanambra
  • 5,845
  • 3
  • 36
  • 43