Array.prototype.toObjKey and Array.prototype.toObjFunc

I find myself doing this many many times and think that it really belongs as a native function on the Array prototype.

If i want to convert an array of recs into a keyed object of the same recs I do this


let ary = [ 
    {id: 'myid1' field1:'abc', field2: 'abc' },
    {id: 'myid1' field1:'abc', field2: 'abc' }  
]
let oAry = ary.reduce((prev,rec)=> ({...prev,[rec.id] : rec}),{})

Coverting from an array to an object is as ubiquitous as ++ in ones code. As such it should be part of the language.

To accomplish this I have created these two functions that appear to be natural fits for extension to the Array prototype.

 Array.prototype.toObjKey   = (ary,key , inOjb={})=> 
      ! ary.length || ! key   ? inOjb  
     : ary.reduce((prev,rec,ix)=> ({...prev,[rec[key]] : rec     }), inOjb )
 
 Array.prototype.toObjFunc  = (ary,func, inOjb={})=> 
      ! ary.length || ! func  ? inOjb 
      : ary.reduce((prev,rec,ix)=> ({...prev,... func(rec,ix,ary) }), inOjb )

These two Array.prototype proposed additions are a natural extension to "Reduce" type functionality that nearly every javascript REST implementation performs .

Low technical risk, low code bloat, easy to understand. What's not to love?

One way this can already be achieved is using Object.fromEntries

Object.fromEntries(ary.map(o => [o.id, o]))

Or using a util like Lodash keyBy

_.keyBy(arr, 'id');

EDIT:

If a method like this was added I wonder if using a Map instead of a plain object would be better as it has support for more key types, rather than just strings.

Array.prototype.toMapKey = function(key) {
  let keyGetter = key;
  if (typeof key === 'string') {
    keyGetter = (o) => o[key];
  }
  let map = new Map();
  for (let v of this) {
       map.set(keyGetter(v), v);
  }
  return map;
}
[
  { 'key': 1, prop2: 'foo' },
  { 'key': 2, prop2: 'bar' },
].toMapKey('key');

// or

[
  { 'key': 1, prop2: 'foo' },
  { 'key': 2, prop2: 'bar' },
].toMapKey(o => o.key);
// both return Map { 1 =>  { 'key': 1, prop2: 'foo' }, 2 =>  { 'key': 2, prop2: 'bar' } }

If toMapKey returns a map instead of an object not sure it is the same result. I like the idea as a separate function but what i am looking for is something that just returns a vanilla javascript object with the right keys. This is the most common use case.

To be perfectly honest I have never found a reason to use map over object. Perhaps it is just my ignorance. (that being said I also only use Set to dedupe arrays :) )

Also Not a big fan of adding lodash to every package.json. Lodash is a great reference to figure out how to do things but adds to complexity and code bloat. On the other hand if there was such thing as a ecma blessed standard template library a la c++ for javascript that was guaranteed to be minimal (and not an extra repo that must be included and maintained) that would be great.

Also like your suggestion about Object.fromEntries ... perfect replacement for the toObjFunc I suggested which is a bit of a ham fisted solution . That being said, converting to and from objects and arrays is at the core of how javascript is used and a "toObjKey" (or "keyBy" per lodash naming) really should be part of the Array prototype.

1 Like

I like Object.fromValues(values, key) better - and furthermore, this should also be added to Map for similar reasons. And yes, I've ran into this a lot myself, though something like Object.mapValues(...) would be more immediately useful. (Engines could and should optimize for key uniqueness, though.)

Not sure I see the benefit of accepting a source object when you can just do Object.assign(target, Object.fromValues(...)) instead.

1 Like