27

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.

5 Answers 5

47

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))
Sign up to request clarification or add additional context in comments.

Comments

9

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.

Comments

8

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))

3 Comments

heh. Take a look at the source code for function indexed: github.com/clojure/clojure-contrib/blob/…
heh. I know. I wonder why you named the function 'enum' in your example, then :-)
clojure-contrib is now deprecated
5

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"]))

2 Comments

map-indexed returns a lazy seq. doseq is necessary to ensure side effects happen now, not later (in this case, the printing effected by println).
@git right and thanks, I noticed that but didn't update because I'm still learning and don't know if doseq is the real favorite idiom here.
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.