I'm rewriting a very odd e-commerce website I first wrote in 1996, for a friend.
The website features categories, which are supposed to be arranged into an acyclic directed graph (actually there are currently cycles in the graph, but there shouldn't be, and that is not the problem I'm trying to solve here; assume there are not). Customers can register interest in a category or in several categories. When an item is added to the site, we want to email every customer who may be interested in that item; which is to say customers who are interested in the category to which the item has been added, and customers who are interested in the parent of that category, and so on recursively to the root of the graph. Now, I can recurse up the graph asking each category in turn for the customers interested in it; and by concatenating the lists, arrive at one list of all the interested customers. The problem is that if one customer has registered interest in both a category and its parent, he will appear on the composite list twice. I need to construct from this a list of records in which each customer appears only once, otherwise they'll receive duplicate emails, which no-one likes. My solution, which I give below, is to recurse across the composite list constructing a map whose keys are email addresses and whose values are the customer records as maps; and then to take the values of that map. It works, but it doesn't feel like idiomatic clojure: (defn map-by-key "From this `list-of-maps`, produce a map keyed on the values for this `key`" [list-of-maps key] (if (empty? list-of-maps) {} (let [record (first list-of-maps)] (merge {(record key) record} (map-by-key (rest list-of-maps) key))))) (defn interested-customers "Return a list of the customers interested in the category with this `category-id`. Recurses up the tree of categories; `path` is passed to protect against cycles in the tree (which shouldn't be there by are not impossible)" ([category-id] (vals (map-by-key (interested-customers category-id #{}) :email))) ([category-id path] (if (contains? path category-id) () (let [id (if (integer? category-id) category-id (Integer/valueOf (str category-id))) category (categories/fetch id) parent (:parent category) upstream (if (not (nil? parent)) (interested-customers parent (conj path id)) ()) interested (select schema/customer (with schema/category (where {:id id})))] (concat interested upstream))))) Can anyone re-express that in a more idiomatic form? -- 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 --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.