Reputation: 1
I am new to clojure and can't really wrap my head around adding to a hashmap without using a typical for loop like other languages would. For example, if I have the following code segment:
(def empty-hashmap {})
(def big-hashmap (assoc empty-hashmap 1 2))
how would I iterate through and add 300 separate elements to the big hashmap? In this case I want my code to look something like
(def empty-hashmap {})
(def big-hashmap (assoc empty-hashmap n (abundance n)))
where n is the numbers 1 to 300 and it populates 300 elements into the big hashmap.
Upvotes: 0
Views: 145
Reputation: 4482
Just wanted to add a reduce
code example to @amalloy's answer:
(let [keys [:a :b :c :d :e :f :g]
vals [1 2 3 4 5 6 7]]
(map vector keys vals))
=> ([:a 1] [:b 2] [:c 3] [:d 4] [:e 5] [:f 6] [:g 7])
(let [keys [:a :b :c :d :e :f :g]
vals [1 2 3 4 5 6 7]]
(reduce
(fn [a [k v]] (assoc a k v))
{}
(map vector keys vals)))
=> {:a 1, :b 2, :c 3, :d 4, :e 5, :f 6, :g 7}
This uses reduce
with an "accumulator function". For each iteration, the function is called with the old value of the map and a new key-value pair. It assoc's in the new value and returns a new map with an extra key-value pair. The initial value is provided as the second argument to reduce
(the {}
empty map).
Upvotes: 0
Reputation: 91917
As Alan Thompson says, reduce
is the general purpose tool for iterating over a sequence and accumulating a result. But if you need to make many "independent" changes, as here you associate keys in a map with values that don't depend on anything but the key, there are better tools. map
is the general purpose tool for producing a new sequence based on an old one, and into
is for turning sequences into maps. So, you can write
(into {}
(map (fn [n] [n (abundance n)])
(range 1 301)))
Note that (fn [n] [n (abundance n)])
could also be written (juxt identity abundance)
, though it's up to you which you find clearer.
Personally I don't like writing (map (fn [n] ...))
- usually if you need a (one-argument) lambda, for
is a better tool than map
. The into
/for
pairing is very common for tasks like this:
(into {}
(for [n (range 1 301)]
[n (abundance n)]))
I would not at all recommend using an atom
just for a "more imperative feel". There are good times to use an atom, but beginners don't run into them super quickly, and this isn't one of them.
Upvotes: 3
Reputation: 29958
Reduce is a good option here, starting from my favorite template project:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(verify
(let [result (reduce (fn [accum item]
(assoc accum item (str "item " item)))
{} ; initial empty map
(range 5)) ; list of items to add
]
(is= result
{0 "item 0"
1 "item 1"
2 "item 2"
3 "item 3"
4 "item 4"})))
If you want a more imperative-style solution, you can always accumulate the result into an atom:
(verify
(let [accum (atom {})]
(doseq [idx (range 5)]
(swap! accum
#(assoc % idx (str "item " idx))))
(is= @accum
{0 "item 0"
1 "item 1"
2 "item 2"
3 "item 3"
4 "item 4"})))
Upvotes: -2