1

How can I have the index of this for loop in this block of code:

(def numbers [:one :two :three :four :five])
(def colors  [:green :red :blue :pink :yellow])
(def letters [:A :B :C :D :E])

(for [x numbers
      i colors
      j letters]
    (println (str x " - " i " - " j)))

This code will print 125 lines and I want to have the index number with each line.

4 Answers 4

6

You need map-indexed:

(def numbers [:one :two :three :four :five])
(def colors  [:green :red :blue :pink :yellow])
(def letters [:A :B :C :D :E])

(->> (for [x numbers
           i colors
           j letters]
       (str x " - " i " - " j))
     (map-indexed (fn [index value] 
                    (str "Index: " index " : " value)))
     (run! println))

Shorter version of (fn [index value] ...):

(map-indexed #(str "Index: " %1" : " %2))

Also consider calling one println with one long string instead of calling 125x println, it seems to be faster:

(->> (for [x numbers
           i colors
           j letters]
       (str x " - " i " - " j))
     (map-indexed #(str "Index: " %1 " : " %2 "\n"))
     str/join
     println)

(str/join is clojure.string/join)

Sign up to request clarification or add additional context in comments.

1 Comment

You can make the string interpolation cheaper by doing it all at once. Instead of returning a string from the for loop, return a function, and call that function with the index: (->> (for [x numbers, i colors, j letters] (fn [ix] (str "Index: " index ": " x " - " i " - " j))) (map-indexed (fn [ix f] (f ix)) ...)
1

Using atoms are discouraged in Clojure, but I think this is the simplest way:

(let [index (atom 0)]
  (for [x numbers
        i colors
        j letters]
    (do (swap! index inc)
        (println (str @index ": " x " - " i " - " j)))))

1 Comment

For returns sequence with 125x nil, doseq is better. And swap! returns a new value of atom, so you can do (let [index (atom 0)] (doseq [x numbers i colors j letters] (println (str (swap! index inc) ": " x " - " i " - " j)))). The simplest, a little bit slow, but also suitable.
0

There are many options for this. Here are a few:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test)
  (:require
    [clojure.string :as str]
    [tupelo.core :as t]
    ))


(def numbers [:one :two :three :four :five])
(def colors [:green :red :blue :pink :yellow])
(def letters [:A :B :C :D :E])

(verify
  ; The tupelo.core/indexed function will add a 0-based index to each element in a sequence
  (is= (t/indexed [:a :b :c])
    [[0 :a]
     [1 :b]
     [2 :c]]))

The tupelo.core/indexed function will add a 0-based index to each element in a sequence, which is often handy.

(verify
  ; tupelo.core/map-let allows you to assign a local name to each item from multiple sequences
  ; but behaves like `map`, not `for`
  (let [vecs (t/map-let [n numbers
                         c colors
                         l letters]
               [n c l]) ; return a vector containing each item in sequence
        ]
    (is= vecs
      [[:one :green :A]
       [:two :red :B]
       [:three :blue :C]
       [:four :pink :D]
       [:five :yellow :E]])))

Using t/map-let allows you to give a local name to the elements of each sequence, but does not create the cartesian product like with for.

(verify
  (t/let-spy-pretty ; or just `let` to suppress the "spy" printing
    [
     ; using (mapv vector ...) will also place each triple into a vector
     vecs     (mapv vector numbers colors letters)

     strs     (mapv #(str/join " - " %) vecs)
     strs-idx (t/indexed strs)
     lines    (t/forv [[the-idx the-str] strs-idx]
                (str the-idx ": " the-str))]
    (is= vecs
      [[:one :green :A]
       [:two :red :B]
       [:three :blue :C]
       [:four :pink :D]
       [:five :yellow :E]])
    (is= strs
      [":one - :green - :A"
       ":two - :red - :B"
       ":three - :blue - :C"
       ":four - :pink - :D"
       ":five - :yellow - :E"])
    (is= strs-idx
      [[0 ":one - :green - :A"]
       [1 ":two - :red - :B"]
       [2 ":three - :blue - :C"]
       [3 ":four - :pink - :D"]
       [4 ":five - :yellow - :E"]])
    (is= lines
      ["0: :one - :green - :A"
       "1: :two - :red - :B"
       "2: :three - :blue - :C"
       "3: :four - :pink - :D"
       "4: :five - :yellow - :E"])))

Comments

-1

One option is to use range. (range 125) is all the integers 0 to 124 inclusive.

(def numbers [:one :two :three :four :five])
(def colors  [:green :red :blue :pink :yellow])
(def letters [:A :B :C :D :E])

(for [x numbers
      i colors
      j letters
      n (range (* x i j))]
    (println (str x " - " i " - " j " line " n)))

1 Comment

Adding n like this doesn't index the existing product of elements, it merely multiplies by another component. In this case, (* x i j) is also meaningless, so it just prints "line 0" at the end of each line.

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.