Reputation: 7728
In Python I can do this:
animals = ['dog', 'cat', 'bird']
for i, animal in enumerate(animals):
print i, animal
Which outputs:
0 dog
1 cat
2 bird
How would I accomplish the same thing in Clojure? I considered using a list comprehension like this:
(println
(let [animals ["dog" "cat" "bird"]]
(for [i (range (count animals))
animal animals]
(format "%d %d\n" i animal))))
But this prints out every combination of number and animal. I'm guessing there is a simple and elegant way to do this but I'm not seeing it.
Upvotes: 27
Views: 9633
Reputation: 22684
Yet another option is to use reduce-kv
, which pairs the elements of a vector with their indexes.
Thus,
(reduce-kv #(println %2 %3) nil ["dog" "cat" "bird"])
or perhaps the slightly more explicit
(reduce-kv (fn [_ i animal] (println i animal)) nil ["dog" "cat" "bird"])
I wouldn’t pick this solution over the one with doseq
here, but it is good to be aware of this specialisation for vectors in reduce-kv
.
Upvotes: 1
Reputation: 5877
map-indexed
looks right but: Do we really need all of the doseq
and deconstructing args stuff in the other answers?
(map-indexed println ["dog", "cat", "bird"])
EDIT:
as noted by @gits this works in the REPL but doesn't respect that clojure is lazy by default. dorun
seems to be the closest among doseq
, doall
and doseq
for this. doseq
, howver, seems to be the idiomatic favorite here.
(dorun (map-indexed println ["dog", "cat", "bird"]))
Upvotes: 5
Reputation: 17299
There is map-indexed
in core as of 1.2.
Your example would be:
(doseq [[i animal] (map-indexed vector ["dog" "cat" "bird"])]
(println i animal))
Upvotes: 46
Reputation: 3176
Use indexed from clojure.contrib.seq:
Usage: (indexed s)
Returns a lazy sequence of [index, item] pairs, where items come
from 's' and indexes count up from zero.
(indexed '(a b c d)) => ([0 a] [1 b] [2 c] [3 d]
For your example this is
(require 'clojure.contrib.seq)
(doseq [[i animal] (clojure.contrib.seq/indexed ["dog", "cat", "bird"])]
(println i animal))
Upvotes: 8
Reputation: 29189
Quick solution:
(let [animals ["dog", "cat", "bird"]]
(map vector (range) animals))
Or, if you want to wrap it in a function:
(defn enum [s]
(map vector (range) s))
(doseq [[i animal] (enum ["dog", "cat", "bird"])]
(println i animal))
What happens here is the function vector is applied to each element in both sequences, and the result is collected in a lazy collection.
Go ahead, try it in your repl.
Upvotes: 9