Enumerate in a functional way

Hello,
I try to develop a function that could compare two dictionaries. As a result i would like a sequence, for each key present, if it has a match or if it is an orphan, and if it has a match, does the corresponding value matches too;
I did it easily for a list but enumerate through the entries of a dictionary is harder for me with F#. Maybe someone can have some clue?

1 Like

Dictionary implements IEnumerable<KeyValuePair<TKey,TValue>> interface (and not only this one - check the docs to see all of them) so you can iterate over dictionary using functions from Seq module or via for loop or whatever way that you convinience with.

Hint: use TryGetValue method to get a value from the dictionary in a safe way.

Thank you @FoggyFinder for your answer. I made a mistake when talking of dictionary, i mean map actually.

I wrote the following for a list:

  let rec syncZip comp (list1: 'a list) (list2: 'a list) =
      match list1, list2 with
      | [], [] -> Seq.empty
      | [], e2::tail2 -> seq {
                                yield (None,Some e2)
                                yield! syncZip comp [] tail2
                          }
      | e1::tail1, [] -> seq {
                                yield (Some e1,None)
                                yield! syncZip comp tail1 []
                              }
      | e1::tail1, e2::tail2 -> 
                              seq {
                                  match comp e1 e2 with
                                  | c when c > 0 -> 
                                      yield (None, Some e2) 
                                      yield! syncZip comp list1 tail2 
                                  | 0 -> 
                                      yield (Some e1, Some e2) 
                                      yield! syncZip comp tail1 tail2 
                                  | c when c < 0 -> 
                                      yield (Some e1, None) 
                                      yield! syncZip comp tail1 list2
                              }

and i would like the same result but with a map

  let rec syncZip comp (map1:Map<'a,'b>) (map2:Map<'a,'b>) : seq<('a * 'b) option * ('a * 'b) option> =
    ListHelpers.syncZip comp (Map.toList map1) (Map.toList map2)

but i found that building to list to do the job is not very efficient

Can you give a sample: set of input data and desirable output?

Yes, here it is:

let capitals =
    [("Australia", "Canberra");  ("China", "Beijing");
        ("Denmark", "Copenhagen"); ("Egypt", "Cairo"); ("Finland", "Helsinki");
        ("France", "Paris"); ("Germany", "Berlin"); ("India", "New Delhi");
        ("Japan", "Tokyo"); ("Mexico", "Mexico City"); ("Russia", "Moscow");
        ("Slovenia", "Ljubljana"); ("Spain", "Madrid"); ("Sweden", "Stockholm");
        ("Taiwan", "Taipei"); ("Hong Kong", "Hong Kong");("USA", "Washington D.C.")]
    |> Map.ofList
    
let capitals2 =
    [("Australia", "Canberra"); ("Canada", "Ottawa"); ("China", "Beijing");
        ("Denmark", "Copenhagen"); ("Egypt", "Cairo"); ("Finland", "Helsinki");
        ("France", "Paris"); ("Germany", "Berline"); ("India", "New Delhi");
        ("Japan", "Tokyo"); ("Mexico", "Mexico City"); ("Russia", "Moscow");
        ("Spain", "Madrid"); ("Sweden", "Stockholm");
        ("Taiwan", "Taipei"); ("Hong Kong", "Hong Kong");("USA", "Washington D.C.")]
    |> Map.ofList

syncZip compareString capitals capitals2 |> Seq.iter (fun item -> printfn "%A" item)

(Some ("Australia", "Canberra"), Some ("Australia", "Canberra"))
(None, Some ("Canada", "Ottawa")) // left orphan
(Some ("China", "Beijing"), Some ("China", "Beijing"))
(Some ("Denmark", "Copenhagen"), Some ("Denmark", "Copenhagen"))
(Some ("Egypt", "Cairo"), Some ("Egypt", "Cairo"))
(Some ("Finland", "Helsinki"), Some ("Finland", "Helsinki"))
(Some ("France", "Paris"), Some ("France", "Paris"))
(Some ("Germany", "Berlin"), Some ("Germany", "Berline")) // value is not analyzed
(Some ("Hong Kong", "Hong Kong"), Some ("Hong Kong", "Hong Kong"))
(Some ("India", "New Delhi"), Some ("India", "New Delhi"))
(Some ("Japan", "Tokyo"), Some ("Japan", "Tokyo"))
(Some ("Mexico", "Mexico City"), Some ("Mexico", "Mexico City"))
(Some ("Russia", "Moscow"), Some ("Russia", "Moscow"))
(Some ("Slovenia", "Ljubljana"), None) // right orphan
(Some ("Spain", "Madrid"), Some ("Spain", "Madrid"))
(Some ("Sweden", "Stockholm"), Some ("Sweden", "Stockholm"))
(Some ("Taiwan", "Taipei"), Some ("Taiwan", "Taipei"))
(Some ("USA", "Washington D.C."), Some ("USA", "Washington D.C."))
1 Like

Okay, now the matter is clear.

You can start with simple, naive approach and then improve it step by step until result fits your criterias.

First that comes to mind:

a) take all keys from the first map
b) take all keys from the second map
c) create union set to exclude duplicates
d) iterate over and grab Some(value) from both maps. None if key doesn’t exist.

Hints:

  1. There is no build-in property Keys like for Dictionary
  2. To get result as an option type you can use Map.TryFind or Map.tryFind if you prefer that style.

Edit:


Forgot to mention:

Map also implements IEnumerable interface so part of my previous comment about Dictionary is true for Map as well.

Besides there is Map module that contans bunch of useful functions.

1 Like