Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clojure rename-keys in nested structure

Tags:

clojure

Suppose I have a nested structure, something like this:

{:data1 
    {:categories [
        {:name "abc" :id 234 :desc "whatever"} 
        {:name "def" :id 456 :desc "nothing"}]
    }
  :data2 {...}
  :data3 {...}
}

And I need to transform the key names in the maps. I can transform the top level keys like this:

(rename-keys mymap {:data1 :d1})

But I'm not sure how to rename keys nested more deeply in the data structure (say I want to rename the :desc field to :description).

I'm pretty sure that zippers are the answer but can't quite figure out how to do it, or if there's a more straightforward way.

like image 716
Kevin Avatar asked Oct 19 '11 22:10

Kevin


2 Answers

postwalk is a pretty heavy sledgehammer in general, although it looks from your original question like you might need it. In many cases, you can perform updates in a nested structure with update-in:

user> (let [m {:foo {:deep {:bar 1 :baz 2}}}]
        (update-in m [:foo :deep] clojure.set/rename-keys {:baz :periwinkle}))
{:foo {:deep {:periwinkle 2, :bar 1}}}
like image 111
amalloy Avatar answered Nov 08 '22 21:11

amalloy


Same as Brian Carper's solution, except the walk namespace already has a specific function for this purpose. All keys at all levels are changed, be they nested inside any sort of collection or seq.

(:use 'clojure.walk)

(def x
  {:data1
   {:categories
    [{:desc "whatever", :name "abc", :id 234}
     {:desc "nothing", :name "def", :id 456}]},
   :data2
   {:categories
    [{:desc "whatever", :name "abc", :id 234}
     {:desc "nothing", :name "def", :id 456}]}})

(postwalk-replace {:desc :something} x)

{:data1
 {:categories
  [{:something "whatever", :name "abc", :id 234}
   {:something "nothing", :name "def", :id 456}]},
 :data2
 {:categories
  [{:something "whatever", :name "abc", :id 234}
   {:something "nothing", :name "def", :id 456}]}}
like image 9
NielsK Avatar answered Nov 08 '22 21:11

NielsK