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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.