Thanks Alex, this is a VERY elegant solution. But there is still ex-C++ programmer sitting inside my head, screaming "Aaaaah, so many temporaries and lookups, this is so inefficient" :-). I'll try to make "him" shut up by comparing perf with my multi-filter approach.
- Dmitry On Oct 18, 7:20 pm, Alex Osborne <a...@meshy.org> wrote: > Dmitry Kakurin wrote: > > I actually like your "tag them then group them" approach. > > But what if the same record can have multiple tags? > > E.g. :sales and :upgrades? > > Hmmm. the first way that occurred to me is just make your tagging > function return a set: > > (defn record-types [x] > (disj > #{(when (is-sale? x) :sales) > (when (is-upgrade? x) :upgrades) > (when (is-demo? x) :demos)} > nil)) > > So you'd then get groups like: > > {#{:sales :upgrades} [...] > #{:sales} [...] > #{:upgrades} [...]} > > But unfortunately it seems that because the group-by in > clojure.contrib.seq-utils uses a sorted-map it can't have keys that are > sets (as there's no ordering defined for sets). You could either use > vectors instead of sets, or better yet lets just define reduce-by and > group-by to use a hash-map instead: > > (defn reduce-by [grouper f val coll] > (reduce (fn [m x] > (let [group (grouper x)] > (assoc m group (f (get m group val) x)))) > {} coll)) > > (defn group-by [grouper coll] > (reduce-by grouper conj [] coll)) > > This has the advantage that you can do things like intersections > ("number of sales sales that were also upgrades"). If you don't care > about intersections and just want the three :sales, :upgrades, and > :demos lists, then I guess we start with 'for' to generate [tag record] > pairs: > > (for [record (get-idata) > type (record-types record)] > [type record]) > > So if "data1" is both sales and upgrades then you'd get: > > ([:sales data1] [:upgrades data1] [:sales data2] [:demos data3] ...) > > We can then reduce-by grouped on the first value in the pair (the tag) > and then transform the resulting maps to select just the second item in > the pair (the record): > > (reduce-by > first #(conj %1 (second %2)) [] > (for [record (get-idata) > type (record-types record)] > [type record])) > > => {:sales [data1 data2 ... ], > :upgrades [data1 data3 ...], > :demos [data4 ...]} --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~----------~----~----~----~------~----~------~--~---