Return value of highest key in Clojure - clojure

I'm working with these two groups of key value pairs, which is being returned by another function. I'd like to write a function that will always find the highest key and return its corresponding value. In this example, I'd be returning 2 because 499 is the highest key. The data that I am working with is
({-99 0, 99 0} {-99 2, 499 2})
When I call
(type ({-99 0, 99 0} {-99 2, 499 2}))
Within the function that is responsible for returning that data it, I get back
(clojure.lang.PersistentTreeMap clojure.lang.PersistentTreeMap)
I hope that helps. Thanks!

This function will return the rightmost entry of a Clojure sorted map (the built-in implementation is called clojure.lang.PersistentTreeMap) in logarithmic time:
(defn rightmost
"Takes a Clojure sorted map sm and returns the entry at the greatest
key (as determined by sm's comparator)."
[sm]
(first (rseq sm)))
Example:
(rightmost (sorted-map 1 1 2 2 3 3))
;= [3 3]
You can then fish out the value using the val function.
All the max-key / apply max-based solutions work in linear time instead. Needless to say, it's a huge difference.
If the other function could be convinced to return data.avl maps instead, you could access the element at any index in logarithmic time using nth:
;; works for data.avl sorted maps, not the built-ins
(nth (avl/sorted-map 1 1 2 2 3 3) 2)
;= [3 3]

(as-> (apply merge pair)
merged
(->> merged
keys
(apply max)
merged))
Notice that when both maps have a "highest" key, the value of second one is returned.

This is a good use case for max-key (See this other SO question for a good example of its use), which I think has kind of a misleading name -- what it actually does is it takes a function and a collection, and it returns the item in the collection that has the highest result of applying the function to that item. You can use the function key, which returns the key of a key-value pair.
(Note that you need to concat your maps together so that you're working with a single collection of key-value pairs.)
(apply max-key key (concat {-99 0, 99 0} {-99 2, 499 2}))
;=> [499 2]
(second *1)
;=> 2

(defn val-for-max-key [maps]
(->> (map (partial apply max-key key) maps)
(apply max-key key)
val))
EDIT:
misunderstood desired return value

({-99 0, 99 0} {-99 2, 499 2}) is a lookup operation, where {-99 0, 99 0} is a dictionary and {-99 2, 499 2} is a key. Since the latter is not a key in the former, the expression will return nil.
When I evaluate (type ({-99 0, 99 0} {-99 2, 499 2})), I get nil, because the type of nil is also nil.

Related

Clojure - Get key with max value ArrayMap

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}))))

Modifying nested data in clojure

How can I manipulate a nested data structure?
I have a list of this kind
[["first_string" {:one 1 :two 2}]
["second_string" {:three 3 :four 4}]
["third_string" {:five 5 :six 6}]
["fourth_string" {:seven 7 :eight 8}]]
And I need to change it to this form:
[["first_string" 1]
["second_string" 3]
["third_string" 5]
["fourth_string" 7]]
Essentially, I want only the first element of each of the inner vectors first key of the map
Try defining a function that operates on a single entry in the vector and then map over it:
(defn manipulate-nested
[entry]
[(first entry) (last (first (last entry)))])
(let [input [["first_string" {:one 1 :two 2}]
["second_string" {:three 3 :four 4}]
["third_string" {:five 5 :six 6}]
["fourth_string" {:seven 7 :eight 8}]]]
(into [] (map manipulate-nested input)))
;; [["first_string" 1]
;; ["second_string" 3]
;; ["third_string" 5]
;; ["fourth_string" 7]]
I need to change it to this form
NB: Keep in mind that strictly speaking you're not changing (mutating) the original vector, but describing a modification of it.
You can't get a reliable first key of a hash-map because hash-maps are an unsorted data structure and seqing them thus has no order guarantees. So there is no first or second.
There is no way of numerically ordering the keywords :one, :two, :three without parsing their names which I leave as a separate problem.
Here is your problem reposed with ordered structures in place of the hash-maps:
(def data [["first_string" [[:one 1] [:two 2]]]
["second_string" [[:three 3] [:four 4]]]
["third_string" [[:five 5] [:six 6]]]
["fourth_string" [[:seven 7] [:eight 8]]]]
One typical and idiomatic solution is to extract from each vector in data independently via map, using destructuring in the transformation functions binding vector to bind the desired nested elements and returning this extraction in a new vector:
(map (fn [[s [[_ n] _]]] [s n]) data)
The input data structure of your specific problem offers a way with less overhead by reusing the passed vector instead of constructing a new one in each step:
(map #(update % 1 (comp second first)) data)

What does clojure 'val' return this value?

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.

Get first x number of elements from a hashmap in clojure

How does one retrieve the first x key value pairs from a sorted clojure map of 2x key value pairs
Simply use take on a sorted-map:
(def a (sorted-map 3 :a 2 :b 1 :c))
(take 2 a) ;; ([1 :c] [2 :b])

How to convert map to a sequence?

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.