Transforming an ugly nested loop into clojure code

348 views
Skip to first unread message

arekanderu

unread,
Sep 30, 2012, 3:59:05 PM9/30/12
to clo...@googlegroups.com
Hello,

I am trying to port an ugly piece of code from Ruby to clojure. So far I have only ported it to clojure by keeping the same way it was written in Ruby and i am trying to re-write it the clojure way because...well....its very ugly.
I have a complex hash map which it's structure is always the same and the keys always known and by using the values in those keys, i make some function calls with those values as parameters. You can find a simplified version of the map and the function which steps into the map below.

The map structure:

(def my-map 
  [{:a-key "foo" :items-a 
  [{:another-key "bar" :items-b 
 [{:items-c 
   [{:name "bar-bar" :items-d 
     [{:items-e 
       [{:name "foo-foo"}]
      }]
    }]
  }]
}]
  }])
 
And the function:

(defn my-func []
   (map (fn [a-hash]
           (map (fn [item-a]
                  (map (fn [item-b]
                         (map (fn [item-c]
                                (when-not (empty? (:items-e item-c))
                                  (map (fn [item]
                                             (doSomething (:a-key item-a) (:name item))
                                          (:items-e items-c))
                                 (doSomethingElse (:a-key item-a) (:another-key item-b) (:name item-c))))
                              (:items-c item-b)))
                       (:items-b item-a)))
                (:items-a a-hash)))
         my-map)))

I would really appreciate it if someone could propose an alternative way of writing the above function or at least to point me where can I look for some useful clojure functions that will help me do what I want but in a cleaner way.

Thank you for your time 
 

Grant Rettke

unread,
Sep 30, 2012, 5:41:41 PM9/30/12
to clo...@googlegroups.com
On Sun, Sep 30, 2012 at 2:59 PM, arekanderu <areka...@gmail.com> wrote:
> I am trying to port an ugly piece of code from Ruby to clojure.

May you share the original code?

> So far I
> have only ported it to clojure by keeping the same way it was written in
> Ruby and i am trying to re-write it the clojure way because...well....its
> very ugly.

Why does my-map have vectors storing maps inside instead of a map with
maps inside?

> I have a complex hash map which it's structure is always the same and the
> keys always known and by using the values in those keys, i make some
> function calls with those values as parameters. You can find a simplified
> version of the map and the function which steps into the map below.

May you write some tests to demonstrate what you want to accomplish
with the ugly map and share them here?

> I would really appreciate it if someone could propose an alternative way
> of writing the above function or at least to point me where can I look for
> some useful clojure functions that will help me do what I want but in a
> cleaner way.

Once you provide tests then we have a better chance at helping.

--
((λ (x) (x x)) (λ (x) (x x)))
http://www.wisdomandwonder.com/
ACM, AMA, COG, IEEE

arekanderu

unread,
Sep 30, 2012, 6:16:25 PM9/30/12
to clo...@googlegroups.com, gre...@acm.org
Thank you for your prompt reply Grant.

> May you share the original code? 

I will post the original function very soon


> Why does my-map have vectors storing maps inside instead of a map with 
> maps inside? 

Because each vector will have more than one hash-map and each hash map will have multiple keys. I only posted a simplified version of the map for readability, unless I am missing something here...
I think of vector as the equivalent of the "array of" and that's why I used it. If i should have done it in some other way please let me know.

> May you write some tests to demonstrate what you want to accomplish 
> with the ugly map and share them here? 

I am basically trying to destruct my deep map with a better way than a 4-nested loop. 

I will post a better code snippet in order to clean things even more.

Thanks again

arekanderu

unread,
Oct 1, 2012, 6:07:51 PM10/1/12
to clo...@googlegroups.com, gre...@acm.org
I wrote a much better example in order to show what am I trying to do. Of course, the following is a simplified version of the actual code in order to make things easier for everybody.

(def my-data [{:area "Somewhere" :warehouses
               [{:warehouse "W54321" :containers
                [{:container "C12345" :boxes
                  [{:box "B12345" :items
                    [{:item "I12345"}]}]}]}]}
              {:area "SomewhereElse" :warehouses
               [{:warehouse "W54321" :containers
                [{:container "C54321" :boxes
                  [{:box "B54321" :items
                    [{:item "I54321"}]}]}]}]}])

(defn my-func [data]
  (map (fn [area]
         (map (fn [warehouse]
                (map (fn [container]
                       (map (fn [box]
                              (if (not (empty? (:items box)))
                                (map (fn [item]
                                       (doSomething (:box box) (:item item)))
                                     (:items box))
                                (doSomethingElse (:warehouse warehouse) (:container container) (:box box))))
                              (:boxes container)))
                     (:containers warehouse)))
              (:warehouses area)))
       data))

My question is, how can I get rid of the nested loops and replace it with something more elegant in clojure.

Thank you for any replies.

gaz jones

unread,
Oct 1, 2012, 6:22:18 PM10/1/12
to clo...@googlegroups.com
You appear to be running over the map purely for side-effects,
therefore off the top of my head something like:

(defn my-func
[data]
(doseq [area data
warehouse (:warehouses area)
container (:containers warehouse)
box (:boxes container)]
(if-not (empty? (:items box))
(doseq [item (:items box)] (do-something (:box box) (:item item)))
(do-something-else (:warehouse warehouse)
(:container container)
(:box box)))))

Might be more appropriate...

arekanderu

unread,
Oct 1, 2012, 6:36:14 PM10/1/12
to clo...@googlegroups.com
Thank you Gaz for your reply. You are correct about the side effects.

I will go through your example more carefully and give it a go. It definitely looks more promising than what I got :)

Stathis Sideris

unread,
Oct 2, 2012, 10:48:35 AM10/2/12
to clo...@googlegroups.com
I'm not sure how/if the following would fit your use case exactly, but you might want to explore the get-in function (and assoc-in, the clojure.walk namespace and the zippers functionality. They all make dealing with such deeply nested structures easier.

Stathis

arekanderu

unread,
Oct 2, 2012, 2:01:39 PM10/2/12
to clo...@googlegroups.com
Thank you for your input Stathis, I will have a look at them as well.
Reply all
Reply to author
Forward
0 new messages