In clojure, how to apply a function to selected elements in a [large] vector

I have a vector v

 (def v [1 2 5 8 4 3 8 9 3]) 

I want to apply the myfn function

 (defn myfn [x] (+ 1 x)) 

for selected elements that have their idx indices

 (def idx [3 5]) 

I saw How to change part of a vector in Clojure? , and that’s not exactly what I need.

Like what you do in MATLAB

 v = [1 2 5 8 9 3]; idx = [3 5]; v(idx) = myfn(v(idx)); 
+8
clojure matlab
source share
4 answers

The vectors in clojure are associative, so you can do something like this: (reduce #(update-in %1 [%2] myfn) v idx)

+9
source share

Updated because I misinterpreted the question.

Here's another solution:

 (apply assoc v (mapcat #(vector % (myfn (v %))) idx)) 

that is, create an argument list of index / new-value pairs for assoc . I think mange solutions are probably better, but.


The original, wrong decision

Do not forget that the vector v itself is a function of its indices. So:

 (map myfn (map v idx)) 

or

 (->> idx (map v) (map myfn)) 

or

 (map (comp myfn v) idx) 

I'm sure there is also a very smart answer involving juxt :)

+3
source share

Do you mention the "big" vector, so you care about performance? You can learn about transients :

 (persistent! (reduce (fn [vi] (assoc! vi (myfn (get vi)))) (transient v) idx)) 

Or, if you prefer a loop style, this does the same:

 (loop [v (transient v), [i & is :as idx] idx] (if (empty? idx) (persistent! v) (recur (assoc! vi (myfn (get vi))) is))) 
+2
source share
 (let [sidx (set idx)] (vec ;(sidx i) (map-indexed (fn [ix] (if (contains? sidx i) (myfn x) x)) v))) 
0
source share

All Articles