Using Duplicate Values in a Clojure Set - clojure

I'm having a problem with sets in clojure where duplicate values are seemingly being removed from a set and it's causing me a real headache.
I have some cards.
(def cards
{
:card1 {:name "Wisp" :type "Monster" :damage 1 :health 1 :cost 0 :ability 0}
:card2 {:name "Spider Tank" :type "Monster" :damage 3 :health 4 :cost 3 :ability 0}
:card3 {:name "Boulder Fist Ogre" :type "Monster" :damage 6 :health 7 :cost 6 :ability 0}
:card4 {:name "Bloodfen Raptor" :type "Monster" :damage 3 :health 2 :cost 2 :ability 0}
:card5 {:name "Chillwind Yeti" :type "Monster" :damage 4 :health 5 :cost 4 :ability 0}
:card6 {:name "Magma Rager" :type "Monster" :damage 5 :health 1 :cost 3 :ability 0}
:card7 {:name "War Golem" :type "Monster" :damage 7 :health 7 :cost 7 :ability 0}
:card8 {:name "Oasis Snapjaw" :type "Monster" :damage 2 :health 7 :cost 4 :ability 0}
:card9 {:name "River Crocolisk" :type "Monster" :damage 2 :health 3 :cost 2 :ability 0}
:card10 {:name "Murloc Raider" :type "Monster" :damage 2 :health 1 :cost 1 :ability 0}
:card11 {:name "Northshire Cleric":type "Monster" :damage 1 :health 3 :cost 1 :ability 2}
}
)
These cards are then part of a set.
(def board1 (set (map cards '(:card3 :card4 :card11 nil nil nil nil))))
When I return this set I want to see the four nil's and three cards. Instead I get:
#{nil {:ability 0, :name "Bloodfen Raptor", :type "Monster", :damage 3, :health 2, :cost 2} {:ability 0, :name "Boulder Fist Ogre", :type "Monster", :damage 6, :health 7, :cost 6} {:ability 2, :name "Northshire Cleric", :type "Monster", :damage 1, :health 3, :cost 1}}
The reason I would like the duplicate values is situations in which I have two of the same card, or multiple nil values. I iterate through these values using a doseq loop that returns an out of bounds exception when duplicates are present.

A Clojure set is by definition unique.
Returns a set of the distinct elements of coll.
https://clojuredocs.org/clojure.core/set
If you need a non-unique set than you can use a vector or a list.
https://clojuredocs.org/clojure.core/vec
https://clojuredocs.org/clojure.core/list

Related

Update vector inside reduce

Given a vector:
(def vec [{:key 1, :value 10, :other "bla"}, {:key 2, :value 13, :other "bla"}, {:key 1, :value 7, :other "bla"}])
I'd like to iterate over each element and update :value with the sum of all :values to that point, so I would have:
[{:key 1, :value 10, :other "bla"}, {:key 2, :value 23, :other "bla"}, {:key 1, :value 30, :other "bla"}])
I've found this for printing the result, but I've tried to change the prn command to update-in, assoc-in in the code below (extracted from the link above) but I didn't work quite well.
(reduce (fn [total {:keys [key value]}]
(let [total (+ total value)]
(prn key total)
total))
0 vec)
I'm new to Clojure, how can I make it work?
If you want to get the running totals then the simplest way is to use reductions:
(reductions (fn [acc ele] (+ acc (:value ele)))
0
[{:key 1, :value 10, :other "bla"}, {:key 2, :value 13, :other "bla"}, {:key 1, :value 7, :other "bla"}])
;; => (0 10 23 30)
As you can see the function you pass to reductions has the same signature as the function you pass to a reduce. It is like you are asking for a reduce to be done every time a new element is reached. Another way of thinking about it is that every time a new accumulator is calculated it is kept, unlike with reduce where the caller only gets to see the result of the last calculation.
And so this is the code that would directly answer your question:
(->> [{:key 1, :value 10, :other "bla"}, {:key 2, :value 13, :other "bla"}, {:key 1, :value 7, :other "bla"}]
(reductions #(update %2 :value + (:value %1))
{:value 0})
next
vec)
;; => [{:key 1, :value 10, :other "bla"} {:key 2, :value 23, :other "bla"} {:key 1, :value 30, :other "bla"}]
You can accumulate the :values thus:
(reductions + (map :value v))
=> (10 23 30)
(I renamed the vector v to avoid tripping over clojure.core/vec.)
Then you can use mapv over assoc:
(let [value-sums (reductions + (map :value v))]
(mapv #(assoc %1 :value %2) v value-sums))
=> [{:key 1, :value 10, :other "bla"} {:key 2, :value 23, :other "bla"} {:key 1, :value 30, :other "bla"}]

Getting values from nested maps

I have a nested map,how can i get values of all :kvota keywords?the result should be - 5.8,3.2,2.25.I tried using select-keys but without any luck...
{:b4f0d011-31a2-4be3-bb8d-037725310207 {:tiket {:3 {:id 13, :par Porto - Zenit, :igra 2, :kvota 5.8}, :2 {:id 12, :par Celtic - Ajax, :igra x, :kvota 3.2}, :1 {:id 11, :par Arsenal - Dortmund, :igra 1, :kvota 2.25}}}}
This will get the values corresponding to each :kvota anywhere in the data structure.
;; Data in quesiton doesn't read as-is, so this is altered slightly.
(def data
{:b4f0d011-31a2-4be3-bb8d-037725310207
{:tiket
{:1 {:kvota 2.25, :par "Arsenal - Dortmund", :igra 1, :id 11}
:3 {:kvota 5.8, :par "Porto - Zenit", :igra 2, :id 13}
:2 {:kvota 3.2, :par "Celtic - Ajax", :igra "x", :id 12}}}})
(keep :kvota (tree-seq map? vals data)) ; (2.25 5.8 3.2)

How can I merge hash-maps that are inside a collection of vectors in clojure?

Given collection of vectors of separated hash-maps
How can I go from :
[[{:a 1} {:b 2} {:c 3}] [{:a 4} {:b 5} {:c 6}] [{:a 7} {:b 8} {:c 9}]]
To:
[[{:a 1 :b 2 :c 3}] [{:a 4 :b 5 :c 6}] [{:a 7 :b 8 :c 9}]]
Thanks you for your answers!
(def coll [[{:a 1} {:b 2} {:c 3}] [{:a 4} {:b 5} {:c 6}] [{:a 7} {:b 8} {:c 9}]])
(mapv (fn [v] [(apply merge v)]) coll)
;; => [[{:a 1 :c 3 :b 2}] [{:a 4 :c 6 :b 5}] [{:a 7 :c 9 :b 8}]]
(def data [[{:a 1} {:b 2} {:c 3}] [{:a 4} {:b 5} {:c 6}] [{:a 7} {:b 8} {:c 9}]])
(mapv #(-> [(into {} %)]) data)

Calculating a value and adding it to a map

I have a list of maps:
(def mylist
[{:id 1 :sub [{:subid 1} {:subid 2}]}
{:id 2 :sub [{:subid 3}]}])
I want to add a new key/value pair to each map element of the list that contains the count of items in :sub:
[{:id 1 :sub [{:subid 1} {:subid 2}] :subcount 2}
{:id 2 :sub [{:subid 3}] :subcount 1}]
How can I do this?
In clojure, "adding to a map" is done with assoc, which returns a new map with the specified value(s) added, and usually if you want to do the same operation on a collection of things, you use the map function.
(defn subcount
"return the number of items in the :sub of m"
[m]
(count (:sub m)))
(defn add-count
"add subcount to the given map"
[m]
(assoc m :subcount (subcount m)))
(defn add-counts
"add subcount to all the objects"
[objects]
(map add-count objects))
(def mylist
[{:id 1 :sub [{:subid 1} {:subid 2}]}
{:id 2 :sub [{:subid 3}]}])
(add-counts mylist)
=> ({:sub [{:subid 1} {:subid 2}], :subcount 2, :id 1} {:sub [{:subid 3}], :subcount 1, :id 2})

How to read a file with test data in with Clojure?

I am writing a piece of code that needs to read in a text file that has data. The text file is in the format:
name 1 4
name 2 4 5
name 3 1 9
I am trying to create a vector of a map in the form [:name Sarah :weight 1 cost :4].
When I try reading the file in with the line-seq reader, it reads each line as an item so the partition is not correct. See repl below:
(let [file-text (line-seq (reader "C://Drugs/myproject/src/myproject/data.txt"))
new-test-items (vec (map #(apply struct item %) (partition 3 file-text)))]
(println file-text)
(println new-test-items))
(sarah 1 1 jason 4 5 nila 3 2 jonas 5 6 judy 8 15 denny 9 14 lis 2 2 )
[{:name sarah 1 1, :weight jason 4 5, :value nila 3 2 } {:name jonas 5 6, :weight judy 8 15, :value denny 9 14}]
I then tried to just take 1 partition, but still the structure is not right.
=> (let [file-text (line-seq (reader "C://Drugs/myproject/src/myproject/data.txt"))
new-test-items (vec (map #(apply struct item %) (partition 1 file-text)))]
(println file-text)
(println new-test-items))
(sarah 1 1 jason 4 5 nila 3 2 jonas 5 6 judy 8 15 denny 9 14 lis 2 2 )
[{:name sarah 1 1, :weight nil, :value nil} {:name jason 4 5, :weight nil, :value nil} {:name nila 3 2 , :weight nil, :value nil} {:name jonas 5 6, :weight nil, :value nil} {:name judy 8 15, :weight nil, :value nil} {:name denny 9 14, :weight nil, :value nil} {:name lis 2 2, :weight nil, :value nil} {:name , :weight nil, :value nil}]
nil
Next I tried to slurp the file, but that is worse:
=> (let [slurp-input (slurp "C://Drugs/myproject/src/myproject/data.txt")
part-items (partition 3 slurp-input)
mapping (vec (map #(apply struct item %) part-items))]
(println slurp-input)
(println part-items)
(println mapping))
sarah 1 1
jason 4 5
nila 3 2
jonas 5 6
judy 8 15
denny 9 14
lis 2 2
((s a r) (a h ) (1 1) (
Please help! This seems like such an easy thing to do in Java, but is killing me in Clojure.
split it into a sequence of lines:
(line-seq (reader "/tmp/data"))
split each of them into a sequence of words
(map #(split % #" ") data)
make a function that takes a vector of one data and turns it into a map with the correct keys
(fn [[name weight cost]]
(hash-map :name name
:weight (Integer/parseInt weight)
:cost (Integer/parseInt cost)))
then nest them back together
(map (fn [[name weight cost]]
(hash-map :name name
:weight (Integer/parseInt weight)
:cost (Integer/parseInt cost)))
(map #(split % #" ") (line-seq (reader "/tmp/data"))))
({:weight 1, :name "name", :cost 4}
{:weight 2, :name "name", :cost 4}
{:weight 3, :name "name", :cost 1})
you can also make this more compact by using zip-map
You are trying to do everything in one place without testing intermediate results. Instead Clojure recommends to decompose task into a number of subtasks - this makes code much more flexible and testable. Here's the code for your task (I assume records in file describe people):
(defn read-lines [filename]
(with-open [rdr (clojure.java.io/reader filename)]
(doall (line-seq rdr))))
(defn make-person [s]
(reduce conj (map hash-map [:name :weight :value] (.split s " "))))
(map make-person (read-lines "/path/to/file"))