Reputation: 24941
I'm starting to learn F#. I'm finding quite dificult to change my mind from OO programing. I would like to know how would a F# developer write the following:
"Traditional" C#
public List<List<string>> Parse(string csvData){
var matrix = new List<List<string>>();
foreach(var line in csvData.Split(Environment.NewLine.ToArray(), StringSplitOptions.None)){
var currentLine = new List<string>();
foreach(var cell in line.Split(','){
currentLine.Add(cell);
}
matrix.Add(currentLine);
}
return matrix;
}
"Functional" C#
public List<List<string>> Parse(string csvData){
return csvData.Split(Environment.NewLine.ToArray(), StringSplitOptions.None).Select(x => x.Split(',').ToList()).ToList();
}
The Question is: Would the code below considered right?
F#
let Parse(csvData:string) =
csvData.Split(Environment.NewLine.ToArray(), StringSplitOptions.None).ToList()
|> Seq.map(fun x -> x.Split(',').ToList())
Upvotes: 1
Views: 650
Reputation: 243041
Your translation looks good to me. The use of extension method in C# (such as foo.Select(...)
) is roughly equivalent to the use of pipeline and F# function from List
, Seq
or Array
modules, depending on which collection type you're using (e.g. foo |> Seq.map (...)
).
It is perfectly fine to use LINQ extension methods from F# and mix them with F# constructs, but I would only do that when there is no corresponding F# function, so I would probably avoid ToArray()
and ToList()
in the sample and write:
open System
let Parse(csvData:string) =
// You can pass the result of `Split` (an array) directly to `Seq.map`
csvData.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.None)
// If you do not want to get sequence of arrays (but a sequence of F# lists)
// you can convert the result using `Seq.toList`, but I think working with
// arrays will be actually easier when processing CSV data
|> Seq.map(fun x -> x.Split(',') |> Seq.toList)
Upvotes: 5
Reputation: 47904
There are subtle differences between the various solutions on this page. For example, your C# solutions and pad's solution are eager, but your F# version and Tomas' are lazy. Yours and pad's split on Environment.NewLine
, but Tomas' splits on any char in NewLine
(I'm guessing you want the latter since you called the overload that accepts StringSplitOptions
). Maybe these matter; maybe not.
Anyway, here's a different take on pad's solution.
let split (delims: char[]) (str: string) =
str.Split(delims, StringSplitOptions.RemoveEmptyEntries)
let parse csvData =
let nl = Environment.NewLine.ToCharArray()
[ for x in split nl csvData -> split [|','|] x ]
Upvotes: 1
Reputation: 41290
There are a few things not very F#-ish in your solution:
List
is named ResizeArray in F# and they don't have good support as built-in collections including Array, List, Seq.Changing the return type to string [] []
, you could make more succinct code with a helper function:
let split (delim: string) (str: string) =
str.Split([|delim|], StringSplitOptions.RemoveEmptyEntries)
let parse (csvData: string) =
csvData
|> split Environment.NewLine
|> Array.map (split ",")
Upvotes: 3