On Wed, Jul 27, 2011 at 11:15 PM, semperos <[email protected]> wrote:
> Darn tabbing...apologies for the previous post.
> Here's my full question:
> I have a map of maps, arbitrarily nested. Every individual map has a
> key/value entry that acts as a unique identifier for that map. I need to be
> able to write a function that, given the whole map of maps and the value of
> that unique ID, can dissoc the map with that unique ID.
> I feel like I'm missing something basic. I'm aware of things like assoc-in
> and update-in for dealing with nested structures, but those require knowing
> the structure ahead of time. I need to be able to pass in a unique ID which
> could be at any level of maps within the map and dissoc it once it's
> "found."
> My question: Is there a method or approach using plain Clojure maps that's
> eluding me? If not and this requires some kind of searching, is there a
> better data structure to use?

If the nested maps are values associated with their IDs as keys in the
containing map, then (dissoc containing-map id) ought to suffice. If
not, and I presume not, then you may need something like

(defn zap-id [containing-map sought-id-key sought-id-val]
  (if-let [x (some
               (fn [[k v]] (if (= (get v sought-id-key) sought-id-val) k))
               containing-map)]
    (dissoc containing-map x)
    containing-map))

Obviously this won't remove the target if its key in the containing
map is nil or false. It also assumes the nested map is a value, not a
key, itself, and goes only one level deep. It will ignore non-map
values in the map. If you want to go deeper, you need something like:

(defn zap-id [containing-map sought-id-key sought-id-val]
  (if-not (map? containing-map)
    containing-map
    (if-let [x (some
                 (fn [[k v]] (if (= (get v sought-id-key) sought-id-val) k))
                 containing-map)]
      (dissoc containing-map x)
      (into {}
        (map
          (fn [[k v]]
            [k (zap-id v sought-id-key sought-id-val)])
          containing-map)))))

This recurses into any nested maps that are values (but again ignores
maps used as keys).

It's not especially efficient, as it traverses the whole data
structure. To be efficient you'd need a different structure, one where
given an id you can locate the map with that id and given a map you
can locate its parent. This might be done by keeping, say, an id-map
of ids to maps with that id and a parent-map of maps to parents. If
the keys in the parents aren't the ids you'd need to map from map to
[parent, key-in-parent] tuples. And all of this has to be "updated"
every time any of this stuff is "changed".

Another option, if the ids can be anything you want, and the keys in
the parent maps never change over time, is to use [key1 key2 key3 ...]
paths as the ids, so the id of a nested map is a vector of the keys
used to reach it from the root. Then contrib (dissoc-in root-map id)
will do you, or (update-in root-map (butlast id) dissoc (last id)).
(That blows chunks of id is empty but then that would mean it was the
root map itself you wanted to nuke. I'm also not sure how well it
works when id is only one element long, but a dummy root map with a
single entry for the real root can be used to work around that, or
special casing like (condp = (count id) 0 nil 1 (dissoc root-map
(first id)) (update-in root-map (butlast id) dissoc (last id))).)

-- 
Protege: What is this seething mass of parentheses?!
Master: Your father's Lisp REPL. This is the language of a true
hacker. Not as clumsy or random as C++; a language for a more
civilized age.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to