Now let’s try something a little crazier. We’ll parse the same document, but this time we want more than just the href values: we also want to know the depth, relative to the document root, of each link, as well as how many sub-nodes there are in the <a/> . For a given link, we want something like this:

This is the same function as before, less the map ping that extracted the URLs. It is just going to give us the bare list of locations. The rest of the work is coming up with a function to map over this seq .

The get-links function returns a seq of URL strings, but for this we need loc s, so we have to simplify the function:

Step two: ancestor count

Suddenly we realize that each element in the list of link node zippers has access to the entire tree. Even though it feels like we are pulling the tree apart, filtering out everything but the parts that we are interested in, each part is actually one of those grains of sand that contains the entire universe (so to speak).

Now, we all love Clojure’s seq interface, but there is something that can trip you up. Or at least that is what happened to me. I’ve talked a lot about the distinction between zipper space'' and node space”, because, in my experience anyway, this is often the difference between getting it or not.

So when we then start thinking about all of this through the seq interface, it can get a little bit more confusing: we’ve got a seq , but a seq of what?

I expected to get a bunch of nodes. This is such a common pattern: walk a tree, look at the nodes, accumulate data, do something. What is really cool, and possibly confusing, about Clojure zippers, is that often you don’t get nodes, you get locations. That means that you aren’t extracting data, you are locating it in the tree. Each element of the seq has access to the entire tree. Each element that gets returned is still a zipper. (Unless it’s not, of course: functions like zip/children return nodes, not loc s.)

So, given a loc , how do you count its ancestors? Well, you write a function.

( defn ancestor-count " How many ancestors do I have? " [loc] ( count ( take-while zip/up ( iterate zip/up loc))))

This is the same pattern as before, except that we iterate using zip/up , and we keep going up until zip/up returns nil , which is what can only happen at the root node.

Let’s take it for a spin:

( map ancestor-count a-list) ; (4 5 7 9 9 6 6 6 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 11 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 4 )

Looks good to me…​ except wait a second! We can do the same thing in fewer characters using the zipper’s path :