taoensso / encore

Core utils library for Clojure/Script
https://www.taoensso.com/encore
Eclipse Public License 1.0
272 stars 52 forks source link

Feature request: index-by #36

Closed timothypratley closed 7 years ago

timothypratley commented 7 years ago

A common transformation I run into is I have a sequence of rows that I want to convert into a map. The most recent example of this I ran into I have a query to return all the "edge-types" which returns this:

[{:edge/type "likes", :edge/color "blue", :edge/distance 30}
 {:edge/type "dislikes" :edge/color "red" :edge/distance 300}]

But what I really want is:

{"likes" {:edge/color "blue", :edge/distance 30}
 "dislikes {:edge/color "red", :edge/distance 300}}

Because my code uses edge type to look up properties:

(get-in edge-types "likes" :edge/color)

So I often find myself writing:

(into {} (for [{:keys [edge/type] :as row} rows] [type row]))

In the context of my application this adds some messy indirection between specifying the data I want, and the data I actually use

A better implementation is:

(defn index-by
  "Given a function f and a seq coll, 
  returns a map mapping for each value x in coll 
  (f x) to x; presumes f is injective on coll."
  [f coll]
  (persistent!
    (reduce
      (fn [tm x]
        (assoc! tm (f x) x))
      (transient {}) coll)))

There exists the group-by function and set/index, but neither is very useful in practice (to me) because group-by creates sequences, and set/index creates maps as keys.

ptaoussanis commented 7 years ago

Hey Timothy! Thanks a lot for this, and sorry for the crazy delay replying. Merged!

timothypratley commented 7 years ago

groovy, thanks! :)