NoIdeaHowToFixThis
NoIdeaHowToFixThis

Reputation: 4564

Pattern match against existing variables

I have a structure of nested maps:

[<RequireQualifiedAccess>]
type NestedMap =
    | Object of Map<string,NestedMap>
    | Value of int    

I need to prune the structure. The purpose of the code is to maintain intact the nested structure of the maps and of the map where the key value pair is found, pruning the branches where the key value pair is not found.

Here is the test NestedMap:

let l2'  = NestedMap.Object ( List.zip ["C"; "S"; "D"] [NestedMap.Value(10); NestedMap.Value(20); NestedMap.Value(30)] |> Map.ofList)
let l3   = NestedMap.Object ( List.zip ["E"; "S"; "F"] [NestedMap.Value(100); NestedMap.Value(200); NestedMap.Value(300)] |> Map.ofList)    
let l2'' = NestedMap.Object ( List.zip ["G"; "H"; "I"; "S"] [NestedMap.Value(30); l3; NestedMap.Value(40); NestedMap.Value(50)] |> Map.ofList)    
let l1   = NestedMap.Object ( List.zip ["Y"; "A"; "B"] [NestedMap.Value(1); l2'; l2''] |> Map.ofList)

This is my code:

let rec pruneWithKeyValue (keyvalue: string * int) (json: NestedMap) =
    let condition ck cv  = 
        let tgtKey = (fst keyvalue)
        let tgtVal = (snd keyvalue)
        match (ck, cv) with
        | (tgtKey, NestedMap.Value(tgtVal)) -> 
            printfn ">>> Found match : "
            printfn "       ck = %s    " ck 
            printfn "       tgtKey and tgtVal == %s, %i" tgtKey tgtVal
            true
        | _ -> false
    match json with
    | NestedMap.Object nmap -> 
        if (nmap |> Map.exists (fun k v -> condition k v)) then 
            json
        else
            printfn "Expanding w keyvalue: (%s,%i): " (fst keyvalue) (snd keyvalue)
            let expanded = nmap |> Map.map (fun k v -> pruneWithKeyValue keyvalue v) 
            NestedMap.Object(expanded |>  Map.filter (fun k v -> v <> NestedMap.Object (Map.empty)))  
    | _ -> NestedMap.Object (Map.empty)

let pruned = pruneWithKeyValue ("S",20) l1
let res = (pruned = l1)

The result is not what desired:

>>> Found match : 
       ck = Y    
       tgtKey and tgtVal == Y, 1

val pruneWithKeyValue : string * int -> json:NestedMap -> NestedMap
val pruned : NestedMap =
  Object
    (map
       [("A", Object (map [("C", Value 10); ("D", Value 30); ("S", Value 20)]));
        ("B",
         Object
           (map
              [("G", Value 30);
               ("H",
                Object
                  (map [("E", Value 100); ("F", Value 300); ("S", Value 200)]));
               ("I", Value 40); ("S", Value 50)])); ("Y", Value 1)])
val remainsTheSame : bool = true

The code says that the output data structure remains unchanged (val remainsTheSame : bool = true). Even more interestingly, somehow the keyvalue tuple that contains the key-value pair the function is searching got modified:

>>> Found match : 
       ck = Y    
       tgtKey and tgtVal == Y, 1

This is the problem. In fact, if I hardcode the keyvalue tuple:

let rec pruneWithKeyValue (keyvalue: string * int) (json: NestedMap) =
    let condition ck cv  = 
        let tgtKey = (fst keyvalue)
        let tgtVal = (snd keyvalue)
        match (ck, cv) with
        | ("S", NestedMap.Value(20)) -> 
            printfn ">>> Found match : "
            printfn "       ck = %s    " ck 
            printfn "       tgtKey and tgtVal == %s, %i" tgtKey tgtVal
            true
        | _ -> false
    match json with
    | NestedMap.Object nmap -> 
        if (nmap |> Map.exists (fun k v -> condition k v)) then 
            json
        else
            printfn "Expanding w keyvalue: (%s,%i): " (fst keyvalue) (snd keyvalue)
            let expanded = nmap |> Map.map (fun k v -> pruneWithKeyValue keyvalue v) 
            NestedMap.Object(expanded |>  Map.filter (fun k v -> v <> NestedMap.Object (Map.empty)))  
    | _ -> NestedMap.Object (Map.empty)

let pruned = pruneWithKeyValue ("S",20) l1
let remainsTheSame = (pruned = l1)

results in (yeah) the desired result:

Expanding w keyvalue: (S,20): 
>>> Found match : 
       ck = S    
       tgtKey and tgtVal == S, 20
Expanding w keyvalue: (S,20): 
Expanding w keyvalue: (S,20): 

val pruneWithKeyValue : string * int -> json:NestedMap -> NestedMap
val pruned : NestedMap =
  Object
    (map
       [("A", Object (map [("C", Value 10); ("D", Value 30); ("S", Value 20)]))])
val remainsTheSame : bool = false

It may be trivial but I don't understand where and how keyvalue ends up being modified, preventing me from getting the right output with parametric key-value tuple.

Upvotes: 0

Views: 77

Answers (1)

Gus
Gus

Reputation: 26174

You can't pattern match against existing variables, in your original code tgtKey and tgtVal will be new bindings, not related to the existing ones which will be shadowed.

So change your match:

    match (ck, cv) with
    | (tgtKey, NestedMap.Value(tgtVal)) -> 

to:

    match (ck, cv) with
    | (k, NestedMap.Value v) when (k, v) = (tgtKey, tgtVal) -> 

or just:

    match (ck, cv) with
    | x when x = (tgtKey, NestedMap.Value(tgtVal)) -> 

Upvotes: 3

Related Questions