Dymond
Dymond

Reputation: 2277

Mix string and List into a List

I have this function that writes to an csv file. the output of the csv file should be like this.

title(string) body(string) images(list) variations(list)
ipsum1        lorem1       img1         variation1
empty         empty        img2         variation2
empty         empty        img3         variation3

The function I'm trying to use is something like this.

    let col1 = ["title"; title]
    let col2 = ["body"; stringedBody]
    let col3 = ["images", images] //LIST  
    let col4 = ["allVariations",allVariations] //LIST
                   
    let cols = [col1; col2; col3; col4; ] //*col3 & 4 gives all elements must be of the same type as the first element, witch here is 'string' . This element has type 'string * string list*'

    let transpose(xs: string list list) : string list list =
        [0 .. xs.[0].Length - 1] |> List.map (fun i ->
        xs |> List.rev |> List.fold (fun acc col -> col.[i] :: acc) [] 
        )

    let stringify_rows(xs: string list list) : string list =
        xs |> List.map (fun row -> System.String.Join(";", row))

    System.IO.File.WriteAllLines("\\test.csv", cols |> transpose |> stringify_rows) 

But the problem seems to be with column 3 and 4, I have also tried to create an array of the images

let mutable img = [|images|]

but that gave me an similar error.

Thanks in advance.

Upvotes: 0

Views: 39

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243041

If you want to process a list of things in F# in a uniform way, then all the things in your list need to be of the same type. This is not the case in your example. You use a list of strings in some cases (when you have different values for each row) but just a single string in other cases (when there is the same value for all rows).

The best way to fix your logic is to come up with a uniform representation. I think the easiest option is to use a list of string option values. Then you can use Some "str" for a value and None for a missing value:

let col1 = [Some "title"; Some "lorem"; None; None]
let col2 = [Some "body"; Some "ipsum"; None; None]
let col3 = [Some "images"; Some "img1"; Some "img2"; Some "img3"]
let col4 = [Some "allVariations"; Some "var1"; Some "var2"; Some "var3"]

With this, most of your code (almost) works! The only change is that you need to make transpose compatible with string option. You can do that by changing the annotation to 'a list list:

let transpose(xs: 'a list list) : 'a list list =
    [0 .. xs.[0].Length - 1] |> List.map (fun i ->
    xs |> List.rev |> List.fold (fun acc col -> col.[i] :: acc) [] 
    )

I also changed stringify_rows to add an empty string as the default value for missing values:

let stringify_rows(xs: string option list list) : string list =
    xs |> List.map (fun row -> 
      row |> List.map (Option.defaultValue "") |> String.concat ";")

Upvotes: 1

Related Questions