0

Ideally,select-keys returns a map with keys in the order of the key seq vector. But I find that it happens only when the number of your keys is less than 10

(def params {:resource-total-calls-abandoned 1, :resource-idle-time 5, :date "2016-12-31", :resource-wrap-up-time 1, :agent-talk-time 1, :resource-work-offers 3, :aux-in-time 1, :user-id 2183, :resource-not-ready-time 3, :split 1, :aux-out-time 1, :resource-logged-in-time 1, :hold-time 115, :acd-calls 1})

(select-keys  params [:user-id
                                            :date
                                            :split
                                            :resource-logged-in-time
                                            :agent-talk-time
                                            :resource-wrap-up-time
                                            :hold-time
                                            :acd-calls
                                            :resource-total-calls-abandoned

                                            ])

Here is my result:

{:user-id 2183, :date "2016-12-31", :split 1, :resource-logged-in-time 1, :agent-talk-time 1, :resource-wrap-up-time 1, :hold-time 115, :acd-calls 1, :resource-total-calls-abandoned 1}

If I have more than 10 keys to be selected from a huge map , the order of the map will be different from the order of keys

(select-keys  params [:user-id
                                            :date
                                            :split
                                            :resource-logged-in-time
                                            :agent-talk-time
                                            :resource-wrap-up-time
                                            :hold-time
                                            :acd-calls
                                            :resource-total-calls-abandoned
                                            :aux-in-time
                                            ])

The result will be different :

{:resource-total-calls-abandoned 1, :date "2016-12-31", :resource-wrap-up-time 1, :agent-talk-time 1, :aux-in-time 1, :user-id 2183, :split 1, :resource-logged-in-time 1, :hold-time 115, :acd-calls 1}

Any other ideas about returning a map with keys in the order of the keyseq vector no matter how many keys you want to select ?

tony
  • 67
  • 4
  • 4
    Maps are not ordered. They appear ordered up to 8 entries, but this is an implementation detail, you should never rely on. – cfrick Aug 03 '17 at 13:35
  • Looks that way . Thanks – tony Aug 03 '17 at 13:40
  • I don't have time for a proper answer but just throwing out a quick idea: you could write a function/macro which takes the keyseq and builds a comparator based on its ordering, and then return a sorted map based on that comparator. And yes, if you look at maps less than 9 k/v pairs, you will see the type is `PersistentArrayMap`, and for greater, it's `PersistentHashMap` -- this is the reason for the appearance of ordering, and this is done since hashing will be quicker for large maps, and an array-based implementation is more performant for very small maps. – Josh Aug 03 '17 at 13:41
  • 1
    @Josh Incidentally this already exists in my [useful](https://github.com/flatland/useful/blob/develop/src/flatland/useful/map.clj#L243-L256) library. – amalloy Aug 03 '17 at 18:08

1 Answers1

2

If you want an associative structure with order, there is a sorted-map that maintains the order of its elements. An off-the-cuff solution could be to first select the keys that you want, and then put them in a new sorted map.

(def k [:user-id
        :date
        :split
        :resource-logged-in-time
        :agent-talk-time
        :resource-wrap-up-time
        :hold-time
        :acd-calls
        :resource-total-calls-abandoned
        :aux-in-time])

(->> (select-keys params k)
     (into (sorted-map-by #(< (.indexOf k %1) (.indexOf k %2)))))

The comparator function #(< ... above is just an example; if you create a sorted map this way, updates to it (e.g., assoc) will most definitely do the wrong thing. The reason is that the index in vector k is used to compare the order of elements, but it is -1 for any element not in the vector.

A more efficient solution could be to reduce over the map into a sorted data structure.

Update: As suggested by @leetwinski, changed apply to into, which removes the need to flatten the map after select-keys.

Toni Vanhala
  • 1,352
  • 6
  • 16