A vector of maps into a tree - dictionary

I have a collection that has the following structure [{:a 0} {:b 1} {:c 1} {:d 2} {:e 3} {:f 2}]. Basically, it is a tree, where an element of the vector is a node. What the number signify is the parent-child relationship. So, the element {:a 0} is the master parent (has no parents), while {:b 1}, {:c 1} are childs of {:a 0}. Also, {:d 2} is a child of {:c 1}.
What I want is to construct a list or a vector (doesn't matter at this point) that has the following structure:
[{:a {:b nil :c {:d {:e nil} :f nil}}}].
How can this be achieved?

This should work:
(fn [xs]
(loop [[x & rs :as xs] xs
m {}
lvl {}]
(if (seq xs)
(let [[k l] (first x)
p (conj (lvl (dec l) []) k)]
(recur
rs
(assoc-in m p nil)
(assoc lvl l p)))
m)))
As #jas mentioned we can't rely on key-order of map, so here we use lvl map to keep last seen element's path per level.

Related

Clojure - walk with path

I am looking for a function similar to those in clojure.walk that have an inner function that takes as argument :
not a key and a value, as is the case with the clojure.walk/walk function
but the vector of keys necessary to access a value from the top-level data structure.
recursively traverses all data
Example :
;; not good since it takes `[k v]` as argument instead of `[path v]`, and is not recursive.
user=> (clojure.walk/walk (fn [[k v]] [k (* 10 v)]) identity {:a 1 :b {:c 2}})
;; {:a 10, :c 30, :b 20}
;; it should receive as arguments instead :
[[:a] 1]
[[:b :c] 2]
Note:
It should work with arrays too, using the keys 0, 1, 2... (just like in get-in).
I don't really care about the outer parameter, if that allows to simplify the code.
Currently learning clojure, I tried this as an exercise.
I however found it quite tricky to implement it directly as a walk down the tree that applies the inner function as it goes.
To achieve the result you are looking for, I split the task in 2:
First transform the nested structure into a dictionary with the path as key, and the value,
Then map the inner function over, or reduce with the outer function.
My implementation:
;; Helper function to have vector's indexes work like for get-in
(defn- to-indexed-seqs [coll]
(if (map? coll)
coll
(map vector (range) coll)))
;; Flattening the tree to a dict of (path, value) pairs that I can map over
;; user> (flatten-path [] {:a {:k1 1 :k2 2} :b [1 2 3]})
;; {[:a :k1] 1, [:a :k2] 2, [:b 0] 1, [:b 1] 2, [:b 2] 3}
(defn- flatten-path [path step]
(if (coll? step)
(->> step
to-indexed-seqs
(map (fn [[k v]] (flatten-path (conj path k) v)))
(into {}))
[path step]))
;; Some final glue
(defn path-walk [f coll]
(->> coll
(flatten-path [])
(map #(apply f %))))
;; user> (println (clojure.string/join "\n" (path-walk #(str %1 " - " %2) {:a {:k1 1 :k2 2} :b [1 2 3]})))
;; [:a :k1] - 1
;; [:a :k2] - 2
;; [:b 0] - 1
;; [:b 1] - 2
;; [:b 2] - 3
It turns out that Stuart Halloway published a gist that could be of some use (it uses a protocol, which makes it extensible as well) :
(ns user)
(def app
"Intenal Helper"
(fnil conj []))
(defprotocol PathSeq
(path-seq* [form path] "Helper for path-seq"))
(extend-protocol PathSeq
java.util.List
(path-seq*
[form path]
(->> (map-indexed
(fn [idx item]
(path-seq* item (app path idx)))
form)
(mapcat identity)))
java.util.Map
(path-seq*
[form path]
(->> (map
(fn [[k v]]
(path-seq* v (app path k)))
form)
(mapcat identity)))
java.util.Set
(path-seq*
[form path]
(->> (map
(fn [v]
(path-seq* v (app path v)))
form)
(mapcat identity)))
java.lang.Object
(path-seq* [form path] [[form path]])
nil
(path-seq* [_ path] [[nil path]]))
(defn path-seq
"Returns a sequence of paths into a form, and the elements found at
those paths. Each item in the sequence is a map with :path
and :form keys. Paths are built based on collection type: lists
by position, maps by key, and sets by value, e.g.
(path-seq [:a [:b :c] {:d :e} #{:f}])
({:path [0], :form :a}
{:path [1 0], :form :b}
{:path [1 1], :form :c}
{:path [2 :d], :form :e}
{:path [3 :f], :form :f})
"
[form]
(map
#(let [[form path] %]
{:path path :form form})
(path-seq* form nil)))
(comment
(path-seq [:a [:b :c] {:d :e} #{:f}])
;; finding nils hiding in data structures:
(->> (path-seq [:a [:b nil] {:d :e} #{:f}])
(filter (comp nil? :form)))
;; finding a nil hiding in a Datomic transaction
(->> (path-seq {:db/id 100
:friends [{:firstName "John"}
{:firstName nil}]})
(filter (comp nil? :form)))
)
Note : in my case I could also have used Specter, so if you are reading this, you may want to check it out as well.
There is also https://github.com/levand/contextual/
(def node (:b (first (root :a))))
(= node {:c 1}) ;; => true
(c/context node) ;; => [:a 0 :b]

Traversing a graph in clojure

Hope you are well. I am stuck with a recursive program that is suppose to traverse a graph until it finds a path back to the start node. the code is here,
(def graph {:A {:B 5 :D 5 :E 7}
:B {:C 4}
:C {:D 8 :E 2}
:D {:C 8 :E 6}
:E {:B 3}
})
(defn looper [g startnode & args]
(let [[inode] (vec args)
acc []]
(if (= startnode inode)
(conj acc inode)
(conj acc inode (map (partial looper g startnode) (vec (keys (get g inode)))))
)))
(looper graph :C)
there is something wrong with the way I accumulate the result I couldnt find what exactly.
The function should return something like '(CDC CEBC) for the above call.
This did the trick, hope its helpful for someone :)
(defn- dfs
[graph goal]
(fn search
[path visited]
(let [current (peek path)]
(if (= goal current)
[path]
(->> current graph keys
(remove visited)
(mapcat #(search (conj path %) (conj visited %))))))))
(defn findpath
"Returns a lazy sequence of all directed paths from start to goal
within graph."
[graph start goal]
((dfs graph goal) [start] #{start}))
(defn circular-path-count [graph node]
(flatten (map (fn [s]
(map count (findpath graph s node))) (vec (keys (get-in graph [node]))) )))
e.g. usage: (circular-path-count paths :B)

Clojure: applyng function on every value in a map in vector of maps

How to apply a function on every value in a map in vector of maps.
If I have a vector of maps
(def vector-of-maps [{:a 1 :b 2} {:a 3 :b 4}])
And want to apply a function on every value in every map and as a result I want the same vector of maps, something like this
(map #(+ 1 %) vector-of-maps)
So that the result is
[{:a 2 :b 3} {:a 4 :b 5}]
And I want it to work with any vector of maps, not just this particular one....
=> (defn update-map [m f] (reduce-kv (fn [m k v] (assoc m k (f v))) {} m))
=> (map #(update-map % inc) vector-of-maps)
({:b 3, :a 2} {:b 5, :a 4})
Perhaps
(defn mapv-map-values [f vm]
(letfn [(map-values [m] (zipmap (keys m) (map f (vals m))))]
(mapv map-values vm)))
... producing
(mapv-map-values inc [{:a 1 :b 2} {:a 3 :b 4}])
;[{:b 3, :a 2} {:b 5, :a 4}]
The map-values function, the only significant departure from #user100464's answer, was adapted from here.
also you can do like this,but it is ugly
(defn dodo [m] (map (fn [map] (apply merge (for [[k v] map] (assoc {} k (inc v))))) m))
(dodo [{:a 1 :b 2} {:a 3 :b 4}])
;({:a 2, :b 3} {:a 4, :b 5})

Using Clojure update-in with multiple keys

I'm trying to apply a function to all elements in a map that match a certain key.
(def mymap {:a "a" :b "b" :c "c"})
(update-in mymap [:a :b] #(str "X-" %))
I'm expecting
{:a "X-a", :c "c", :b "X-b"}
But I get
ClassCastException java.lang.String cannot be cast to clojure.lang.Associative clojure.lang.RT.assoc (RT.java:702)
Anyone can help me with this?
update-in is to update a single key in the map (at a particular nesting level, [:a :b] means update key :b inside the map value of key :a.
What you want can be done using reduce:
(reduce #(assoc %1 %2 (str "X-" (%1 %2)))
mymap
[:a :b])
Here's a generalized function:
(defn update-each
"Updates each keyword listed in ks on associative structure m using fn."
[m ks fn]
(reduce #(update-in %1 [%2] fn) m ks))
(update-each mymap [:a :b] #(str "X-" %))
In the solution below, the haspmap if first filtered, then it is mapped to the str function, and then merged with the original hashmap -
(def m {:a "a" :b "b" :c "c"})
(def keys #{:a :b})
(->> m
(filter (fn [[k v]] (k keys)))
(map (fn [[k v]] [k (str "X-" v)]))
(into {})
(merge m))

Tree from adjacency map

I'm trying to make a function that builds a tree from an adjacency list of the form {node [children]}.
(def adjacency
{nil [:a]
:a [:b :c]
:b [:d :e]
:c [:f]})
which should result in
{nil {:a {:b {:d nil
:e nil}
:c {:f nil}}}}
However I tried, I couldn't get it to work. Recursion is a bit of a weak spot of mine, and most recursion examples I found only dealt with recursion over a list, not a tree.
Edited: Original dataset and result were unintentionally nested too deep, due to not having an editor and original source at time of posting. Sorry about that.
There is only one entry in every submap in adjacency. Is this necessary? And the same problem in the result tree.
I hope it would be more clear:
(def adjacency {:a [:b :c]
:b [:d :e]
:c [:f]})
So solution is:
(defn tree [m root]
(letfn [(tree* [l]
(if (contains? m l)
{l (into {} (map tree* (m l)))}
[l nil]))]
(tree* root)))
Test:
(tree adjacency :a)
=> {:a {:b {:d nil
:e nil}
:c {:f nil}}}
Update. If you don't need the result tree as nested maps
(defn tree [m root]
(letfn [(tree* [l]
(if (contains? m l)
(list l (map tree* (m l)))
(list l nil)))]
(tree* root)))
(tree adjacency :a)
=> (:a ((:b ((:d nil)
(:e nil)))
(:c ((:f nil)))))
I usually prefer to use clojure.walk when dealing with trees.
I am assuming that the root node is first in the adjacency vector.
(use 'clojure.walk)
(def adjacency
[{nil [:a]}
{:a [:b :c]}
{:b [:d :e]}
{:c [:f]}])
(prewalk
(fn [x]
(if (vector? x)
(let [[k v] x lookup (into {} adjacency)]
[k (into {} (map (fn [kk] [kk (lookup kk)]) v))])
x))
(first adjacency))
Result: {nil {:a {:b {:d {}, :e {}}, :c {:f {}}}}}
NOTE: Empty child are represented as {} rather than nil, also child elements are maps rather than vector as map makes easy to navigate this tree then.

Resources