I have a data set that looks like this:
({"1880" 5} {"1951" 6} {"1952" 5} {"1976" 10} {"1902" 7} {"1919" 7} {"1949" 12} {"1814" 4} {"1930" 11})
I am trying to get the key with the highest value. So in the case above I want to get the value "1949" back. I believe my answer lies with max-key, however I don't fully understand how max-key works. For clarity as one answer was about looking at the string value:
I want the string "1949" as the result because it has the highest number associated with it of 12
Just use max-key, with a function to grab the val from each map:
(def data
[{"1880" 5} {"1951" 6} {"1952" 5} {"1976" 10} {"1902" 7} {"1919" 7} {"1949" 12} {"1814" 4} {"1930" 11}])
(apply max-key #(val (first %)) data) => {"1949" 12}
You need the first function to convert each single element map into a MapEntry. You can then use the val function to grab value out of the MapEntry:
(first {"1880" 5}) => <#clojure.lang.MapEntry ["1880" 5]>
(val (first {"1880" 5})) => <#java.lang.Long 5>
Be sure to bookmark The Clojure CheatSheet and peruse it often!
P.S. Why first works for this:
Note that you can convert a map into sequence of MapEntry's using either seq or vec:
some-map => <#clojure.lang.PersistentArrayMap {:a 1, :b 2}>
(seq some-map) => <#clojure.lang.PersistentArrayMap$Seq ([:a 1] [:b 2])>
(vec some-map) => <#clojure.lang.PersistentVector [[:a 1] [:b 2]]>
You then need the first item from this seq/vector, which is where first comes in:
(first (vec some-map)) => <#clojure.lang.MapEntry [:a 1]>
Note, however, that first implicitly calls seq on whatever you pass to it, so we can skip the conversion and let first implicitly convert the map into a seq of MapEntry's for us:
(first some-map) => <#clojure.lang.MapEntry [:a 1]>
You can sort your list of maps by the value of each map within it.
(last (sort-by (comp second first) data))
=> {"1949" 12}
But looking at the data, I'm wondering it wouldn't just be a single map rather than a sequence of maps. So I'm going to make the assumption that your data will never have duplicate keys, and then we can work with just a single map structure and it's easier:
(into {} data)
=> {"1919" 7, "1880" 5, "1814" 4, "1902" 7, "1951" 6, "1949" 12, "1976" 10, "1930" 11, "1952" 5}
Then you can get the same answer like this:
(last (sort-by second (into {} data)))
=> ["1949" 12]
You can call first with these outputs to get just the string "1949".
Here's another way to do it, sorting descending with a custom/reversed comparator:
(->> (into {} data)
(sort-by second #(compare %2 %1))
(ffirst))
=> "1949"
Since your keys aren't numbers (they are strings) you can't use max-key without casting to a number.
You could achieve your desired result with this:
(last (sort (mapcat keys ({"1889" 1} {"1990" 2}))))
Related
Given
[["foo" "bar" 2] ["biz" "baf" 3]]
how do I get
[{:a "foo" :b "bar" :num 2} {:a "biz" :b "baf" :num 3}]?
In reality my vector has hundreds of vectors that need to have keys added and be converted to hash-maps.
What leetwinski said, or:
(def input [["foo" "bar" 2]["biz" "baf" 3]])
(mapv (fn [[a b num]]
{:a a
:b b
:num num}) input)
If you need to convert a lot of data, maybe mapv is not the best option because it will keep the whole vector in memory at once. Normal map which creates a lazy seq, or a transducer might be better in that case.
A general solution:
(defn vectors->maps
"takes a vector of vectors containing values and a vector of keys
and returns a vector of maps such that each value at index n in the value
vector is paired with each key at index n in the keys vector
ex: (vectors->maps [["foo" "bar" 2] ["biz" "baf" 3]], [:a :b :num])
=> [{:a "foo", :b "bar", :num 2} {:a "biz", :b "baf", :num 3}]"
[vectors keys]
(mapv (partial zipmap keys) vectors))
Exercise for the reader: writing a spec for this function and generating tests for it to ferret out any edge cases
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".
Why is there a difference in return types of assoc and dissoc in Clojure, when their argument is a record? I mean that assoc'ing a non-existent key still returns a record, but dissoc'ing an existing key returns a map.
But, in a sense, both should produce either a map or a record, but not exhibit different behavior. What is the reason for this dissimilarity?
Record will be converted to an ordinary clojure map only if you'll dissoc one of its predefined fields. It's very reasonable behavior, because records can't have undefined fields.
Consider the following code:
(defrecord Point [x y])
(def p (Point. 1 2)) ; => Point{:x 1, :y 2}
(assoc p :x 3) ; => Point{:x 3, :y 2}
(dissoc p :x) ; => {:y 2}
(assoc p :z 3) ; => Point{:x 1, :y 2, :z 3}
(dissoc p :z) ; => Point{:x 1, :y 2}
(-> p
(assoc :z 3) ; => Point{:x 1, :y 2, :z 3}
(dissoc :z)) ; => Point{:x 1, :y 2}
As you can see, both assoc and dissoc return a record as long as it satisfies Point definition.
Record instances are guaranteed to include all the fields declared in the record definition.
When a declared field is removed from an instance, this guarantee would be violated. Hence a map is returned.
Apparently they are not guaranteed to exclude all fields not declared in the record definition, thus new fields can be added to instances.
I'm just starting to learn clojure and have been reading some simple examples and then doing my best to rtfm for concepts.
However I'm a bit confused by what val is doing in the example below. This has been taken from the Clojure doc examples for val.
(first {:one :two}) ;; => [:one :two]
Here, a hash-map with a key of :one and a value of :two is being passed to first. Behind the scenes, Clojure converts this hash-map to a sequence of vectors. Since there is only one vector in this sequence, it returns [:one :two].
(val (first {:one :two})) ;; => :two
(val [:one :two]) ;; => ClassCastException clojure.lang.PersistentVector cannot be cast to java.util.Map$Entry
(val {:one :two}) ;; => ClassCastException clojure.lang.PersistentArrayMap cannot be cast to java.util.Map$Entry
If I try to call val on a (I think) a hash-map (I realize it's actually a "persistent array map"), I get the exception as seen above.
I'm also confused by the following:
(first {:one :two}) ;; # => [:one :two] (this is a vector right?)
(val [:one :two]) ;; # => ClassCastException (why doesn't this give back the same result as the example above?)
Why can't I just plug the result of (first {:one :two}) into val and get the same result?
Additionally, another example listed on the page is the following:
(map val {:a 1 :b 2}) ;; => (1 2)
Here's how I read the line. Take the array-map {:a 1 :b 2}. For each key-value pair, call val on the pair to return the value. Return a sequence from the resulting calls to map. Is this the correct way to read the problem?
As always, thanks for any and all help.
a sequence of a map produces MapEntry values as you've noted, which look like and can be compared with vectors
user=> (= (first {:a 1 :b 2}) [:a 1])
true
but aren't the same class
user=> (= (class (first {:a 1 :b 2})) (class [:a 1]))
false
So although the output on the repl of (first {:a 1}) looks like a vector, it isn't, it's a MapEntry, so it can be passed to val, but the vector [:a 1] cannot, hence the class cast exception.
Your reading of what map is doing is correct at a high level, a little more specific might be "For each entry in the sequence from {:a 1 :b 2} (which are MapEntry values) call the function val on each item (a MapEntry), and generate a sequence from the results".
This will explain why something like
user=> (map val '([:a 1] [:b 2]))
will cause the same ClassCastExceptions as the sequence generates Vector elements, not MapEntry elements.
val returns value of a map entry, not a map.
(first {:one :two}) return the first map entry (although it appears to be just a vec)
(map val {:one :two}) return the value of every entry, and is equivalent to (vals {:one :two})
(first {:one :two}) ;; # => [:one :two] (this is a vector right? No, it's not.)
[:one :two] in this case is a MapEntry, not a vector.
Answers to this question explain how to convert maps, sequences, etc. to various sequences and collections, but do not say how to convert a map to a sequence of alternating keys and values. Here is one way:
(apply concat {:a 1 :b 2})
=> (:b 2 :a 1)
Some alternatives that one might naively think would produce the same result, don't, including passing the map to vec, vector, seq, sequence, into [], into (), and flatten. (Sometimes it's easier to try it than to think it through.)
Is there anything simpler than apply concat?
You can also do
(mapcat identity {:a 1 :b 2})
or
(mapcat seq {:a 1 :b 2})
As #noisesmith gently hints below, the following answer is seductive but wrong: left as a warning to other unwary souls! Counterexample:
((comp flatten seq) {[1 2] [3 4], 5 [6 7]})
; (1 2 3 4 5 6 7)
(comp flatten seq) does the job:
((comp flatten seq) {1 2, 3 4})
; (1 2 3 4)
But flatten on its own doesn't:
(flatten {1 2, 3 4})
; ()
I'm surprised it doesn't work, and in that case it should return nil, not ().
None of the others you mention: vec, vector ... , does anything to the individual [key value] pairs that the map presents itself as a sequence of.