Search code examples
f#fold

F# folding gracefully without the first or last element?


Say you have a list of strings: [ "a"; "b"; "c" ] and you want to transform it to a single string like so: "a,b,c" notice that the last comma is missing. I find this case comes up again and again for me, think of all the programming languages that do not allow the trailing comma to be there, and you are building some kind of code generator. I usually end up with something like:

let listOfThings = ["a";"b";"c"]
let folded =
    listOfThings
    |> List.map (fun i -> i + ",")
    |> List.fold (+) ""
    |> (fun s -> s.Substring(0, s.Length - 1))

I feel like there is some fold like function already because this seems such a basic use case, and I just can't figure out what would be it's name, or by what name to search for it.


Solution

  • A fold applies your folding function recursively over all values of the list, starting with an initial state, which you don't particularly want in this case.

    It's simpler to use a reduce which uses the list's head as its starting state:

    listOfThings |> List.reduce (fun sum cur -> sum + "," + cur) // "a,b,c"
    

    A minor drawback is that since it uses the list head, calling reduce with an empty list would fail. You can mitigate that with a check for an empty list.

    Without any built-ins, as you have described, we skip the addition of the trailing comma for the last element:

    let rec join = function
    | []    -> ""
    | [x]   -> x
    | x::xs -> x + ","  + join xs
    
    ["a"; "b"; "c"] |> join // a,b,c
    

    However, the most efficient method would be to use String.Join which internally uses a StringBuilder, while reduce allocates a new string for every call:

    String.Join(",", listOfThings) // "a,b,c"