Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure: How to collapse nested maps with a specific key?

Tags:

clojure

I'm trying to clean up some JSON data in Clojure. Some values in the JSON document are encapsulated in objects with associated (and no longer needed) metadata. I start with a JSON document like:

{ "household": {
    "address": {
        "street": { "value": "123 Fire Ln", "foo": "bar1" },
        "zip": { "value": "01234", "foo": "bar2" }
    },
    "persons": [
        {
            "id": "0001",
            "name": { "value": "John Smith", "foo": "bar3" }
        },
        {
            "id": "0002",
            "name": { "value": "Jane Smith", "foo": "bar4" }
        }
    ]
} }

Using Cheshire I parse this JSON and get the following data structure:

{ "household" {
    "address" {
        "street" {"value" "123 Fire Ln", "foo" "bar1"},
        "zip" {"value" "01234", "foo" "bar2"}
    },
    "persons" [
        {"id" "0001", "name" {"value" "John Smith", "foo" "bar3"}}
        {"id" "0002", "name" {"value" "Jane Smith", "foo" "bar4"}}
    ]
} }

My goal is to "collapse" those nested maps with a "value" key, drop the "foo" assoc, and assign the value to the map key one level higher (e.g., "street", "zip", "name"). The resulting data structure would look like:

{ "household" {
    "address" {
        "street" "123 Fire Ln",
        "zip" "01234"
    },
    "persons" [
        {"id" "0001", "name" "John Smith"}
        {"id" "0002", "name" "Jane Smith"}
    ]
} }

Any help here would be wonderful, thanks!

like image 530
mrspacey Avatar asked Mar 08 '12 20:03

mrspacey


1 Answers

Sounds like a job for clojure.walk/postwalk!

(defn collapse [obj]
  (postwalk (fn [obj]
              (or (and (map? obj)
                       (get obj "value"))
                  obj))
            obj))

You can actually shorten this substantially because get is willing to work on non-map objects (it just returns nil), but I think it's a lot more clear what is going on in the first version.

(defn collapse [obj]
  (postwalk #(get % "value" %) obj))
like image 80
amalloy Avatar answered Sep 20 '22 12:09

amalloy