Realized I forgot to include an example of how the code I linked to can be 
used to solve the original problem, which will make it clearer how it is 
used.

https://gist.github.com/4349972

But when writing that example, I remembered why I created factory-style 
functions.  The group-by from Daniel is assuming we want our result with 
vectors as values, which is a case that I didn't want to be limited to, 
even though the existing group-by results in values that are a vector of 
maps.

Merging the two implementations yields:  https://gist.github.com/4350075

Basically, just giving more responsibility/power to the g fn, which makes 
producing the same result as the original look like this instead (a lot 
busier):

(x-group-by :type #(conj (or % []) (.toUpperCase (:name %2))) animals)

=> {:cow ["BETSY"], :cat ["MURMUR" "ROSIE" "ALF"], :dog ["LESSIE" "DINGO" 
"REX"]}

On Thursday, December 20, 2012 11:19:01 AM UTC-6, Alex Walker wrote:
>
> I like the idea of it being built-in and might prefer that approach, 
> however, I wanted to share an alternative.
>
> https://gist.github.com/4346395
>
> I needed to take a denormalized table of config data and create a nested 
> lookup map, so that I wouldn't need to repeatedly filter the dataset while 
> using it to process a stream of data.
>
> On Monday, December 17, 2012 3:20:32 PM UTC-6, Alex Baranosky wrote:
>>
>> I think it sounds like a nice addition, after mulling it over a little.
>>
>> On Mon, Dec 17, 2012 at 4:47 AM, László Török <ltor...@gmail.com> wrote:
>>
>>> Hi,
>>>
>>> I have come across use cases in the past where an additional 
>>> transformation step was indeed very handy and I wrote my own version of 
>>> group-by, one identical to Daniel's.
>>>
>>> Maybe a function worthwhile for c.c.incubator.
>>>
>>> Las
>>>
>>>
>>> 2012/12/17 Daniel Dinnyes <dinn...@gmail.com>
>>>
>>>> Also note that I wrote in my first post that "Without the value-mapper 
>>>> argument it is very awkward to achieve the same structure after the 
>>>> group-by call". The `map-vals` function is almost the closest you can get 
>>>> to map values after a group-by in a streamlined and clean manner. There is 
>>>> `
>>>> fmap<http://clojuredocs.org/clojure_contrib/clojure.contrib.generic.functor/fmap>`
>>>>  
>>>> in the contrib which does a similar thing already though.
>>>>
>>>> An even cleaner mapper would be something like a `map-multi-vals`, so 
>>>> that you can do something like this:
>>>>
>>>> (->> (group-by :type animals)
>>>>      (map-multi-vals :name))
>>>>
>>>> That's the cleanest one can get with a separate value-mapper. In my 
>>>> opinion that has little added benefit though, and possibly the performance 
>>>> is worse too. The only benefit would be separation of concern: you can map 
>>>> values of a multi-map without knowing how it was created. Now think about 
>>>> it: how often would you use `map-multi-vals` separately, not right after a 
>>>> group-by? My impression is that whenever an multi-map is created, it 
>>>> almost 
>>>> always involves in some way a `group-by` - which itself is a special case 
>>>> of `reduce`. There is always a `reduce` somewhere, whether an `into`, a 
>>>> `for`, or some imperative iteration. Only `group-by` is the simplest for 
>>>> this specific purpose of creating a multi-map. 
>>>>
>>>> My argument therefore is that whenever you need a multi-value mapping, 
>>>> it is always preceded by a group-by, and therefore I feel the right place 
>>>> for the value-mapper is as an optional parameter for `group-by` itself.
>>>>
>>>> What do you think?
>>>>
>>>> Cheers,
>>>> Daniel
>>>>
>>>>
>>>> On Monday, December 17, 2012 10:13:17 AM UTC, Daniel Dinnyes wrote:
>>>>>
>>>>> Hi,
>>>>>
>>>>> I expect the cost of calling `identity` to be negligible. Not for 
>>>>> sure, but the JVM might even inline it at run-time, or there might be 
>>>>> optimizations for it in clojure.core during compilation... I cannot 
>>>>> comment 
>>>>> on that. But even with a full virtual call, it should be faster than 
>>>>> iterating the whole map again.
>>>>>
>>>>> Also, that `map-vals` is still indeed clunkier ;) Different usages, 
>>>>> but for me whenever I use `group-by` I very often find I prefer to map 
>>>>> the 
>>>>> values too (to get a nice streamlined data structure to be passed around 
>>>>> for further processing). Just my experience. It was very handy in .NET, 
>>>>> and 
>>>>> I think it was there for this reason.
>>>>>
>>>>> Regards,
>>>>> Daniel
>>>>>
>>>>> On Monday, December 17, 2012 8:21:44 AM UTC, Alex Baranosky wrote:
>>>>>>
>>>>>> I haven't run into this issue (yet).  My first devil's advocate 
>>>>>> thought was to suggest that you could map over the data after calling 
>>>>>> the 
>>>>>> group-by.
>>>>>>
>>>>>> (->> (group-by :type animals)
>>>>>>      (map-vals #(map :name %)))
>>>>>>
>>>>>> There are two problems with this.  One, it uses a custom util 
>>>>>> function `map-vals` so it is a bit of a cheat.  Two, even with that it 
>>>>>> still looks pretty clunky.  
>>>>>>
>>>>>> How does the `identity` effect performance?  I wouldn't think much.
>>>>>>
>>>>>> Alex
>>>>>>
>>>>>> On Fri, Dec 14, 2012 at 9:58 AM, Daniel Dinnyes <dinn...@gmail.com>wrote:
>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> I would like to suggest an enhancement to the  clojure.core/group-by  
>>>>>>> function. The idea came from using Enumerable.GroupBy 
>>>>>>> <http://msdn.microsoft.com/en-us/library/bb534304.aspx>extension 
>>>>>>> method in .NET quite much. It is really handy to have an optional 
>>>>>>> value-mapper function which transforms the elements before adding them 
>>>>>>> to 
>>>>>>> the collection under the key. It is backward compatible, because 
>>>>>>> calling 
>>>>>>> the overload with 2 parameters can call the 3 parameter one with 
>>>>>>> clojure.corj/identity as value-mapper function.
>>>>>>>
>>>>>>> The implementation is easy-peasy (almost the same as the original):
>>>>>>>
>>>>>>> (defn group-by
>>>>>>>   ([f g coll]
>>>>>>>      (persistent!
>>>>>>>       (reduce
>>>>>>>        (fn [ret x]
>>>>>>>          (let [k (f x)]
>>>>>>>            (assoc! ret k (conj (get ret k []) (g x)))))
>>>>>>>        (transient {}) coll)))
>>>>>>>   ([f coll]
>>>>>>>      (group-by f identity coll)))
>>>>>>>
>>>>>>> Without the value-mapper argument it is very awkward to achieve the 
>>>>>>> same structure after the group-by call. Also, doing the transformation 
>>>>>>> before the group-by is often impossible, because the key function 
>>>>>>> depends 
>>>>>>> on some property of the source element, which would be removed after 
>>>>>>> the 
>>>>>>> transformation.
>>>>>>>
>>>>>>> To demonstrate the usage, check out the below calls:
>>>>>>>
>>>>>>> (def animals [{:name "Betsy" :type :cow}
>>>>>>>               {:name "Murmur" :type :cat}
>>>>>>>               {:name "Lessie" :type :dog}
>>>>>>>               {:name "Dingo" :type :dog}
>>>>>>>               {:name "Rosie" :type :cat}
>>>>>>>               {:name "Rex" :type :dog}
>>>>>>>               {:name "Alf" :type :cat}])
>>>>>>>
>>>>>>> (group-by :type animals) ; old usage
>>>>>>> > ... ugly stuff
>>>>>>>
>>>>>>> (group-by :type :name animals) ; new usage
>>>>>>> > {:cow ["Betsy"], :cat ["Murmur" "Rosie" "Alf"], :dog ["Lessie" 
>>>>>>> "Dingo" "Rex"]}
>>>>>>>
>>>>>>> (group-by :type #(.toUpperCase (:name %)) animals) ; hell yeah!
>>>>>>> > {:cow ["BETSY"], :cat ["MURMUR" "ROSIE" "ALF"], :dog ["LESSIE" 
>>>>>>> "DINGO" "REX"]}
>>>>>>>
>>>>>>> It would be so cool to have this in the core. What do you guys think?
>>>>>>>
>>>>>>> Regards,
>>>>>>> Daniel Dinnyes
>>>>>>>
>>>>>>>  -- 
>>>>>>> You received this message because you are subscribed to the Google
>>>>>>> Groups "Clojure" group.
>>>>>>> To post to this group, send email to clo...@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+u...@googlegroups.com
>>>>>>> For more options, visit this group at
>>>>>>> http://groups.google.com/**group/clojure?hl=en<http://groups.google.com/group/clojure?hl=en>
>>>>>>
>>>>>>
>>>>>>  -- 
>>>> You received this message because you are subscribed to the Google
>>>> Groups "Clojure" group.
>>>> To post to this group, send email to clo...@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+u...@googlegroups.com
>>>> For more options, visit this group at
>>>> http://groups.google.com/group/clojure?hl=en
>>>>
>>>
>>>
>>>
>>> -- 
>>> László Török
>>>
>>>  -- 
>>> You received this message because you are subscribed to the Google
>>> Groups "Clojure" group.
>>> To post to this group, send email to clo...@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+u...@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 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