Clojure For loop index numbers - for-loop

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.

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)

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

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

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

Related

I'm trying to understand the syntax of this cartesian-product function in Clojure

Here's some code for a cartesian product, it can be two lists, two vectors, or any number of combinations of the two. I'd really appreciate help with the second, fourth, and final lines, explaining what each line is doing
(defn cartesian-product ;function name definition
([] '(())) ;need help understanding this
([xs & more] ; at least two variables, xs is one of them
(mapcat #(map (partial cons %) ;mapcat means a create a concatenated map of the following
;still trying to figure out partial, but cons takes a
;variable and puts it in front of a sequence
(apply cartesian-product more)) ; this is the sequence that is mapped
; using (partial cons %)
xs))) ;not sure what this is here for
Here is a reworked version that illustrates what is going on (and how):
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
;----------------------------------------------------------------------------
; Lesson: how map & mapcat work
(defn dup [x]
"Return 2 of the arg in a vector"
[x x])
(dotest
(let [nums [0 1 2]]
(is= (mapv inc nums) [1 2 3])
(is= (mapv dup nums) [[0 0] ; like a matrix, 2-D
[1 1]
[2 2]])
; mapcat glues together the inner "row" vectors. So the result is 1-D instead of 2-D
(is= (mapcat dup nums) [0 0 1 1 2 2])))
then the reworked code
;----------------------------------------------------------------------------
(def empty-matrix [[]]) ; 0 rows, 0 cols
(defn cartesian-product ;function name definition
"When called with 1 or more sequences, returns a list of all possible combinations
of one item from each collection"
([] ; if called with no args
empty-matrix) ; return an empty matrix
; if called with 1 or more args,
([xs ; first arg is named `xs` (i.e. plural for x values)
& more] ; all other args are wrapped in a list named `more`
(let [recursion-result (apply cartesian-product more) ; get cartesian prod of sequences 2..N
inner-fn (fn [arg] (map ; for each recursion-result
(partial cons arg) ; glue arg to the front of it
recursion-result))
; for each item in the first sequence (xs), glue to front of
; each recursion result and then convert 2D->1D
output (mapcat inner-fn xs)]
output)))
and some unit tests to show it in action
(dotest
(is= (cartesian-product [1 2 3]) [[1] [2] [3]])
(is= (cartesian-product [1 2 3] [:a :b])
[[1 :a]
[1 :b]
[2 :a]
[2 :b]
[3 :a]
[3 :b]])
(is= (cartesian-product [1 2 3] [:a :b] ["apple" "pea"])
[[1 :a "apple"]
[1 :a "pea"]
[1 :b "apple"]
[1 :b "pea"]
[2 :a "apple"]
[2 :a "pea"]
[2 :b "apple"]
[2 :b "pea"]
[3 :a "apple"]
[3 :a "pea"]
[3 :b "apple"]
[3 :b "pea"]]))

Convert vector of strings into hash-map in Clojure

I have the following data structure:
["a 1" "b 2" "c 3"]
How can I transform that into a hash-map?
I want the following data structure:
{:a 1 :b 2 :c 3}
Use clojure.string/split and then use keyword and Integer/parseInt:
(->> ["a 1" "b 2" "c 3"]
(map #(clojure.string/split % #" "))
(map (fn [[k v]] [(keyword k) (Integer/parseInt v)]))
(into {}))
=> {:a 1, :b 2, :c 3}
and one more :)
(->> ["a 1" "b 2" "c 3"]
(clojure.pprint/cl-format nil "{~{:~a ~}}")
clojure.edn/read-string)
;;=> {:a 1, :b 2, :c 3}
(into {}
(map #(clojure.edn/read-string (str "[:" % "]")))
["a 1" "b 2" "c 3"])
;; => {:a 1, :b 2, :c 3}
(def x ["a 1" "b 2" "c 3"])
(clojure.edn/read-string (str "{:" (clojure.string/join " :" x) "}"))
;;=> {:a 1, :b 2, :c 3}

Calling up rand-int into a variable

I need to create random numbers and store them into a variable for multiple calculations and for different calls.
(defn position
(def x (rand-int 2147483647))
(def y (rand-int 2147483647))
(def z (rand-int 2147483647))
)
What I want to do is calling this function in a loop, do calculations with it and store this away.
Anyone could help, please? There is probably a better way.
If you want an arbitrary number of things, you need to use an arbitrarily sized data structure. In this case you can probably use the function repeatedly:
(repeatedly 5 #(rand-int 2147483647))
In this example we take 5 elements (you can change to as many as you need) from repeatedly running the anonymous no-arguments function #(rand-int 2147483647) which is what you seem to need.
To generate an infinite lazy sequence of random ints you can use:
(repeatedly #(rand-int 2147483647))
To generate many positions you can also use repeatedly:
(defn rand-position []
{:x (rand-int 2147483647)
:y (rand-int 2147483647)
:z (rand-int 2147483647)})
(def positions (repeatedly rand-position))
(take 5 positions) ;; will generate 5 random positions, each represented as a map
With functional programming, I won't suggest you go down the path of defining global variables (using def). It is better to design your functions to operate on some data structure and returns the same or yet other data structure. In your case, the data-structure is called position. Given it is not a primitive (like integer), you have to decide how to model it. With Clojure, you can either pick a vector or a map for it then all your functions have to follow. Here is how I would go through this development process:
Warning: long reply ahead...
Prologue
(require '[clojure.spec.alpha :as s])
(require '[clojure.spec.gen.alpha :as gen])
(def gen-samples
"A function to create a generator, and to generate
some samples from it in one step"
(comp gen/sample s/gen))
Spec out your data structure
;; dimension is an integer between 0 to 2147483647
(s/def ::dim (s/int-in 0 2147483647))
(gen-samples ::dim)
;; => (1 0 2 1 0 0 2 0 0 27)
;; Option 1: position as a collection of 3 dimensions
(s/def ::position (s/coll-of ::dim :count 3))
(gen-samples ::position)
;; => ([0 0 0] [0 0 0] [1 0 0] [0 1 1] [1 0 1] [3 2 1] [26 1 0] [7 1 1] [6 24 1] [2 0 21])
;; Option 2: position as a map - with x,y,z as keys and with dimension as values
(s/def ::x ::dim)
(s/def ::y ::dim)
(s/def ::z ::dim)
(s/def ::position (s/keys :req-un [::x ::y ::z]))
(gen-samples ::position)
;; => ({:x 1, :y 1, :z 0} {:x 0, :y 0, :z 0} {:x 1, :y 2, :z 1} {:x 1, :y 2, :z 0} {:x 2, :y 2, :z 5} {:x 4, :y 1, :z 13} {:x 4, :y 8, :z 7} {:x 2, :y 5, :z 10} {:x 22, :y 3, :z 4} {:x 124, :y 1, :z 8})
Assuming you take option 2, now spec out your function
;; in this case, move-east is a function which takes a position
;; and returns another position - with x-dimension of the
;; new position always greater than the old one
(s/fdef move-east
:args (s/cat :pos ::position)
:ret ::position
:fn #(> (-> % :ret :x) (-> % :args :pos :x)))
Implementation - the easy part
(defn move-east [pos]
(update pos :x inc))
Some manual test
(-> ::position gen-samples first)
;; => {:x 1, :y 1, :z 0}
(move-east *1)
;; => {:x 2, :y 1, :z 0}
Auto test based on the spec
(require '[clojure.spec.test.alpha :as stest])
(-> `move-east
stest/check
stest/summarize-results)
;; => {:total 1, :check-passed 1}
;; what if my function is wrong?
(defn move-east [pos]
(update pos :x dec))
(-> `move-east
stest/check
stest/summarize-results)
;; => {:total 1, :check-failed 1}
;; what is wrong?
(-> `move-east
stest/check
first
stest/abbrev-result)
;; which basically returns a result like below...
;; showing return x is -1 and hence fails the ::dim spec
{:clojure.spec.alpha/problems
({:path [:ret :x],
:pred
(clojure.core/fn
[%]
(clojure.spec.alpha/int-in-range? 0 2147483647 %)),
:val -1,
:via [:play/dim],
:in [:x]}),
:clojure.spec.alpha/failure :check-failed}

Clojure sort map over value

I'm trying to sort a map over the values.
The input-map looks like:
{:Blabla 1, :foo 1, :bla-bla 1, :Bla 2, :bla/bla 1, :bla 4, :blub 2, :hello 1, :Foo 2}
The output should look like:
{:bla 4 :Bla 2 :blub 2 :Foo 2 :Blabla 1 :bla-bla 1 :bla/bla 1 :foo 1 :hello 1}
I used sorted-map-by like in the documentation here:
http://clojuredocs.org/clojure.core/sorted-map-by
(defn sort-keyword-list [texts]
(let [results (word-counts texts)]
;results is now {:Blabla 1, :foo 1, :bla-bla 1, :Bla 2, :bla/bla 1, :bla 4, :blub 2, :hello 1, :Foo 2}
(into (sorted-map-by (fn [key1 key2]
(compare [(get results key2) key2]
[(get results key1) key1])))
results))
)
Well I found out that this solution works only if the keywords have no special characters like "/" or "-" inside. Is this a known bug?
So how can I sort a map by values quickly without writing a own and slowly sort-algorithm?
In my Clojure 1.6.0 REPL, the code in the question already sorts by value:
user=> (into (sorted-map-by (fn [key1 key2]
(compare [(get x key2) key2]
[(get x key1) key1])))
x)
{:bla 4, :blub 2, :Foo 2, :Bla 2, :bla/bla 1, :hello 1, :foo 1, :bla-bla 1, :Blabla 1}
If you want entries with the same value to be sorted by key, you need to stringify the keys. Here's why:
user=> x
{:bla-bla 1, :Blabla 1, :bla/bla 1, :hello 1, :bla 4, :foo 1, :Bla 2, :Foo 2, :blub 2}
user=> (sort (keys x))
(:Bla :Blabla :Foo :bla :bla-bla :blub :foo :hello :bla/bla)
user=> (sort (map str (keys x)))
(":Bla" ":Blabla" ":Foo" ":bla" ":bla-bla" ":bla/bla" ":blub" ":foo" ":hello")
Here is a solution based on the suggestion by #user100464 with explicit considerations of comparison of keys, when the values are the same.
Note: I choose to sort decreasingly by reversing the order of the arguments to the comparisons: (compare (x k2) (x k1)) and (compare k2 k1).
(defn sort-by-value-then-key [x]
(into (sorted-map-by (fn [k1, k2]
(let [v_c (compare (x k2) (x k1))]
(if (= 0 v_c)
(compare k2 k1)))))
x))
One may customize at (compare k2 k1) to implement more elaborate key comparison.

Clojure: I have many sorted maps and want to reduce in order all there values a super maps of keys -> vector

I have seen this but can't work out how to apply it (no pun intended) to my situation.
I have a sorted list of maps like this: (note there can be more than two keys in the map)
({name1 3, name2 7}, {name1 35, name2 7}, {name1 0, name2 3})
What I am after is this data structure afterwards:
({:name1 [3,35,0]}, {:name2 [7,7,3]})
Ive been struggling with this for a while and cant seem to get anywhere near.
Caveats: The data must stay sorted and I have N keywords not just two.
I'd go for merge-with with some preprocessing added:
(def maps [{:a :b :e :f} {:a :c} {:a :d :e :g}])
(apply merge-with concat (for [m maps [k v] m] {k [v]}))
>>> {:e (:f :g), :a (:b :c :d)}
I think the function you want is merge-with:
user=> (def x {:a 1 :b 2})
user=> (def y {:a 3 :b 4})
user=> (merge-with vector x y)
{:a [1 3], :b [2 4]}
user=>
user=> (def z {:a 5 :b 6 :c 7})
user=> (merge-with vector x y z)
{:a [[1 3] 5], :c 7, :b [[2 4] 6]} ; oops
user=> (merge-with #(vec (flatten (vector %1 %2))) x y z)
{:a [1 3 5] :b [2 4 6] :c 7}
user=>
This is my attempt at the problem. It is not as elegant as Rafal's solution.
(def maps [{:a :b :e :f} {:a :c} {:a :d :e :g}])
(apply merge-with #(if (list? %1) (conj %1 %2) (list %1 %2)) maps)
>>> {:a (:d :b :c), :e (:f :g)}

Resources