BrendanC
BrendanC

Reputation: 463

Extracting Primary Key data value from a dictionary sequence

I wrote the following code to extract the values of the ID fields in a sequence of dictionaries and return these as a set - basically I'm looking for the PK values for rows in a db table that I've used to populate a dictionary (1 dict per table row). Below the code is some sample data that is loaded into the dictionary sequences.

While the code below works the style probably could be more functional - I had to resort to using a mutable list to build the result set. So it does not feel right to me.

Anyone care to offer an improved, more functional solution here.

// Extract the PK values from a dictionary and create a key set  from these data values
// The expected result here is: set ["PK1"; "PK2"; "PK3"; "PK4"]
let get_keyset (tseq:seq<Dictionary<string,string>>) =  
    let mutable setres =[]  
    for x in tseq do  
        for KeyValue(k,v) in x do  
            // Extract ID values/keys from each dict  
            setres <- x.Item("ID")::setres  
    setres |> List.rev |> Set.ofList   

// Sample Data  
// First Tuple is PK/ID value  
let tabledata = [  
                [("ID", "PK1"); ("a2","aa"); ("a3", "aaa"); ("a4", "aaaa") ]  
                [("ID", "PK2"); ("b2","bb"); ("b3", "bbb"); ("b4", "bbbb") ]  
                [("ID", "PK3"); ("c2","cc"); ("c3", "ccc"); ("c4", "cccc") ]  
                [("ID", "PK4"); ("d2","dd"); ("d3", "ddd"); ("d4", "dddd") ]  
                ]  

//generate dict sequence from datasets  
let gendictseq tabledata =  
    seq {  
        for tl in tabledata do  
            let ddict = new Dictionary<string,string>()    
            for (k,v) in tl do  
                    ddict.Add(k,v)  
            yield ddict  
    }  

Upvotes: 1

Views: 318

Answers (3)

ildjarn
ildjarn

Reputation: 62975

Your get_keyset looks quite convoluted to me. This is considerably more succinct:

let get_keyset tseq =
    tseq |> Seq.map (fun (x:Dictionary<_,_>) -> x.["ID"]) |> set

For gendictseq, I would personally prefer higher-order functions over a sequence expression, but that is largely a matter of taste:

let gendictseq tabledata =
    tabledata
    |> Seq.map (fun table ->
        (Dictionary<_,_>(), table)
        ||> List.fold (fun dict keyValue -> dict.Add keyValue; dict))

Upvotes: 3

Ankur
Ankur

Reputation: 33637

Functionally this operation is map as shown below:

let get_keyset_new (tseq:seq<Dictionary<string,string>>) = 
  let s = tseq |> Seq.map (fun i -> i |> Seq.map (fun e -> i.Item("ID") ) )
  seq {
      for i in s do
          yield! i
  } |> Set.ofSeq

Upvotes: 0

Yin Zhu
Yin Zhu

Reputation: 17119

Using a ResizeArray (List<T> in C#) is better than a mutable list variable:

let getKeyset (tseq:seq<Dictionary<string,string>>) =  
    let setres = new ResizeArray<string>()
    for x in tseq do 
        for KeyValue(k,v) in x do 
            setres.Add(x.["ID"])
    setres |> Set.ofSeq

or use the more functional sequence computation:

let getKeyset2 (tseq:seq<Dictionary<string,string>>) =  
    seq {
        for x in tseq do 
            for KeyValue(k,v) in x do 
                yield x.["ID"]
        }
    |> Set.ofSeq

Upvotes: 1

Related Questions