Scott Nimrod
Scott Nimrod

Reputation: 11595

Converting strings in F#

I am attempting to convert a file name like the following:

ten_of_clubs.png

to

10_of_clubs.png

However, I do not like my implementation:

let getFile (card: Card Option) =
    let fileName = 
        match card with
        | Some card -> (getUnionTypeName card.Face + "_of_" + getUnionTypeName card.Suit + ".png").ToLower()
        | None -> ""

    let delimitedName = fileName.Split '_'
    let currentFace = delimitedName.[0]
    let updatedFace = 
        match currentFace with
        | "two" -> "2"
        | "three" -> "3"
        | "four" -> "4"
        | "five" -> "5"
        | "six" -> "6"
        | "seven" -> "7"
        | "eight" -> "8"
        | "nine" -> "9"
        | "ten" -> "10"
        | _ -> currentFace
    updatedFace + "_" + delimitedName.[1] + "_" + delimitedName.[2]

In F#, how can I just change the first element of a string without referencing all elements?

Example:

updatedFace + "_" + delimitedName.[1] + "_" + delimitedName.[2]

to

updatedFace + "_" + delimitedName.[1..]

Upvotes: 1

Views: 85

Answers (2)

Mark Seemann
Mark Seemann

Reputation: 233135

There's no reason to first construct a formatted string, then split it, and finally create a new formatted string.

Assuming that your intent is to return the empty string in the None case, you can do something like this:

let getFile card =
    let digitize candidate = 
        match candidate with
        | "Two" -> "2"
        | "Three" -> "3"
        | "Four" -> "4"
        | "Five" -> "5"
        | "Six" -> "6"
        | "Seven" -> "7"
        | "Eight" -> "8"
        | "Nine" -> "9"
        | "Ten" -> "10"
        | _ -> candidate
    match card with
    | Some c ->
        let face = c.Face |> string |> digitize
        let suit = c.Suit |> string
        sprintf "%s_of_%s.png" face suit
    | None -> ""

Here, I've assumed that the Face and Suit types override ToString instead of relying on a function called getUnionTypeName that I don't know of. This enables you to use the built-in function string (which simply calls ToString ()).

Ad hoc tests:

> Some { Face = Ten; Suit = Clubs } |> getFile;;
val it : string = "10_of_Clubs.png"
> Some { Face = Jack; Suit = Clubs } |> getFile;;
val it : string = "Jack_of_Clubs.png"
> None |> getFile;;
val it : string = ""

That digitize function looks like a candidate for a general-purpose function, and I wonder if the BCL doesn't already have something like that lying around in its Globalization namespace... Otherwise, I'm sure there are hundreds of NuGet packages out there that implement such functionality...


It turns out that there aren't hundreds of NuGet packages that already do this, so I created one, called Numsense. With it, you can simplify the getFile function to this:

open Ploeh.Numsense

let getFile card =
    let digitize candidate = 
        match Numeral.tryParseEnglish candidate with
        | Some i -> string i
        | None -> candidate
    match card with
    | Some c ->
        let face = c.Face |> string |> digitize
        let suit = c.Suit |> string
        sprintf "%s_of_%s.png" face suit
    | None -> ""

Upvotes: 3

Bartek Kobyłecki
Bartek Kobyłecki

Reputation: 2395

After splitting your string by _ you get an array of parts. Array is mutable Collection. You can change First by your function and then concatenate by _ again.

let parts = myString.Split [|'_'|]
parts.[0] <- toNumber parts.[0]
String.concat "_" parts

Upvotes: 2

Related Questions