[HN Gopher] Using GroupBy on an array of objects in JavaScript
       ___________________________________________________________________
        
       Using GroupBy on an array of objects in JavaScript
        
       Author : saranshk
       Score  : 68 points
       Date   : 2021-12-29 15:39 UTC (7 hours ago)
        
 (HTM) web link (www.wisdomgeek.com)
 (TXT) w3m dump (www.wisdomgeek.com)
        
       | jbverschoor wrote:
       | Oh lord... so you group by a Number, but the map-key becomes a
       | String. Sounds like idiomatic JavaScript
        
         | saranshk wrote:
         | groupByToMap would be useful in that case as pointed out in
         | another comment. Updated the post with that.
        
         | ojr wrote:
         | Javascript has been so widely used for so long, there are two
         | types of languages one that people hate and one that people
         | don't use
        
         | [deleted]
        
         | throw_m239339 wrote:
         | This isn't a map, Javascript has a dedicated map type. In
         | JavaScript, Objects keys can only be strings or symbols.
        
       | mberning wrote:
       | I worked on a Ruby gem to add group_by and aggregate_by functions
       | to collections/enumerables. I finished about 1/2 of the
       | functionality and found the implementation to be hateful and
       | thought it would be received poorly. Now seeing this I am second
       | guessing myself.
        
         | caseyohara wrote:
         | Enumerable#group_by is in Ruby core https://ruby-
         | doc.org/core-3.0.2/Enumerable.html#method-i-gro...
        
         | gfunk911 wrote:
         | Ruby already has group_by, unless I'm misunderstanding what
         | you're saying
        
       | mahesh_rm wrote:
       | Where was this when I needed it? Happy discovery.
        
         | saranshk wrote:
         | probably would not have been written then, it is a stage 3
         | proposal but should be merged in hopefully soon.
        
       | zackmorris wrote:
       | For what it's worth, the Laravel PHP framework (no affiliation)
       | has the best introduction to higher-order methods for imperative
       | programmers used to Javascript/C style of anything that I've come
       | across:
       | 
       | https://laravel.com/docs/master/collections
       | 
       | Most of these methods are also available as part of the Eloquent
       | ORM, for filtering/reshaping queries before they're executed:
       | 
       | https://laravel.com/docs/master/eloquent
       | 
       | Since being exposed to this way of working, I rarely use
       | foreach() anymore, much less for(). The main downside being that
       | I find most other languages tedious to work in now. LINQ in
       | .NET/C# is nice, there might be others.
        
       | newlisp wrote:
       | I normally just write                   const groupBy = (fn, arr)
       | => {             return arr.reduce((o, e) => {
       | const k = fn(e)                 if (o[k] !== undefined)
       | o[k].push(e)                 else o[k] = [e]
       | return o             } Object.create(null))         }
        
         | saranshk wrote:
         | while there is nothing wrong with that, it's always good to
         | have these things built-in as native functions instead.
        
       | [deleted]
        
       | duxup wrote:
       | I was just doing something like this and thinking "I do this in
       | SQL all the time but users like to change things up so much it
       | should be a native array thing."
        
         | saranshk wrote:
         | all of lodash features would probably become a native thing
         | soon enough
        
       | yashap wrote:
       | Personally I'd have this return a map instead of an object, so
       | that the keys aren't forced to be strings. Otherwise seems like a
       | reasonable API, very similar to groupBy in any other moderately
       | functional language, except with the "keys must be strings"
       | restriction inherited from returning a plain object.
       | 
       | Also, for those thinking JS objects can have non-string keys,
       | they can't, it just sometimes appears that way due to JS type
       | coercion: https://www.becomebetterprogrammer.com/can-javasscript-
       | objec...
       | 
       | Edit: as pointed out by shawnz, this isn't entirely accurate, JS
       | object keys can also be symbols.
        
         | shawnz wrote:
         | It looks like they are also creating a "groupByToMap" for that
         | reason. See: https://github.com/tc39/proposal-array-grouping
         | 
         | Property keys can also be symbol objects and it seems like
         | that's supported by the groupBy function in addition to
         | strings, see: https://tc39.es/proposal-array-grouping/
        
           | yashap wrote:
           | Ah fair enough!
        
           | saranshk wrote:
           | updated the post with groupByToMap, Thanks for pointing that
           | out.
        
           | roebk wrote:
           | I wonder if groupBy should have had some sort of into
           | argument. Otherwise, we may end up with four groupBy methods.
           | groupBy, groupByToMap, groupByToWeakMap and groupByToRecord
           | (should records land).
           | 
           | Edit: this has already been discussed
           | https://github.com/tc39/proposal-array-grouping/issues/3
        
         | masklinn wrote:
         | > Otherwise seems like a reasonable API, very similar to
         | groupBy in any other moderately functional language
         | 
         | Though lots of them implement the less useful but stream-able
         | groupBy which only groups siblings matching the predicate (or
         | key function).
        
         | jraph wrote:
         | I found this article a bit verbose (maybe because I'm already
         | familiar with the topic) and yet missing three "key" (ah ah)
         | points:
         | 
         | - one can get the keys of an object by using Object.keys or a
         | for-in loop and notice that keys are always strings, even when
         | using arrays.
         | 
         | - the object definition syntax [1] is set to define property
         | strings by the standard (and symbols). You can (and probably
         | should) experiment, but the spec will tell you what happens
         | anyway with 100% certainty (except for implementation bugs, but
         | that's not what we are looking for).
         | 
         | - the definitive answer to "what kind of value an object key
         | can be in Javascript?" is given by the section "The Object
         | Type" of the ECMAScript standard [2]:
         | 
         | > Properties are identified using key values. A property key
         | value is either an ECMAScript String value or a Symbol value.
         | All String and Symbol values, including the empty String, are
         | valid as property keys. A property name is a property key that
         | is a String value.
         | 
         | Experimentation helps anchoring things in memory and building
         | intuition, as well as getting into things. Inviting people to
         | experiment by themselves and not blindly trust what they read
         | is a fantastic advice. I do experiment a lot myself. However,
         | experimentation is not the best way to "prove" things (contrary
         | to what the article states). Definitive answers should be found
         | in specifications and documentations (or implementation
         | sources). No amount of experimentation will be enough to
         | convince me of "See? I did this this and this, and it's always
         | strings".
         | 
         | The article is well written, the same thing with these
         | references to the spec would make it strong and irrefutable.
         | 
         | [1] https://tc39.es/ecma262/#sec-object-initializer-static-
         | seman...
         | 
         | [2] https://tc39.es/ecma262/#sec-object-type
        
         | wereHamster wrote:
         | A <<group by>> function requires keys to be comparable (for
         | equality). You can see this when you look at the type
         | signatures of <<group by>> functions in any strongly typed
         | language or library. They either require a comparator to be
         | provided, or infer it from the context (type classes in
         | Haskell). JavaScript has quite intuitive equality for primitive
         | types (strings, symbols), but nothing that would be natural for
         | complex objects.
         | 
         | Consider this array: [{ key: {}, foo: 1 }, { key: {}, foo: 2
         | }], there are multiple answers how a Map can look when you
         | group by 'key'. Both comparing the keys using '===' and deep
         | equality are valid options, depending on needs.
        
         | beebeepka wrote:
         | Key strings can also contain emojis. Not that I would recommend
         | it in most situations
        
           | VWWHFSfQ wrote:
           | it's just utf8 bytes right
        
       | badjeans wrote:
       | Hardly shorter than just writing the for loop, i.e. the example
       | in python                   groupByAge =
       | collections.defaultdict(list)              for person in people:
       | groupByAge[person["age"]].append(person)
        
         | shhsshs wrote:
         | In JS you would also need to check if the key exists first (so
         | you can add it). I think the minimal example would be:
         | const groupByAge = {}         for (const person in people) {
         | if (!groupByAge[person.age]) {
         | groupByAge[person.age] = []           }
         | groupByAge[person.age].push(person)         }
         | 
         | IMO it is a very nice change to now be able to write:
         | const groupByAge = people.groupBy(p => p.age)
        
           | SahAssar wrote:
           | A reduce version seems quite okay to me though, and keeps it
           | pretty functional-styled.                   const groupByAge
           | = people.reduce((previous, current) => {           if
           | (!previous[current.age]) previous[current.age] = []
           | previous[current.age].push(current)           return previous
           | }, {})
        
       | ksbrooksjr wrote:
       | Unfortunately the typescript definitions for these two methods
       | haven't been written yet [1].
       | 
       | [1] https://github.com/microsoft/TypeScript/issues/47171
        
       ___________________________________________________________________
       (page generated 2021-12-29 23:01 UTC)