0

Suppose I have a list of elements L, a function g, and a list of indices I.

Then, how can I map the function g only to the elements of the list L specified by the indices I?

For instance, if g is the squaring function, L is the list (1 2 3 4 5 6 7) and I is the set of indices (1 3 4), then I should obtain (1 4 3 16 25 6 7), that is the list L in which I squared the elements in positions I.

(The first index is 0, like it is used in the nth function)

I can do it in some way or another, but I was wondering if there is a simple way to do it.

Matteo
  • 539
  • 3
  • 9
  • This is, not quite a duplicate of, but very similar to [How do I filter elements from a sequence based on indexes](https://stackoverflow.com/q/7744656/625403). The approaches there will provide some additional insight. – amalloy Apr 19 '20 at 17:27

4 Answers4

3

Or, without a library, you can just make use of map-indexed:

(def I #{1 3 4})
(def L '(1 2 3 4 5 6 7))

(defn g [n] (* n n))


(map-indexed #(if (I %) (g %2) %2) L))

; or, with explicit parameters

(map-indexed (fn [i n] (if (I i) (g n) n)) L)

; Both produce a lazy-list of (1 4 3 16 25 6 7)

Of course, with better names, this would be a lot more readable.

I have I as a set here instead of a list so lookups can be done efficiently. If you have it as a list originally, you can convert it to a set using set.

Also note, this produces a lazy-list. If you want a different structure, you can use vec for example to force it into a vector afterward.

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
  • contains? on a list is no good. It's not just show, it doesn't work. – amalloy Apr 19 '20 at 17:23
  • @amalloy Whoops. Honestly it's been a good while since I've written Clojure. Apparently I'm starting to lose some fundamental knowledge. That's sad. – Carcigenicate Apr 19 '20 at 17:27
1
(require '[com.rpl.specter :as s])
(use '[plumbing.core])
(s/transform [s/INDEXED-VALS (fn->> first (contains? #{1 3 4})) s/LAST]
             (fn [x] (* x x)) 
             '(1 2 3 4 5 6 7))
akond
  • 15,865
  • 4
  • 35
  • 55
0

I would say, you can do it with a simple map call:

(defn g [x]
  (* x x))

(def L '(1 2 3 4 5 6 7))
(def I '(1 3 4))
(map #(g (nth L %)) I)
;; => (4 16 25)

The mindset here is, for each indexes of I, I lookup the associated value in L and I compute g function over it.

Rozar Fabien
  • 352
  • 2
  • 9
0

Another option is to loop over the desired indexes to change, using assoc to replace items in a vector:

(ns tst.demo.core
  (:use tupelo.core tupelo.test) )

(defn update-idx
  [data txfn idxs]
  (reduce
    (fn [result idx]
      (let [val-orig (get result idx)
            val-new  (txfn val-orig)]
        (assoc result idx val-new)))
    (vec data) ; coerce data to vector
    idxs))

(dotest
  (let [plus100 (fn [x] (+ 100 x))]
    (is= (update-idx (range 10) plus100 [2 3 5 7]))
    [0 1 102 103 4 105 6 107 8 9]))
Alan Thompson
  • 29,276
  • 6
  • 41
  • 48