professor bigglesworth
professor bigglesworth

Reputation: 570

Convert String to Key Value Pair in F#

Given a string such as

one:1.0|two:2.0|three:3.0

how do we create a dictionary of the form string: float?

open System
open System.Collections.Generic

let ofSeq (src:seq<'a * 'b>) = 
    // from fssnip
    let d = new Dictionary<'a, 'b>()
    for (k,v) in src do
        d.Add(k,v)
    d


let msg = "one:1.0|two:2.0|three:3.0"
let msgseq = msg.Split[|'|'|] |> Array.toSeq |> Seq.map (fun i -> i.Split(':'))
let d = ofSeq msgseq // The type ''a * 'b' does not match the type 'string []'

This operation would be inside a tight loop so efficiency would be a plus. Although I'd like to see a simple solution as well just to get my F# bearings.

Thanks.

Upvotes: 3

Views: 1436

Answers (2)

TheInnerLight
TheInnerLight

Reputation: 12184

How about something like this:

let msg = "one:1.0|two:2.0|three:3.0"

let splitKeyVal (str : string) =
    match str.Split(':') with
    |[|key; value|] ->  (key, System.Double.Parse(value))
    |_ -> invalidArg "str" "str must have the format key:value"

let createDictionary (str : string) =
    str.Split('|') 
    |> Array.map (splitKeyVal)
    |> dict
    |> System.Collections.Generic.Dictionary

You could drop the System.Collections.Generic.Dictionary if you don't mind an IDictionary return type.

If you expect the splitKeyVal function to fail then you'd be better off expressing it as a function that returns option, e.g.:

let splitKeyVal (str : string) =
    match str.Split(':') with
    |[|key; valueStr|] ->  
        match System.Double.TryParse(valueStr) with
        |true, value -> Some (key, value)
        |false, _ -> None
    |_ -> None

But then you'd also have to decide how you wanted to handle failure in the createDictionary function.

Upvotes: 6

Sehnsucht
Sehnsucht

Reputation: 5049

Not sure about the perf side but if you're sure of your input and can "afford" a warning you can go with :

let d =
    msg.Split '|'
    |> Array.map (fun s -> let [|key; value|] (*warning here*) = s.Split ':' in key, value)
    |> dict
    |> System.Collections.Generic.Dictionary // optional if a IDictionary<string, string> suffice

Upvotes: 2

Related Questions