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
-~----------~----~----~----~------~----~------~--~---

Reply via email to