Use reduce or something appropriate instead of loop / recur

131 views
Skip to first unread message

Sven Richter

unread,
Mar 24, 2015, 5:22:21 PM3/24/15
to clo...@googlegroups.com
Hi,

I wrote a function to trackdown a path in a vector containing nested maps:

(defn get-files-from-folder-path [ffs folder-path]
(filter #(= :file (:type %))
(loop [tree-path-position 0 acc [] fof ffs]
(let [folder (first (filter #(and
(= :folder (:type %))
(= (nth folder-path tree-path-position nil) (:name %))) fof))]
(if (not (:children folder))
acc
(recur (inc tree-path-position)
(if (= (+ tree-path-position 1) (count folder-path)) (concat acc (:children folder)) acc)
(:children folder)))))))

And these are the inputs:
(def ffs [{:type :folder, :name "sub1", :children [{:type :file, :name "datomic-data.edn"}]}
{:type :folder, :name "sub2", :children [{:type :file, :name "foo (1).csv"}
{:type :folder, :name "sub21", :children [{:type :file, :name "lein-env"}]}]}])

(def tree-path ["sub2" "sub21"])

And I call it like this:

(
get-files-from-folder-path ffs tree-path)

Is there a way to use reduce for that? I was stuck because I think I have to reduce on two lists here,
but reduce only takes one to start with, thats why I chose the loop / recur route. Which works
but it looks ugly to me and I am afraid in one week I want understand it anymore.

Thanks Regards,
Sven


Francis Avila

unread,
Mar 24, 2015, 5:55:23 PM3/24/15
to clo...@googlegroups.com
Separate out traversal from selection to make this clearer.

We make a generic traversal function get-in-via. It accepts a via function which takes the current result and some value which determines the next result, and returns the next result.

(defn get-in-via [m via ks]
 
(reduce (fn [m' k] (via m' k)) m ks))


Here is a via function that follows folder paths. This is a single "step" of the reduction.

(defn via-folderpath [items foldername]

 
(->> items
       
(filter #(and (= (:type %) :folder)
                     
(= (:name %) foldername)))
       
(first)
       
:children))



Example of use:

(get-in-via ffs via-folderpath ["sub2" "sub21"])

;=> [{:name "lein-env", :type :file}]

Francis Avila

unread,
Mar 24, 2015, 5:58:18 PM3/24/15
to clo...@googlegroups.com
Notice that get-in-via is simply reduce:

(defn get-in-via [m via ks]
 
(reduce (fn [m' k] (via m' k)) m ks))


Same as:

(defn get-in-via [m via ks]

 
(reduce via m ks))



Same as:

(reduce via m ks)



So once you write your "step" function, traversal is taken care of by the reduction.

Sven Richter

unread,
Mar 25, 2015, 2:58:00 AM3/25/15
to clo...@googlegroups.com
Thank you very much. I had the feeling I should reduce over the tree-path, but was not able to come up with something good like you did.

Best Regards,
Sven
Reply all
Reply to author
Forward
0 new messages