3

I have this structure (which it is the result of parsing JSON response):

 [{"a" {"b" 1 "c" 2} 
       "children" [{"a" {"b" 3 "c" 4} "children" []}]}
  {"a" {"b" 5 "c" 6} "children" []}
  {"a" {"b" 7 "c" 8}
        "children" [{"a" {"b" 9 "c" 10} "children" []}]}]

So it is a tree. children is a vector of node. Each node is a map that has a, b and children.

I'm trying to find the node that has the value 9 for its b property. So, the result of the lookup is:

I tried to traverse the structure with tree-seq:

(tree-seq #(not-empty? % "children") identity structure)

But actually I'm getting the same structure. I was expecting to get a sequence of nodes where the relation is flattened and then I can filter on the sequence.

How to do that in an idiomatic way (and hopefully performant)? Feel free to blow my mind with zippers or walk.

Chiron
  • 20,081
  • 17
  • 81
  • 133
  • you want to search through current three nodes or in nested nodes as well? And the result should be either parent node (with children node) either some of children node? – fl00r Aug 19 '15 at 19:15
  • @fl00r I need to search through the nested nodes also. So, if I'm searching for the node where b = 9 , I want to get that node, including its children. – Chiron Aug 19 '15 at 19:17
  • 1
    `(tree-seq #(or (vector? %) (% "children")) #(if (vector? %) (seq %) (% "children")) data)`? – fl00r Aug 19 '15 at 19:35
  • @fl00r That is neat! Would you please post it as answer? And would you please explain why `vector?` is part of if-branch predicate? – Chiron Aug 19 '15 at 19:48

2 Answers2

2

You can get the desired tree-seq like so:

(def ts (mapcat (partial tree-seq #(contains? % "children") 
                                  #(get % "children"))
                your-data-structure))

mapcat is required because your input datastructure contains multiple root-nodes.

E. g. find a node like so:

(first (filter #(= (get-in % ["a" "b"]) 9) ts))
;-> {"a" {"b" 9, "c" 10}, "children" []}
Leon Grapenthin
  • 9,246
  • 24
  • 37
0
(def data
  [{"a" {"b" 1 "c" 2} 
    "children" [{"a" {"b" 3 "c" 4} "children" []}]}
   {"a" {"b" 5 "c" 6} 
    "children" []}
   {"a" {"b" 7 "c" 8}
    "children" [{"a" {"b" 9 "c" 10} "children" []}]}])

branch? fn will receive whole vector as an argument on first iteration, so I used vector? predicate, but it is unnecessary because we could simplify it with simple not-empty function which will work perfectly fine for both vector and map.

(tree-seq 
  not-empty
  #(if (vector? %) % (% "children")) 
  data)
fl00r
  • 82,987
  • 33
  • 217
  • 237
  • 1
    Performance: branch? is supposed to be a predicate. Reliability: Logical truth is not even mentioned in the docstring of tree-seq. Performance: The vector? condition only holds one time. Style: #(not-empty %) is the same as not-empty. – Leon Grapenthin Aug 19 '15 at 20:18
  • @LeonGrapenthin What do you mean by: `"vector? condition only holds one time"` ? – Chiron Aug 19 '15 at 20:26
  • @Chiron The only vector passed to that lambda is the input dataset. From thereon, all children are maps. This is because the input dataset is not a generic node. It is a collection of children (or root-nodes, depending on how you want to see it). – Leon Grapenthin Aug 19 '15 at 20:47