Clojure. list of keywords to dict - clojure

Could you please tell me how can I make the dict form the list?
E.g. I have the list [3 4 5] and :value keyword.
I need to create the following dict:
{{:constant_keyword "constant", :value 5 } {:constant_keyword "constant", :value 4} {:constant_keyword "constant", :value 3}}?
I know how to make a constant value:
(def const-dict (take (count [my-list]) (repeat {:column "type"})))
But I do not know who to do it with a parameter like the item of an array:
(take (count [my_list]) :value) - doesn't work, I can't create list of keywords and then zipmap it to another list.
How can I do it?

(map #(assoc {:constant_keyword "constant"} :value %) [3 4 5])

Related

Map nested vector of array maps

I've been trying to map the nested values of a map within a vector into a vector of vectors without success.
The data I have is like this:
[{:country {:name "chile", :id 1},
:subcountries [{:name "talca", :id 2}
{:name "concepcion", :id 3}
{:name "puerto montt", :id 4}]}
{:country {:name "united states", :id 5},
:subcountries [{:name "boston", :id 6}
{:name "texas", :id 7}]}]
While the code I've been playing with vaguely returns an approximation of what I'm trying to get as a result:
(map
(fn [x]
(let [{{id :id name :name} :country
subcountries :subcountries} x]
[id
name
(map (fn [y] (let [{yid :id yname :yname} y] [yid yname])))]))
data)
The result I'm receiving with that is something pretty odd, since the vector I'd want to have is just a function:
([1 "chile" #function[clojure.core/map/fn--5862]]
[5 "united states" #function[clojure.core/map/fn--5862]])
What am I doing wrong?
Expected output should be something like:
[[["chile" 1] ["talca" 2] ["concepcion" 3] ["puerto montt" 4]]
[["united states" 5] ["boston" 6] ["texas" 7]]]
The reason you're seeing the function in your vector output is that your inner map wasn't applying the function to any data structure, so it was returning a transducer.
Here I've updated the inner map to map the function to the subcountries, which I assume was your intent. (There was also a tiny typo, you had yname :yname instead of yname :name)
(defn f [data]
(mapv
(fn [x]
(let [{{id :id name :name} :country
subcountries :subcountries} x]
[id
name
(mapv (fn [y] (let [{yid :id yname :name} y] [yid yname])) subcountries)]))
data))
Not sure if this is exactly your desired output, since you said "something like...". If not, let us know if you need more help getting it the rest of the way there.
> (f data)
[[1 "chile" [[2 "talca"] [3 "concepcion"] [4 "puerto montt"]]]
[5 "united states" [[6 "boston"] [7 "texas"]]]]
This might be a more "Clojurey" way to do it:
(defn countries->vecs [data]
(let [->pair (juxt :name :id)
map->pairs (fn [{:keys [country subcountries]}]
(apply vector (->pair country)
(map ->pair subcountries)))]
(mapv map->pairs data)))

Map from list of maps

My problem is next, i have list of maps, for example:
({:id 1 :request-count 10 ..<another key-value pair>..}
{:id 2 :request-count 15 ..<another key-value pair>..}
...)
Need create map with records in which, key is value of 'id' and value is value of 'request-count', for each map from prev example, like:
{1 10
2 15
...}
I know how to do this. My question is - standard library have function for achieve this? Or maybe i can achiev this with combination few function, without 'reduce'?
Use the juxt function to generate a sequence of pairs, and then toss them into a map:
(into {} (map (juxt :id :request-count) data))
Example:
user=> (def data [{:id 1 :request-count 10 :abc 1}
#_=> {:id 2 :request-count 15 :def 2}
#_=> {:id 3 :request-count 20 :ghi 3}])
#'user/data
user=> (into {} (map (juxt :id :request-count) data))
{1 10, 2 15, 3 20}
Be aware that if there is more than one map in data with the same :id, then the last one encountered by map will be the one that survives in the output map.
I would do it like so:
(def data
[{:id 1 :request-count 10}
{:id 2 :request-count 15}] )
(defn make-id-req-map [map-seq]
(vec (for [curr-map map-seq]
(let [{:keys [id request-count]} curr-map]
{id request-count}))))
With result:
(make-id-req-map data) => [{1 10} {2 15}]
Note: while you could combine the map destructuring into the for statement, I usually like to label the intermediate values as described in Martin Fowler's refactoring "Introduce Explaining Variable".

Convert map of list into list of maps (i.e. rows to colums)

I have the following data structure in Clojure
{:a [1 2 3]
:b [4 5 6]
:c [7 8 9]}
And I'd like to convert it into something like
[{:a 1 :b 4 :c 7}
{:a 2 :b 5 :c 8}
{:a 3 :b 6 :c 9}]
At the moment I'm kinda stumped as to how to do this.
In Clojure you can never guarantee the order of keys in maps after transformations. They're indexed by key, not by order.
Vectors are, however. And with get-in you can do a lookup on position with a vector of coordinates .
=> (def mat
[[1 2 3]
[4 5 6]
[7 8 9]])
=> (defn transpose
[m]
(apply mapv vector m))
=> (get-in (transpose mat) [1 2])
8
Got it:
(defn transpose-lists [x]
(map (fn [m] (zipmap (keys x) m)) (apply map vector (vals x))))
Unfortunately it doesn't preserve order of the keys.
If anyone has a better solution then of course I'd like to hear it!

Idiomatic Clojure way to find most frequent items in a seq

Given a sequence of items I want to find the n most frequent items, in descending order of frequency. So for example I would like this unit test to pass:
(fact "can find 2 most common items in a sequence"
(most-frequent-n 2 ["a" "bb" "a" "x" "bb" "ccc" "dddd" "dddd" "bb" "dddd" "bb"])
=>
'("bb" "dddd"))
I am fairly new to Clojure and still trying to get to grip with the standard library. Here is what I came up with:
(defn- sort-by-val [s] (sort-by val s))
(defn- first-elements [pairs] (map #(get % 0) pairs))
(defn most-frequent-n [n items]
"return the most common n items, e.g.
(most-frequent-n 2 [:a :b :a :d :x :b :c :d :d :b :d :b]) =>
=> (:d :b)"
(take n (->
items ; [:a :b :a :d :x :b :c :d :d :b :d :b]
frequencies ; {:a 2, :b 4, :d 4, :x 1, :c 1}
seq ; ([:a 2] [:b 4] [:d 4] [:x 1] [:c 1])
sort-by-val ; ([:x 1] [:c 1] [:a 2] [:b 4] [:d 4])
reverse ; ([:d 4] [:b 4] [:a 2] [:c 1] [:x 1])
first-elements))) ; (:d :b :a :c :x)
However this seems like a complicated chain of functions to do a fairly common operation. Is there a more elegant or more idiomatic (or more efficient) way to do this?
As you have discovered, typically you would use a combination of sort-by and frequencies to get a frequency-sorted list.
(sort-by val (frequencies ["a" "bb" "a" "x" "bb" "ccc" "dddd" "dddd" "bb" "dddd" "bb"]))
=> (["x" 1] ["ccc" 1] ["a" 2] ["dddd" 3] ["bb" 4])
Then you can manipulate this fairly easily to get the lowest / highest frequency items. Perhaps something like:
(defn most-frequent-n [n items]
(->> items
frequencies
(sort-by val)
reverse
(take n)
(map first)))
Which again is pretty similar to your solution (apart from that you don't need the helper functions with clever use of the ->> macro).
So overall I think your solution is pretty good. Don't worry about the chain of functions - it's actually a very short solution for what is logically quite a complicated concept. Try coding the same thing in C# / Java and you will see what I mean......

Clojure's defrecord - how to use it?

I'm attempting to create my own immutable datatype/methods with defrecord in Clojure. The goal is to have a datatype that I can create instances of, and then call its methods to return a new copy of itself with mutated variables. Say a and b are vectors. I'd like to update a value in both and return a new copy of the entire structure with those vectors updated. This obviously doesn't compile, I'm just trying to get my ideas across.
(defrecord MyType [a b]
(constructor [N]
; I'd like to build an initial instance, creating a and b as vectors of length N
)
(mutate-and-return []
; I'd like to mutate (assoc the vectors) and return the new structure, a and b modified
)
)
I'd like to call the constructor and then the mutator as many times as I'd like (there are other functions that don't mutate, but I don't want to make it more complex for the question).
Alternatively, if this is not idiomatic Clojure, how are you supposed to do something like this?
Here's how you define your record:
(defrecord MyType [a b])
Note that in Clojure you don't typically define "methods" within your record type itself (the exception is if you want to directly implement a Java interface or a protocol).
A basic constructor (prefixed with ->) gets generated automatically for free:
(def foo (->MyType [1 2 3] [4 5 6]))
foo
=> #user.MyType{:a [1 2 3], :b [4 5 6]}
You can then write more sophisticated constructor functions that use this, e.g.
(defn mytype-with-length [n]
(let [a (vec (range n))
b (vec (range n))]
(->MyType a b)))
(mytype-with-length 3)
=> #user.MyType{:a [0 1 2], :b [0 1 2]}
And "mutate-and-return" also comes for free - you can just use assoc:
(assoc foo :b [7 8 9])
=> user.MyType{:a [1 2 3], :b [7 8 9]}
Clojure defrecord example:
;;define Address record
(defrecord Address [city state])
;;define Person record
(defrecord Person [firstname lastname ^Address address])
;;buid the constructor
(defn make-person ([fname lname city state]
(->Person fname lname (->Address city state))))
;;create a person
(def person1 (make-person "John" "Doe" "LA" "CA"))
;;retrieve values
(:firstname person1)
(:city (:address person1))
Clojure allows you to create records, which are custom, maplike data types.
They’re maplike in that they associate keys with values, you can look up their values the same way you can with maps, and they’re immutable like maps.
(defrecord Person [last first address])
;=> core.Person
(defrecord Ad [street city zip])
;=> core.Ad
(def p1 (Person. "Jhon" "Mick"
(Ad. "US187956" "NY" 3369)))
;=> #'core/p1
(update-in p1 [:address :zip] inc)
;=> #core.Person{:last "Jhon", :first "Mick", :address #playsync.core.Ad{:street "US187956", :city "NY", :zip 3370}}
(assoc p1 :last "Adam")
;=> #core.Person{:last "Adam", :first "Mick", :address #playsync.core.Ad{:street "US187956", :city "NY", :zip 3370}}