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.

davidscolgan
  • 7,508
  • 9
  • 59
  • 78

5 Answers5

42

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))
kotarak
  • 17,099
  • 2
  • 49
  • 39
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.

Leonel
  • 28,541
  • 26
  • 76
  • 103
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))
ordnungswidrig
  • 3,140
  • 1
  • 18
  • 29
  • heh. Take a look at the source code for function indexed: https://github.com/clojure/clojure-contrib/blob/b8d2743d3a89e13fc9deb2844ca2167b34aaa9b6/src/main/clojure/clojure/contrib/seq.clj#L51 – Leonel Nov 16 '10 at 16:39
  • heh. I know. I wonder why you named the function 'enum' in your example, then :-) – ordnungswidrig Nov 16 '10 at 16:42
  • 1
    clojure-contrib is now deprecated – Indra Nov 14 '19 at 14:38
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"]))
Community
  • 1
  • 1
Phil Cooper
  • 5,747
  • 1
  • 25
  • 41
  • 2
    `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`). – glts Mar 12 '16 at 17:12
  • @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. – Phil Cooper Mar 12 '16 at 17:40
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.

glts
  • 21,808
  • 12
  • 73
  • 94