Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding keys closest to a given value for clojure sorted-maps

For clojure's sorted-map, how do I find the entry having the key closest to a given value?

e.g. Suppose I have

(def my-map (sorted-map 
                      1 A 
                      2 B
                      5 C))

I would like a function like

(find-closest my-map 4)

which would return (5,C), since that's the entry with the closest key. I could do a linear search, but since the map is sorted, there should be a way of finding this value in something like O(log n).

I can't find anything in the API which makes this possible. If, for instance, I could ask for the i'th entry in the map, I could cobble together a function like the one I want, but I can't find any such function.

Edit:

So apparently sorted-map is based on a PersistentTreeMap class implemented in java, which is a red and black tree. So this really seems like it should be doable, at least in principle.

like image 692
Rob Lachlan Avatar asked Dec 30 '09 19:12

Rob Lachlan


People also ask

What is a map in CL Clojure?

Clojure - Maps. A Map is a collection that maps keys to values. Two different map types are provided - hashed and sorted. HashMaps require keys that correctly support hashCode and equals. SortedMaps require keys that implement Comparable, or an instance of Comparator.

What are HMAP parameters and return values in Clojure?

Parameters − ‘hmap’ is the map of hash keys and values. ‘key’ is the key which needs to be searched in the map. Return Value − Returns the key value pair for the desired key, else returns nil. Following is an example of find in Clojure. The above code produces the following output.

What is the return value of find in Clojure?

Return Value − Returns the key value pair for the desired key, else returns nil. Following is an example of find in Clojure. The above code produces the following output.

Can I use apply-values and select-keys in Clojure?

I find select-keys, select-values, & apply-values to be helpful when writing Clojure applications. If you find you need these functions, feel free to use them in your own code. However, you'll probably want to check the comments-I'm sure someone with more Clojure experience than I have will provide superior implementations.


2 Answers

Use the Clojure contrib library data.avl. It supports sorted-maps with a nearest function and other useful features.

https://github.com/clojure/data.avl

like image 165
miner49r Avatar answered Oct 17 '22 23:10

miner49r


subseq and rsubseq are very fast because they exploit the tree structure:

(def m (sorted-map 1 :a, 2 :b, 5 :c)) 

(defn abs [x] (if (neg? x) (- x) x))
(defn find-closest [sm k]
  (if-let [a (key (first (rsubseq sm <= k)))]
    (if (= a k)
      a
      (if-let [b (key (first (subseq sm >= k)))]
        (if (< (abs (- k b)) (abs (- k a)))
          b
          a)))
    (key (first (subseq sm >= k)))))

user=> (find-closest m 4)
5
user=> (find-closest m 3)
2

This does slightly more work than ideal, in the ideal scenario we would just do a <= search then look at the node to the right to check if there is anything closer in that direction. You can access the tree (.tree m) but the .left and .right methods aren't public so custom traversal is not currently possible.

like image 27
Timothy Pratley Avatar answered Oct 17 '22 21:10

Timothy Pratley