I'm trying to retrieve an entire hash from a vector of hashes based on whether or not it has a specific value in a field.
(def foo {:a 1, :b 2})
(def bar {:a 3, :b 4})
(def baz [foo bar])
In baz, I want to return the entire hash where :a 3 so the result will be {:a 3, :b 4}. I have tried get get-in and find but those rely on keys and do not return the entire hash. I've also tried some suggestion from this question but they don't return the hash either.
filter to the rescue!
hello.core> (def foo {:a 1, :b 2})
#'hello.core/foo
hello.core> (def bar {:a 3, :b 4})
#'hello.core/bar
hello.core> (def baz [foo bar])
#'hello.core/baz
hello.core> (filter #(= (:a %) 3) baz)
({:a 3, :b 4})
#(= (:a %) 3) is a short form for creating an anonymous that takes one argument, named %, in which it will look up the key :a and return true if that matches the value 3. Any entry in the vector baz which passes this test will make it into the output.
PS: a note on pronunciation: that data structure is typically called a "map" because it maps one key to one value. This is terribly confusing because there is also a function named map which changes every member of a sequence by a function.
filter definitely does the job as Arthur mentioned. Just for the sake of completeness these are 2 other solutions which differ in 2 aspects from filter:
(some #(when (= 3 (:a %)) %) baz)
(first (drop-while #(not= 3 (:a %)) baz))
these will stop further searching through your whole collection as soon as they have found the first element in the collection which fits your requirements (hence less resource) and
because of that, in contrary to filter they give you only the first fitting element and not all the elements in the collection which pass your
requirements (in case you have multiple repeated elements in your collection).
Related
I really like using contains? because it's so terse and readable. I want to see if a set contains maps that have the same key and value pairs of an example that also had other key value pairs. I'm pretty sure contains? won't work here. Is there an alternative? Maybe I'll have to write one (I'm finally getting into the mindset!). For example, if I had
(def some-set #{{:foo "bar" :beep "boop"}{:foo "bar"} {:foo "bar" :hi "there"}})
what would be a quick way to know if it had any maps that matched {:foo "bar" :one "two"} on :foo "bar"?
Edited: Remembering that a map is a collection of key-value vectors, here is an implementation for the predicate submap?:
(defn submap?
"Returns true if subm is a submap of m, false otherwise."
[subm m]
(every? (fn [[k v]] (= (get m k ::not-found) v)) subm))
This predicate can be used to filter any collection:
(filter #(submap? {:a 1 :b 2} %) [{:a 1} {:a 1 :b 2 :c 3}])
=> ({:a 1, :b 2, :c 3})
Original answer
This solution works but is slower than my updated answer, due to the construction of (set m) for large m
(defn submap?
"Returns true if subm is a submap of m, false otherwise."
[subm m]
(let [kvs (set m)]
(every? kvs subm)))
A generic way would be to write a predicate, that checks if a map
contains another map. This can be done using select-keys to only get
a map with certain keys; using the keys from the map to compare and
then just comparing the result will give you that.
(def maps #{{:foo "bar" :beep "boop"} {:foo "bar"} {:foo "bar" :hi "there"} {:foo "baz"}})
(defn submap?
[submap m]
(= (select-keys m (keys submap)) submap))
(println
(filter (partial submap? {:foo "bar"}) maps))
; → ({:foo bar, :beep boop} {:foo bar, :hi there} {:foo bar})
Yet this is just a simple sequential search. This does not (and AFAIR
there is nothing in core to help) utilize your maps being in a set.
Also note, that the order of the result is undefined since the order of
sets is too.
You can find many predicates of this nature and related helper functions in the Tupelo library, in particular:
submap?
submatch?
wild-match?
wild-submatch?
These are especially helpful in writing unit tests. For example, you may only care about certain fields like :body when testing a webserver response, and you want to ignore other fields like the IP address or a timestamp.
The unit tests show the code in action.
Alright, I'm new to clojure, this should be easy but for the life of me I can't find the answer
Let's say I have this map
(def mymap {:a 10 :b 15})
Now I want to change the value of :a to 5. I don't know how to do this properly
I know update and assoc can make changes but they both receive a function as last argument, which applies to the value. I don't want that, I don't want any function to run, I just want to simply set :a to 5.
I think I can pass an anonymous function that simply returns 5 and ignores the arg, but is this the right way? Doesn't look good to me
(update mymap :a (fn [arg] 5))
assoc does not take a function as its last argument; unless you were wanting to associate a function with a key in the map. (assoc mymap :a 5) does what you want.
I'll add though, update, which does take a function, could be used here as well when combined with constantly or just another function (although there's no reason to use them over assoc):
; constantly returns a function that throws away any arguments given to it,
; and "constantly" returns the given value
(update mymap :a (constantly 5))
; Basically the same as above
(update mymap :a (fn [_] 5))
Do keep in mind that as mymap is immutable, so calling (update mymap :a (constantly 5)) or (assoc mymap :a 5) will return a map {:a 5 :b 15}, further references to mymap will continue to return the original value of {:a 10 :b 15}.
If you want to update the value for later calls, you can look at using atoms.
(defonce mymap (atom {:a 10 :b 15}))
(defn change-mymap [value]
(swap! mymap #(assoc % :a value)))
Just make sure that when you want to reference the value of an atom, you dereference it with the # symbol. For example: (clojure.pprint/pprint #mymap)
When you call (change-mymap 5) this will update the stored mymap value to set :a to a new value, leaving any other key-value pairs in your map alone. This can be helpful when you are mapping in updated state in client/server code when responding to inputs from the other system.
Also note that for nested maps, such as
(defonce nested (atom {:a "a value"
:b {:inner "Another value"
:count 3
:another {:value 5}}}))
You can address a particular value in your map by a path vector. You can use the get-in function to retrieve the value (get-in #nested [:b :another :value])
and you can use assoc-in or update-in with a path to update the values. This also allows you to extend a map. For example, with the above value of nested, you can add a whole section to the tree:
(swap! nested #(assoc-in % [:a :b :c :d] "foo"))
will update the initial map to look like this:
{:a {:b {:c {:d "foo"}}}
:b {:inner "Another value"
:count 3
:another {:value 5}}}
I have an atom that has two parts to it.
(def thing (atom {:queue '() :map {}}))
I want to update both :queue and :map in one atomic stroke, to prevent them from getting off-sync.
Queue individually:
(swap! thing update-in [:queue] (list 1))
(From this question: How to append to a nested list in a Clojure atom?)
Map individually:
(swap! thing assoc-in [:map 1] (:key :value))
(From this question: Using swap to MERGE (append to) a nested map in a Clojure atom?)
How can I do these both within a single swap statement? (assuming that would prevent them from getting off-sync?)
You have one change you want to make, right? And you could write that change as a pure function? All you need to do is write that function, and pass it as the argument to swap!.
(defn take-from-queue [{q :queue, m :map}]
{:queue (rest q), :map (assoc m :new-task (first q))})
(swap! thing take-from-queue)
Where of course I have no idea what you actually want the body of your function to do, so I've made up something that doesn't throw an exception.
Say you have a hash-map atom:
(def m1 (atom {:a "A" :b "B"}))
To change :a and :b at the same time, changing their values to values that are different, say the numbers 1 and 2, use this function:
(defn my-swap! [params]
(swap! m1 (fn [old new] new) params))
, like so:
(my-swap! {:a 1 :b 2}) ;=> {:a 1, :b 2}
And the same effect could be achieved with the following function and execution:
(defn my-multi-swap! [params1 params2]
(swap! m1 (fn [old new1 new2] new2) params1 params2))
(my-multi-swap! {} {:a 1 :b 2}) ;=> {:a 1, :b 2}
Normally reset! is used if you want to ignore the old value. Here we use it:
(defn my-merge-swap! [params]
(swap! m1 (fn [old new] (merge old new)) params))
(my-merge-swap! {:b 3}) ;=> {:a "A", :b 3}
The first parameter to the swap! function is the existing value of the atom, and you must pass in one or more extra parameters, which you can use to give the atom its new value.
I'm saving a nested map of data to disk via spit. I want some of the maps inside my map to be sorted, and to stay sorted when I slurp the map back into my program. Sorted maps don't have a unique literal representation, so when I spit the map-of-maps onto disk, the sorted maps and the unsorted maps are represented the same, and #(read-string (slurp %))ing the data makes every map the usual unsorted type. Here's a toy example illustrating the problem:
(def sorted-thing (sorted-map :c 3 :e 5 :a 1))
;= #'user/sorted-thing
(spit "disk" sorted-thing)
;= nil
(def read-thing (read-string (slurp "disk")))
;= #'user/read-thing
(assoc sorted-thing :b 2)
;= {:a 1, :b 2, :c 3, :e 5}
(assoc read-thing :b 2)
;= {:b 2, :a 1, :c 3, :e 5}
Is there some way to read the maps in as sorted in the first place, rather than converting them to sorted maps after reading? Or is this a sign that I should be using some kind of real database?
The *print-dup* dynamically rebindable Var is meant to support this use case:
(binding [*print-dup* true]
(prn (sorted-map :foo 1)))
; #=(clojure.lang.PersistentTreeMap/create {:foo 1})
The commented out line is what gets printed.
It so happens that it also affects str when applied to Clojure data structures, and therefore also spit, so if you do
(binding [*print-dup* true]
(spit "foo.txt" (sorted-map :foo 1)))
the map representation written to foo.txt will be the one displayed above.
Admittedly, I'm not 100% sure whether this is documented somewhere; if you feel uneasy about this, you could always spit the result of using pr-str with *print-dup* bound to true:
(binding [*print-dup* true]
(pr-str (sorted-map :foo 1)))
;= "#=(clojure.lang.PersistentTreeMap/create {:foo 1})"
(This time the last line is the value returned rather than printed output.)
Clearly you'll have to have *read-eval* bound to true to be able to read back these literals. That's fine though, it's exactly the purpose it's meant to serve (reading code from trusted sources).
I don't think its necessarily a sign that you should be using a database, but I do think its a sign that you shouldn't be using spit. When you write your sorted maps to disk, don't use the map literal syntax. If you write it out in the following format, read-string will work:
(def sorted-thing (eval (read-string "(sorted-map :c 3 :e 5 :a 1)")))
(assoc sorted-thing :b 2)
;= {:a 1, :b 2, :c 3, :e 5}
Novice question, but I don't really understand why there are so many operations for constructing maps in clojure.
You have conj, assoc and merge, but they seem to more or less do the same thing?
(assoc {:a 1 :b 2} :c 3)
(conj {:a 1 :b 2} {:c 3})
(merge {:a 1 :b 2} {:c 3})
What's really the difference and why are all these methods required when they do more or less the same thing?
assoc and conj behave very differently for other data structures:
user=> (assoc [1 2 3 4] 1 5)
[1 5 3 4]
user=> (conj [1 2 3 4] 1 5)
[1 2 3 4 1 5]
If you are writing a function that can handle multiple kinds of collections, then your choice will make a big difference.
Treat merge as a maps-only function (its similar to conj for other collections).
My opinion:
assoc - use when you are 'changing' existing key/value pairs
conj - use when you are 'adding' new key/value pairs
merge - use when you are combining two or more maps
Actually these functions behave quite differently when used with maps.
conj:
Firstly, the (conj {:a 1 :b 2} :c 3) example from the question text does not work at all (neither with 1.1 nor with 1.2; IllegalArgumentException is thrown). There are just a handful of types which can be conjed onto maps, namely two-element vectors, clojure.lang.MapEntrys (which are basically equivalent to two-element vectors) and maps.
Note that seq of a map comprises a bunch of MapEntrys. Thus you can do e.g.
(into a-map (filter a-predicate another-map))
(note that into uses conj -- or conj!, when possible -- internally). Neither merge nor assoc allows you to do that.
merge:
This is almost exactly equivalent to conj, but it replaces its nil arguments with {} -- empty hash maps -- and thus will return a map when the first "map" in the chain happens to be nil.
(apply conj [nil {:a 1} {:b 2}])
; => ({:b 2} {:a 1}) ; clojure.lang.PersistentList
(apply merge [nil {:a 1} {:b 2}])
; => {:a 1 :b 2} ; clojure.lang.PersistentArrayMap
Note there's nothing (except the docstring...) to stop the programmer from using merge with other collection types. If one does that, weirdness ensues; not recommended.
assoc:
Again, the example from the question text -- (assoc {:a 1 :b 2} {:c 3}) -- won't work; instead, it'll throw an IllegalArgumentException. assoc takes a map argument followed by an even number of arguments -- those in odd positions (let's say the map is at position 0) are keys, those at even positions are values. I find that I assoc things onto maps more often than I conj, though when I conj, assoc would feel cumbersome. ;-)
merge-with:
For the sake of completeness, this is the final basic function dealing with maps. I find it extremely useful. It works as the docstring indicates; here's an example:
(merge-with + {:a 1} {:a 3} {:a 5})
; => {:a 9}
Note that if a map contains a "new" key, which hasn't occured in any of the maps to the left of it, the merging function will not be called. This is occasionally frustrating, but in 1.2 a clever reify can provide a map with non-nil "default values".
Since maps are such a ubiquitous data structure in Clojure, it makes sense to have multiple tools for manipulating them. The various different functions are all syntactically convenient in slightly different circumstances.
My personal take on the specific functions you mention :
I use assoc to add a single value to a map given a key and value
I use merge to combine two maps or add multiple new entries at once
I don't generally use conj with maps at all as I associate it mentally with lists