4

When I iterate over vector in Reagent, like this:

(for [item ["rattata" "pidgey" "spearow"]]
  [:li item])])

I would like to get index of a specific item - like this:

  [:li item index]

I'm not asking about general clojure 'for', because another way to iterate over vector will satisfy me as well.

Simon Perepelitsa
  • 20,350
  • 8
  • 55
  • 74
Oskar Skuteli
  • 648
  • 6
  • 16
  • 1
    `map-indexed` or `map` over two collections where one of them is a range – Chris Murphy Aug 08 '16 at 12:08
  • Thanks. So is there any reason to use 'for' in this type of scenario? map seems more concise: `(map child-component-function collection)` versus: `(for [item collection] (child-component-function item))` – Oskar Skuteli Aug 08 '16 at 15:05

3 Answers3

8

This is actually a general Clojure question, rather than specific to Reagent, but there are a few way you could do this.

You can approach it similarly to your current code with something like

(def items ["rattata" "pidgey" "spearow"])
(for [index (range (count items))]
  [:li (get items index) index])

You could also use map-indexed

(doall (map-indexed (fn [index item] [:li index item]) items))

The doall in this case is for Reagent, as map and friends return lazy lists that can interfere with Reagent (it will print a warning to the console if you forget it).

Kolja
  • 2,667
  • 1
  • 30
  • 29
Walton Hoops
  • 864
  • 4
  • 14
  • I'm asking about Reagent, because all the iterations in examples on their page use 'for'. Actually I would find them more readable with 'map' or 'map-indexed', and it works, so I'm wondering why do they 'for'. Still very new to Clojure and I'm afraid I'm missing something here. – Oskar Skuteli Aug 08 '16 at 14:30
  • After some investigation: map-indexed is ok but does not instantiate react component and does not allow dereferencing atoms inside function. When used with doall it lets you dereference atom, but still doesn't create component. for is the way to go, thought it should be [index (range (count items))] – Oskar Skuteli Sep 22 '16 at 14:46
  • 1
    Another good option is `(map-indexed (fn [index item] ^{:key index}[component index item]) items)` – Oskar Skuteli Sep 22 '16 at 14:59
4

You can also combine map-indexed with for-loop:

(for [[index item] (map-indexed vector items)]
  [:li item index])])

; vector function is a shorthand:
(for [[index item] (map-indexed (fn [index item] [index item]) items)]
  [:li item index])])
Simon Perepelitsa
  • 20,350
  • 8
  • 55
  • 74
1

You could treat yourself to a for-indexed macro:

(defmacro for-indexed [[item index coll] & body]
  `(for [i# (range (count ~coll))] 
     (let [~item (nth ~coll i#)
           ~index i#]
       ~@body)))
  
(for-indexed [item index ["rattata" "pidgey" "spearow"]]
  [:li item index])

;; => ([:li "rattata" 0] [:li "pidgey" 1] [:li "spearow" 2])

Alternatively, don't pass index in the binding and have for-indexed return [index item] vectors like this:

(defmacro for-indexed [[item coll] & body]
  `(for [i# (range (count ~coll))] 
     (let [~item [i# (nth ~coll i#)]]
       ~@body)))

(for-indexed [item ["rattata" "pidgey" "spearow"]]
  [:li item])

;; => ([:li [0 "rattata"]] [:li [1 "pidgey"]] [:li [2 "spearow"]])
Kolja
  • 2,667
  • 1
  • 30
  • 29