Iterate over clojure pairs (loop)

I have a sequence of pairs-pairs like this (currently about 17,000 pairs)

(def myseq '({:name "Peter" :rank 2222} {:name "Anna" :rank 111})) 

I want to filter specific pairs in a new sequence using

 (filter (fn [x] (> x 222)) (:rank (first myseq))) 

I try to loop through the loop this way, but keep getting the death thread. Also, if I use a filter in one collection of cards, it just returns a new sequence, not sure if I need to create it myself?

 (defn remove-lower [number myseq] (loop [i 0] (if (= i (count file)) (println "done") (filter [x] (> x number)) (:rank (first myseq)))) (recur (rest myseq)))) 

Is ending really the most effective way to get a new sequence of pairs?

Best, J

+7
source share
2 answers

No need for loop / repeat here. the filter is already iterating through seq for you:

 (filter (fn [entry] (> (:rank entry) 220)) myseq) 
+8
source

The first thing to know is that (most) data structures in clojure are immutable, and most functions are functional. This means that they have no side effects. In your case, filter does not change the sequence, returns a new one containing only unfiltered elements.

So, to filter myseq you need to do something like:

 (def filtered-seq (filter (fn [x] ...) myseq)) 

The filter will call the function again, binding x to the current filtered item in myseq . That is, for the first time it will be attached to {:name "Peter" :rank 2222} , and then to {:name "Anna" :rank 111} . filtered-seq will contain only those elements for which the function returned. myseq will not be changed!

So, you want to leave only elements with :rank above 222:

 (filter (fn [x] (> (:rank x) 222)) myseq) 

What is it. And another feature of the filter is that it is lazy. That is, the elements in the returned collection are "implemented" (or calculated) only when they are needed.

You do not need to use loop for this, since filter does the job nicely and loop not lazy.

However, your loop does not work as it has several problems:

  • recur is out of loop . In this case, clojure will return to the beginning of the function.
  • you need to build the return value and you need to maintain the "current" element
  • you need to check the final condition correctly

The code may look something like this (untested):

 (defn remove-lower [number myseq] (loop [sq myseq res []] (if (empty? sq) res (let [current (first sq)] (if (> (:rank current) number) (recur (rest sq) (conj res current)) (recur (rest sq) res)))))) 

Please note how:

  • recur now inside the loop
  • res contains the return value, and sq contains the current left sequence
  • each recur passes the new sq and res values ​​for the next iteration
  • sq "shrinks" with each iteration, so the loop will eventually exit if myseq not infinite. Compare this with filter , which handles infinite sequences perfectly.

As you can see, this is harder to read and less general than filter , and also impatient (not lazy).

+6
source

All Articles