niloofar
niloofar

Reputation: 2334

Clojure For loop index numbers

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.

Upvotes: 1

Views: 320

Answers (4)

triplej
triplej

Reputation: 299

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

Upvotes: -1

Alan Thompson
Alan Thompson

Reputation: 29958

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

Upvotes: 0

Pouya Abbassi
Pouya Abbassi

Reputation: 422

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

Upvotes: 1

Martin Půda
Martin Půda

Reputation: 7568

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)

Upvotes: 6

Related Questions