The idiomatic way is to only update the first element, corresponding to the first in number

I have seq, (def coll '([:a 20] [:b 30] [:c 50] [:d 90]))

I want to iterate through seq and change only the first element matching the predicate.

Predicate (def pred (fn [[ab]] (> b 30)))

(f pred (fn [[ab]] [a (+ b 2)]) coll) => ([:a 20] [:b 30] [:c 52] [:d 90])

f is fn, I want pred to take, and fn is applicable to the first element that corresponds to pred. All other items are unchanged and returned to seq.

What is an idiomatic way to do this?

+4
source share
4 answers

One possible way is to split the collection into split-with , apply the function f to the first element of the second set returned by split-with , and concat elements together again.

 (defn apply-to-first [pred f coll] (let [[ht] (split-with (complement pred) coll)] (concat h (list (f (first t))) (rest t)))) 

Note that the pred function in your example should look something like this:

 (def pred #(> (second %) 30)) 
+5
source

As with most problems, there are several ways to solve it. This is just one of them.

If you are using Clojure 1.5, try:

 (reduce (fn [acc [ab]] (if (pred b) (reduced (concat (:res acc) [[a (+ b 2)]] (rest (:coll acc)))) (assoc acc :res (conj (:res acc) [ab]) :coll (rest (:coll acc))))) {:coll coll :res []} coll) ;; ([:a 20] [:b 30] [:c 52] [:d 90]) 

The key to this algorithm is to use the reduced function (note the "d" function) - it essentially tells reduce stop the iteration and return the result. From the doc line:

 ------------------------- clojure.core/reduced ([x]) Wraps x in a way such that a reduce will terminate with the value x 

The code is a bit accurate, but it should give you the basic idea.

Hope this helps.

+4
source

This function is not difficult to write recursively from scratch. This is not only a good training exercise, but also the best solution: it is as lazy as possible and makes the absolute minimum amount of computation. So far, only one answer to this question is lazy, and once calls pred twice on all elements before the update occurs: once in take-while and once in drop-while , in split-with parts.

 (defn update-first [pred f coll] (lazy-seq (when-let [coll (seq coll)] (if (pred (first coll)) (cons (f (first coll)) (rest coll)) (cons (first coll) (update-first pred f (rest coll))))))) 
+4
source

To make it simple: find the first element, find its index and use it to "update" the element by index:

 (let [e (first (filter pred coll)) ind (.indexOf coll e)] (assoc (vec coll) ind ((fn [[ab]] [a (+ b 2)]) e) )) 

Dominic proposal comment:

 (def pred #(> (second %) 30)) 
0
source

Source: https://habr.com/ru/post/1480091/


All Articles