clojure convert lazy-seq to hash map - clojure

I'm a clojure beginner. How do I create a map from a lazySeq?
(def fields [:name :age :color])
(def values ["joe" 32 "red"])
(def record (interleave fields values))
(def mymap (into {} record)) ;; ????
;; (get mymap :age)
;; 32

To answer your actual question:
(apply hash-map record)
But since you actually already have separate keys and values, I would suggest skipping the interleave step and instead writing
(zipmap fields values)
Or if you have your heart set on into, you could
(into {} (map vector fields values))

This isn't sensible at all, but since the original question wanted to use into with record:
(into {} (map vec (partition 2 record)))

Related

How to transform Clojure map with collections as vals to seq of maps?

I have a map where each key has a collection as value:
{:name ["Wut1" "Wut2"] :desc ["But1" "But2"]}
The value collections can be assumed to have the same number of elements.
How to transform it into a list (or vector) where each element is a map with key being the key from original collection and value being 1 value like:
[{:name "Wut1" :desc "But1"} {:name "Wut2" :desc "But2"}]
It should be said that the number of keys is not known before (so I can't hardcode for :name and :desc)
(fn [m]
(let [ks (keys m)]
(apply map (fn [& attrs]
(zipmap ks attrs))
(vals m))))
Get the list of keys to use to label everything with
Use apply map to "transpose" the list-of-lists in (vals m) from "for each attribute, a list of the values it should have" to "for each object, a list of the attributes it should have".
zipmap that back together with the keys extracted in the first part.
As a general rule apply map vector will always do a transpose operation:
(apply map vector '(["Wut1" "Wut2"] ["But1" "But2"]))
;;=> (["Wut1" "But1"] ["Wut2" "But2"])
With that one trick in hand, we just need to make sure to zipmap each vector object (e.g. ["Wut1" "But1"]) with the keys:
(fn [m]
(->> m
vals
(apply map vector)
(map #(zipmap (keys m) %))))
;;=> ({:name "Wut1", :desc "But1"} {:name "Wut2", :desc "But2"})
Another rule to go by is that there's a problem when you have consecutive maps - really you ought to bring together the map functions, rather than needlessly transferring items from list to list. (This can often be done using comp). See #amalloy's solution for how to avoid double mapping. Also of course the keys function call should not be done repeatedly as here.

clojure: filtering a vector of maps by keys existence and values

I have a vector of maps like this one
(def map1
[{:name "name1"
:field "xxx"}
{:name "name2"
:requires {"element1" 1}}
{:name "name3"
:consumes {"element2" 1 "element3" 4}}])
I'm trying to define a functions that takes in a map like {"element1" 1 "element3" 6} (ie: with n fields, or {}) and fiters the maps in map1, returning only the ones that either have no requires and consumes, or have a lower number associated to them than the one associated with that key in the provided map (if the provided map doesn't have any key like that, it's not returned)
but I'm failing to grasp how to approach the maps recursive loop and filtering
(defn getV [node nodes]
(defn filterType [type nodes]
(filter (fn [x] (if (contains? x type)
false ; filter for key values here
true)) nodes))
(filterType :requires (filterType :consumes nodes)))
There's two ways to look at problems like this: from the outside in or from the inside out. Naming things carefully can really help when working with nested structures. For example, calling a vector of maps map1 may be adding to the confusion.
Starting from the outside, you need a predicate function for filtering the list. This function will take a map as a parameter and will be used by a filter function.
(defn comparisons [m]
...)
(filter comparisons map1)
I'm not sure I understand the comparisons precisely, but there seems to be at least two flavors. The first is looking for maps that do not have :requires or :consumes keys.
(defn no-requires-or-consumes [m]
...)
(defn all-keys-higher-than-values [m]
...)
(defn comparisons [m]
(some #(% m) [no-requires-or-consumes all-keys-higher-than-values]))
Then it's a matter of defining the individual comparison functions
(defn no-requires-or-consumes [m]
(and (not (:requires m)) (not (:consumes m))))
The second is more complicated. It operates on one or two inner maps but the behaviour is the same in both cases so the real implementation can be pushed down another level.
(defn all-keys-higher-than-values [m]
(every? keys-higher-than-values [(:requires m) (:consumes m)]))
The crux of the comparison is looking at the number in the key part of the map vs the value. Pushing the details down a level gives:
(defn keys-higher-than-values [m]
(every? #(>= (number-from-key %) (get m %)) (keys m)))
Note: I chose >= here so that the second entry in the sample data will pass.
That leaves only pulling the number of of key string. how to do that can be found at In Clojure how can I convert a String to a number?
(defn number-from-key [s]
(read-string (re-find #"\d+" s)))
Stringing all these together and running against the example data returns the first and second entries.
Putting everything together:
(defn no-requires-or-consumes [m]
(and (not (:requires m)) (not (:consumes m))))
(defn number-from-key [s]
(read-string (re-find #"\d+" s)))
(defn all-keys-higher-than-values [m]
(every? keys-higher-than-values [(:requires m) (:consumes m)]))
(defn keys-higher-than-values [m]
(every? #(>= (number-from-key %) (get m %)) (keys m)))
(defn comparisons [m]
(some #(% m) [no-requires-or-consumes all-keys-higher-than-values]))
(filter comparisons map1)

Convert list of vars to a name-var map

I'm using Yesql library for Clojure. Its defqueries macro returns a list of Vars representing SQL queries. I want to convert that list into a map of query names to Vars, and have come up with a variant below:
(def main-queries (defqueries "sql/main.sql"))
(def query-map
(apply hash-map
(reduce
(fn [coll query]
(conj coll (->> query meta :name keyword) query))
[] main-queries)))
It seems very inelegant and cumbersome for such an easy task. Can you suggest a shorter and more idiomatic version?
Thanks!
You can create the name -> var mappings and then use into:
(def query-map
(let [pairs (map (fn [v] [(->> v meta :name keyword) v]) main-queries)]
(into {} pairs)))

Assoc-if in Clojure

Is there an "assoc-if" function in the Clojure library? I.e if a value is truthy, update a map with a given key value. I've tried to find something to this effect, but came up lacking.
(defn assoc-if
[m key value]
(if value (assoc m key value) m))
If the goal is to avoid repeating m, you can use conj:
(conj m (when value [key value]))
...or Clojure 1.5's new threading macros:
(-> m
(cond-> value (assoc key value)))
If it's actually important to avoid repeating both the m and value, you'll have to write your own or reach outside clojure.core
There is no build-in assoc-if function in Clojure, but you're not the first one who needs it. Check this link with an implementation of assoc-if by ALEX MILLER:
(defn ?assoc
"Same as assoc, but skip the assoc if v is nil"
[m & kvs]
(->> kvs
(partition 2)
(filter second)
flatten
(apply assoc m)))
But, since flatten is recursive, it's best to replace it with something which is not (thanks to kotarak for the hint). Another problem of this implementation is that (apply assoc m) will fail on empty list. So, it's best to replace it to:
(defn ?assoc
"Same as assoc, but skip the assoc if v is nil"
[m & kvs]
(->> kvs
(partition 2)
(filter second)
(map vec)
(into m)))
Just use assoc-some from the medley library. Battle-tested and widely used.

Alternatives for converting list of nested maps to a map

I have the following working code to convert a list with nested maps (actually tweet data) to a map:
(defn filter
"This function returns a map with the user as key, #followers as value"
[raw-tweets]
(let [users (map :user raw-tweets)
names (map :name users)
followers (map :followers_count users)]
(zipmap names followers)))
Although this works as expected, I was wondering if there would be a more idiomatic way to do this in Clojure. Any alternatives?
What you have is fine, though you can build the map as you go by using reduce:
(defn user-followers [raw-tweets]
(reduce #(assoc %1 (:name %2) (:followers_count %2))
{} (map :user raw-tweets)))
I'm only starting to learn clojure but I think this way might be a bit more idiomatic. It's an alternative in any case.
(defn filter
"This function returns a map with the user as key, #followers as value"
[raw-tweets]
(into {} (map #(let [user (:user %)]
[(:name user) (:followers_count user)])
raw-tweets)))
It maps over the raw tweets with a function that retrieves the user for each tweet and returns a vector with the name and followers count for that user. The into function takes two sequences and conjoins every element of the second one onto the first, which will turn the list of vectors into a map before it's returned from the filter function.
I find #Daan's answer nice, but I'd add destructuring into the mix.
(defn filter-tweets
"This function returns a map with the user as key, #followers as value"
[raw-tweets]
(into {} (map (fn [{{name :name follower-count :followers_count} :user}]
[name follower-count])
raw-tweets)))
I don't like the (map (fn ...)) pattern - it's really just an ugly way to write a for comprehension. I'd write this as:
(into {}
(for [{:keys [user]} raw-tweets]
((juxt :name :followers_count) user)))
Or this, which feels a little less natural to me but avoids inventing names for values you're just going to use once anyway.
(into {} (map (comp (juxt :name :followers_count) :user)
raw-tweets))