Reputation: 2334
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
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
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
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
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