Array.prototype.toObjectByProperty( element=>element.property )

# Naveen Chawla (7 years ago)

I've often needed to cache array elements by a unique key each element has, for quick access.

This is a shortcut:

const elementsById = elements.toObjectByProperty(element=>element.id);

Support?

# Lachlan Hunt (7 years ago)

On 2017-08-05 20:18, Naveen Chawla wrote:

I've often needed to cache array elements by a unique key each element has, for quick access.

This is a shortcut:

const elementsById = elements.toObjectByProperty(element=>element.id);

That looks like what Array.prototype.map can already do.

# T.J. Crowder (7 years ago)

On Sat, Aug 5, 2017 at 11:18 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:

I've often needed to cache array elements by a unique key each element has, for quick access.

Yes, please. I've needed this in every codebase I've ever worked on. For me, having it in the standard lib would be a win. (Yes, you can easily do it by (ab)using reduce, but A) It's arguably an abusage [one I'm increasingly resigning myself to], and B) Why keep re-writing it?)

Some off-the-cuff suggestions:

  1. Put it on Object: Object.from.

  2. Have a Map version: Map.from.

  3. Accept any iterable, not just an array.

  4. Accept a string, Symbol, or callback: If a string or Symbol, use the matching property as the key for the result. If a callback, use the callback's return value (your example).

  5. Optionally accept the object or map rather than creating a new one.

So something like:

Object.from(iterable, indexer, target = Object.create(null))

and

Map.from(iterable, indexer, target = new Map)

-- T.J. Crowder

# T.J. Crowder (7 years ago)

On Sat, Aug 5, 2017 at 12:22 PM, Lachlan Hunt <lachlan.hunt at lachy.id.au> wrote:

On 2017-08-05 20:18, Naveen Chawla wrote:

I've often needed to cache array elements by a unique key each element has, for quick access.

That looks like what Array.prototype.map can already do.

No, the end result is an object, not an array. It probably would have helped if Naveen had included an example. Here I'm using Object.from (see my previous email) rather than toObjectByProperty:

const a = [{id: "tjc", name: "T.J. Crowder"}, {id: "nc", name: "Naveen
Chawla"}, {id: "lh", name: "Lachlan Hunt"}];
const index = Object.from(a);
console.log(index);

would yield:

{
    tjc: {id: "tjc", name: "T.J. Crowder"},
    nc: {id: "nc", name: "Naveen Chawla"},
    lh: {id: "lh", name: "Lachlan Hunt"}
}

-- T.J. Crowder

# T.J. Crowder (7 years ago)

On Sat, Aug 5, 2017 at 12:41 PM, T.J. Crowder <tj.crowder at farsightsoftware.com> wrote:

Apologies, better proofreading required, left out the "id" argument:

const a = [{id: "tjc", name: "T.J. Crowder"}, {id: "nc", name: "Naveen
Chawla"}, {id: "lh", name: "Lachlan Hunt"}];
const index = Object.from(a, "id");
console.log(index);

-- T.J. Crowder

# Lachlan Hunt (7 years ago)

On 2017-08-05 21:47, T.J. Crowder wrote:

On Sat, Aug 5, 2017 at 12:41 PM, T.J. Crowder <tj.crowder at farsightsoftware.com> wrote:

Apologies, better proofreading required, left out the "id" argument:

const a = [{id: "tjc", name: "T.J. Crowder"}, {id: "nc", name: "Naveen
Chawla"}, {id: "lh", name: "Lachlan Hunt"}];
const index = Object.from(a, "id");
console.log(index);

OK, I misunderstood the original request. But even so, as you noted in your previous mail, Array.prototype.reduce can handle it.

 a.reduce((obj, value) => (obj[value.id] = value, obj), {})

But basically, is the proposal for a native version of Lodash's _.keyBy() method?

lodash.com/docs/#keyBy

# T.J. Crowder (7 years ago)

On Sat, Aug 5, 2017 at 1:05 PM, Lachlan Hunt <lachlan.hunt at lachy.id.au> wrote:

But basically, is the proposal for a native version of Lodash's _.keyBy() method?

Well, that's how I read it, yes; along those lines anyway.

-- T.J. Crowder

# kdex (7 years ago)

Inline.

On Saturday, August 5, 2017 1:35:35 PM CEST T.J. Crowder wrote:

On Sat, Aug 5, 2017 at 11:18 AM, Naveen Chawla

<naveen.chwl at gmail.com> wrote:

I've often needed to cache array elements by a unique key each element has, for quick access.

Yes, please. I've needed this in every codebase I've ever worked on. For me, having it in the standard lib would be a win. (Yes, you can easily do it by (ab)using reduce, but A) It's arguably an abusage [one I'm increasingly resigning myself to], and B) Why keep re-writing it?)

Why do you consider this an abuse of reduce? The problem at hand is to reduce an iterable to a single value with certain properties. And I would like to think that reducing is what reduce is supposed to do. ;)

Could you please elaborate on that?

# T.J. Crowder (7 years ago)

On Sat, Aug 5, 2017 at 3:08 PM, kdex <kdex at kdex.de> wrote:

Could you please elaborate on that?

It's not important here. :-) I've replied off-list. (If anyone feels strongly that the reply is relevant, let me know and I'll repost it to the list, but...)

-- T.J. Crowder

# Darien Valentine (7 years ago)

FWIW, while I find needs like this common, too, where Map is sensible instead of Object, it does come out pretty clean:

const a = [
  {id: "tjc", name: "T.J. Crowder"},
  {id: "nc", name: "Naveen Chawla"},
  {id: "lh", name: "Lachlan Hunt"}
];

const index = new Map(a.map(member => [ member.name, member ]));

Although I’m also puzzled by the suggestion that reducing to an object is an abuse, I do find I wish there were a complement to Object.entries:

// Object to pairs, and therefore map, is simple:

const map = new Map(Object.entries(obj));

// Converting back is also simple ... but not exactly expressive, if you inline it:

[ ...map ].reduce((acc, [ key, val ]) => Object.assign(acc, { [key]: val }), {});

Something like Object.fromEntries would not provide as much sugar for the OP case as toObjectByProperty, but it gets pretty close and has the advantage of being more generic; toObjectByProperty strikes me as rather specific for a built-in, especially since one might want to map by a derived value rather than a property. Both map<->object and array<->object cases would become more expressive — plus it follows pretty naturally from the existence of Object.entries that there might be a reverse op.

Object.fromEntries(a.map(member => [ member.name, member ]));

In other words, Object.fromEntries(Object.entries(obj)) would be equivalent in effect to Object.assign({}, obj).

Would that adequately address this case you think? My sense is that it’s better to supply generic helpers before more specific helpers when it comes to built-ins.

# Isiah Meadows (7 years ago)

I have a better idea: how about Object.from(iter), where iter is an iterable of [key, value] pairs?

const a = [
  {id: "tjc", name: "T.J. Crowder"},
  {id: "nc", name: "Naveen Chawla"},
  {id: "lh", name: "Lachlan Hunt"}
];

const object = Object.from(a.map(o => [o.id, o.name]))

We already have Object.entries(object), returning an array of [key, value] pairs, so this would effectively be the inverse of that - Object.from(Object.entries(object)) would be equivalent to Object.assign({}, object) assuming native prototypes are unmodified.

(Yes, it's like _.zipObject(pairs) from Lodash)

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Jordan Harband (7 years ago)

If anything were to be added, it would surely be a static method that took an iterable of entries and returned an object, and it would surely have the semantic that new Set(Object.entries(thisThing(entries))) and new Set(entries) were two conceptually equal sets (iow, that each corresponding entry's key were ===, modulo ToPropertyKey conversion, and each corresponding entry's value were ===).

I'm not necessarily proposing this at all or any time soon, but one thing that would help me or a future champion (if proposed) is if interested parties wrote up a convincing proposal readme - rationale, similar patterns in the ecosystem, motivating use cases, etc.

# Darien Valentine (7 years ago)

@Isiah I think you and I just described the identical thing (except that I’d put fromEntries where you put from) — and it’s a subset of the overloaded proposed solution from Crowder above. That three people responded with the same thing or a variation of it suggests that this is indeed a gap where a thing intuitively ought to exist.

@Harband Unless somebody else wants to, I’d be interested in trying my hand at assembling a proper proposal. I think I’ve spent enough time in the spec & various proposals at this point that I might not totally butcher it.

# Darien Valentine (7 years ago)

Awesome. I didn’t know about the upcoming template, so I may have jumped the gun haha:

bathos/object-from-entries

It’s quite primitive though, no formal algorithm / ecmarkup or anything. I’ll keep an eye on the template project and update accordingly once that’s ready, thanks.


Edit: oops, this was in response to a message from Jordan Harband that I didn’t realize was off-list (it showed up in the same email thread in gmail), hence the odd first sentence.

# T.J. Crowder (7 years ago)

On Sat, Aug 5, 2017 at 9:42 PM, Darien Valentine <valentinium at gmail.com> wrote:

FWIW, while I find needs like this common, too, where Map is sensible instead of Object, it does come out pretty clean:

const a = [
  {id: "tjc", name: "T.J. Crowder"},
  {id: "nc", name: "Naveen Chawla"},
  {id: "lh", name: "Lachlan Hunt"}
];

const index = new Map(a.map(member => [ member.name, member ]));

And:

On Sun, Aug 6, 2017 at 7:31 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:

I have a better idea: how about Object.from(iter), where iter is an iterable of [key, value] pairs?

For the common case I have, that would require an additional transform producing a lot of unnecessary temporary objects. I basically never have [ [key, value], [key, value] ...] as my starting point where I want this.

On Sat, Aug 5, 2017 at 9:42 PM, Darien Valentine <valentinium at gmail.com> wrote:

Although I’m also puzzled by the suggestion that reducing to an object is an abuse...

Let's ignore that, sorry; it's purely a distraction in this conversation.

toObjectByProperty strikes me as rather specific for a built-in, especially since one might want to map by a derived value rather than a property.

Naveen's original post shows a callback that returns the key to use. I also flag up using a callback to determine the value in my earlier post on this. (In my post, I cater to the common case of a specific property known in advance, but also allow for a callback; like String#replace's second parameter.)

Would that adequately address this case you think?

For me, no; an unnecessary additional transform and too much memory churn.

My sense is that it’s better to supply generic helpers before more specific helpers when it comes to built-ins.

In general, absolutely. But where the abstraction takes us into unnecessary complexity and performance degradation, slightly more specificity is fine in my view. I don't think my suggested Object.from and Map.from are too specific.

-- T.J. Crowder

# Darien Valentine (7 years ago)

For me, no; an unnecessary additional transform and too much memory churn.

Interesting. I’ve included a note in the 'concerns' section on that proposal regarding the possibility of accepting a mapping function (like Array.from). That could avoid the extra array creation. However I suspect the idea of accepting a string or symbol key rather than a function is unlikely to gain traction — for one, there’s nothing like it presently in the many places there could be, and perhaps more importantly, that overloading represents an additional cost for every use case, even if it might reduce the cost for one. We’re talking very small perf differences here of course, but one of the reasons many lodash tools are not particularly fast is the overhead that comes from supporting heavy overloading.

Assuming that proposal isn’t a flop, please feel free to open an issue on the repo making the case for the map argument (in whatever form) — that could help measure whether that’s something most people expect to see.

# T.J. Crowder (7 years ago)

On Mon, Aug 7, 2017 at 2:23 PM, Darien Valentine <valentinium at gmail.com> wrote:

For me, no; an unnecessary additional transform and too much. memory churn.

However I suspect the idea of accepting a string or symbol key rather than a function is unlikely to gain traction — for one, there’s nothing like it presently in the many places there could be

Where are you thinking of?

...and perhaps more importantly, that overloading represents an additional cost for every use case, even if it might reduce the cost for one.

That's a fair concern, although the test is trivially simple and very fast. I'll flag up again the precedent of String#replace. In my experience, the string use case is overwhelmingly the common one. So slowing down the common case because of edge cases outweighs that concern for me. In spec terms, I'd test first for string, then function, then Symbol. (In polyfill terms, it'd be a switch on typeof.)

Assuming that proposal isn’t a flop, please feel free to open an issue on the repo making the case for the map argument...

I don't think your Object.fromEntries isn't similar enough to what I want. I'll create a proposal for what I want in a few days after more feedback has come in.

You seem to have had some private reply about an upcoming proposal template...?

-- T.J. Crowder

# Darien Valentine (7 years ago)

Where are you thinking of? [re: other methods that could take overloaded/shorthand map/predicate args, but do not]

These could also accept strings for property mapping in the same manner you proposed, but do not currently:

  • Array.from
  • Array.prototype.every
  • Array.prototype.filter
  • Array.prototype.map
  • Array.prototype.some
  • new Map
  • new Set

Mind, I’m not arguing that it isn’t a useful idea. Where lodash has equivalents for the above, strings are permitted, and I’m sure people make use of that functionality.

I think the spot where we might disagree is the assessment that "the string case is overwhelmingly the common one." This doesn’t sound quite right to me. That’s not been true in my experience, though I don’t have any data to say concretely one way or another. In any case, I’m not out to block anything if smarter people think it’s a good idea.

I'll create a proposal for what I want in a few days after more feedback has come in.

Would you consider the method you’ll be proposing to be "instead of" a reverse-of-object-entries type proposal, or is it orthogonal to that?

You seem to have had some private reply about an upcoming proposal template...?

Yeah — it doesn’t exist yet, however.

# T.J. Crowder (7 years ago)

On Mon, Aug 7, 2017 at 4:17 PM, Darien Valentine <valentinium at gmail.com> wrote:

Where are you thinking of? [re: other methods that could take

overloaded/shorthand map/predicate args, but do not]

These could also accept strings for property mapping in the same manner

you proposed, but do not currently:

  • Array.from
  • Array.prototype.every
  • ...

I think it comes down to what the majority use case is. For most of those I've never used just a single property access. But I certainly have for map and filter, I see your point there. It's not been anything like my majority use case for those two, but definitely not rare.

I think the spot where we might disagree is the assessment that "the string case is overwhelmingly the common one." This doesn’t sound quite right to me. That’s not been true in my experience...

:-) Your mileage may vary, but I'm surprised by that; surely you use maps keyed by a property of the object they map to? The times I've wanted to generate the key rather than just pluck it can be counted on one finger (since Object.create; prior to that, I was prefixing names in index objects to avoid Object.prototype collisions, back in the day). Whereas wanting to use an existing property as the index key I've done hundreds of times if not more. For instance, an app with a list of...well, almost anything, where I want access both as a list (for iteration; showing the list) and random access by ID (for showing details in response to a pick).

I'll create a proposal for what I want in a few days after more feedback has come in.

Would you consider the method you’ll be proposing to be "instead of" a reverse-of-object-entries type proposal, or is it orthogonal to that?

Ortogonal I think. Your Object.fromEntries works with an iterable of [key, value] pairs (or, I'm guessing, a callback that can produce [key, value] pairs). My Object.from works with an iterable of any kind of object where the object itself is expected to be the value. I think trying to shoehorn both in to one function would over-complicate it.

-- T.J. Crowder

# Naveen Chawla (7 years ago)

The concept of using a 2-element array to represent a key and value is unique to Maps and if I'm not mistaken, exists purely because there wasn't a tight, compact alternative that could house a key and value where the key could be an object.

However, if we are going straight from iterable to Object, I don't think we should be propagating the "[key, value] as an array" pattern. It's unclean partly because it's 2 elements max and IDEs can't inherently show warnings if you accidentally added more, unless it has some intelligence about the method itself. Partly also because the arrays are not immediately readable unless you already know the that the arrays mean [key, value].

I'm not convinced that Maps have any real value at all right now vs objects (despite what the documentation says): currently they only offer the ability to have objects as keys but without custom key equality (only ===), and you lose the elegant square bracket syntax for accessing elements, among other things, so it's unclear to me how compelling the use of Maps vs objects is at all in real world scenarios, so I'm not sure anything about Maps should be a pattern for other language features.

Iterables are very generic, since they can include generators, sets etc. Objects are a fundamental construct, so it makes sense to be able to cleanly transition between these structures.

Adding the idea to iterable in general (instead of just Array) would look like e.g.:

const cache = iterable.toObjectByProperty(item=>item.id)
# Jussi Kalliokoski (7 years ago)

On Mon, Aug 7, 2017 at 10:24 PM, Naveen Chawla <naveen.chwl at gmail.com>

wrote:

However, if we are going straight from iterable to Object, I don't think we should be propagating the "[key, value] as an array" pattern.

Consistency is good, and especially better than for example adding new syntax. Subjectively not a big fan of doing single pair computed key objects either: return { [key]: value } vs return [key, value]. Arrays also have a better set of standard library functions available which makes preprocessing easier, for example reversing keys and values.

It's unclean partly because it's 2 elements max and IDEs can't inherently show warnings if you accidentally added more, unless it has some intelligence about the method itself. Partly also because the arrays are not immediately readable unless you already know the that the arrays mean [key, value].

You're probably not referring to hand-writing lists of key-value pairs (in which case you should just use an object literal to begin with), but yes, text editors inherently don't help much with code validity to begin with, but type systems such as flow make it pretty easy1 to catch this sort of thing:

/* @flow */

function objectFrom<KeyType, ValueType>(
  iterable : Iterable<[KeyType, ValueType]>
) : {
  [key : KeyType]: ValueType
} {
  const object : { [key : KeyType]: ValueType } = {};

  for (const [key, value] of iterable) {
    object[key] = value;
  }

  return object;
}

objectFrom([["key", 2], [null, "value"], [1, 3]]) // <- all good
objectFrom([["key", "value", "other"]]) // <- ERROR: Tuple arity mismatch.
This tuple has 3 elements and cannot flow to the 2 elements of...
objectFrom([1,2,3,4,5].map(x => [x, x])) // <- all good

objectFrom([1,2,3,4,5].map(x => Array(x))) // <- // <- ERROR: Tuple arity

mismatch...

IMO it's generally a good idea to use stronger correctness guarantees than editor syntax highlighting / validation anyway.

Regarding Map.from(...), not sure how that would be useful since the map constructor already accepts an iterator. Maybe when passing to something that expects a function, but then adding arity (for example a mapper function) becomes a hazard. The partial application proposal might help with this tho: x.map(Map.from(?)).

I'd be happy to see something like Object.from(iterable) and maybe even Map.from(iterable) in the core library - simple, generic and common. Not so convinced about the mapper functions - if an engine doesn't bother optimizing away the extra allocation in Object.from(x.map(fn)) I don't see why it would bother optimizing Object.from(x, fn) either. Please correct me if and how I'm wrong though.

# Darien Valentine (7 years ago)

The concept of using a 2-element array to represent a key and value is unique to Maps

The entries pattern is not unique to Map — it’s also used by Object.entries, Set.entries, and Array.entries — e.g.

for (const [ index, member ] of array.entries()) ...

A bit tangential, but in the case of Map, it’s perhaps notable that the contract for "entry" is not "an array of two and only two members". It is "any object at all", with the implied expectation that you’d want properties keyed by "0" and "1" defined on it in order for it to be useful.

Regardless of one’s thoughts on Map (which I use quite a bit personally), the "entries" pattern is well established in ES. (I’d also point out that adding a method for composition from entries does not preclude supporting other approaches.)

# Naveen Chawla (7 years ago)

Very interesting!

Set.entries() is interesting - it has [value, value] with the justification (according to MDN) "to keep the API similar to the Map object".

Array entries() - it's really unclear why this would ever be used, do you have any idea?

Object entries() is another strange case. Iterating over Object.keys with key=> leaves myObject[key] to access the value, whereas iterating over

Object.entries with entry=> requires entry[0] and entry[1] which seems more

verbose for access, and less readable! So do you know why this was introduced?

All seem motivated by Map. And I think the pattern is unhealthy, for the reasons given before.

Sometimes we need to look at how code looks out there in production, to appreciate the benefits of a language feature. Imagine the mapping of an array passed into a function (or a reduce), repeated in code for several arrays that need caching for quick access - obviously you would expect that to be factored out into a toObjectByProperty function in code, right? That's what I'm talking about. Now imagine that transition function offered by iterable - an extra dependency useful across multiple projects is gone. It's only nice. Isn't it?

# Jordan Harband (7 years ago)

Entries are an established pattern. Relitigating it isn't going to be useful. Something like this would only make sense if it accepted either an iterable of entries, or an array of entries (which is strictly less useful than taking an iterable) - no other input value would make sense to me.

# Logan Smyth (7 years ago)

Object entries() is another strange case. Iterating over Object.keys with

key=> leaves myObject[key] to access the value, whereas iterating over

Object.entries with entry=> requires entry[0] and entry[1] which seems more

verbose for access, and less readable! So do you know why this was introduced?

Keep in mind that the pair syntax plays quite nicely with destructuring, so assuming the iteration you're describing is something like

for (const key of Object.keys(myObject)) {
  const value = myObject[key];

  // do stuff
}

I at least think it's much more readable to do

for (const [key, value] of Object.entries(myObject)) {

  // do stuff
}

Array entries() - it's really unclear why this would ever be used, do you

have any idea?

The same is nice for iterating arrays when you need the index

for (const [i, value] of ['one', 'two', 'three', 'four'].entries()) {

  // do stuff
}

Imagine the mapping of an array passed into a function (or a reduce),

repeated in code for several arrays that need caching for quick access - obviously you would expect that to be factored out into a toObjectByProperty function in code, right? That's what I'm talking about. Now imagine that transition function offered by iterable - an extra dependency useful across multiple projects is gone. It's only nice. Isn't it?

Generally at least for me I've transitioned to using Map objects for this type of lookup. It's slightly longer to use .get, but at least for me it hasn't been particularly troublesome. There are certainly performance arguments to be made for polyfilled implementations, but that will only decrease over time.

# Naveen Chawla (7 years ago)

On Tue, 8 Aug 2017 at 03:25 Jordan Harband <ljharb at gmail.com> wrote:

Entries are an established pattern. Relitigating it isn't going to be useful. Something like this would only make sense if it accepted either an iterable of entries, or an array of entries (which is strictly less useful than taking an iterable) - no other input value would make sense to me.

They are established as a feature in the language. I am not trying to relitigate that. I'm simply saying that when transitioning from a typical iterable to an object we shouldn't be forced into it as an intermediary, besides which that doesn't offer any advantage over the reduce mentioned earlier in this thread, and even has worse performance. The [key, value] pattern was originally introduced for Maps to allow objects as keys, which isn't even valid in an object, besides the pattern not being a tight structure in of itself.

# Naveen Chawla (7 years ago)

On Tue, 8 Aug 2017 at 03:28 Logan Smyth <loganfsmyth at gmail.com> wrote:

Object entries() is another strange case. Iterating over Object.keys with key=> leaves myObject[key] to access the value, whereas iterating over Object.entries with entry=> requires entry[0] and entry[1] which seems more verbose for access, and less readable! So do you know why this was introduced?

Keep in mind that the pair syntax plays quite nicely with destructuring, so assuming the iteration you're describing is something like

for (const key of Object.keys(myObject)) {
  const value = myObject[key];

  // do stuff
}

I at least think it's much more readable to do

for (const [key, value] of Object.entries(myObject)) {

  // do stuff
}

Yes unfortunately this doesn't apply to forEach, which I use much more frequently because it allows chaining after map, filter, sort, concat etc. and because it's slightly less verbose. I only use for..of when I need to break out of a loop, and which frankly I'll stop using once/if takeWhile/skipWhile are introduced.

# Darien Valentine (7 years ago)

forced into it as an intermediary

Fortunately, no force is involved :)

T.J. Crowder indicated an intention to draft a proposal that I believe aligns with your ideas for converting objects to a single object by key with an all-in-one map+compose method. It is not necessary for there to be only a single proposal in this space — aside from the fact that having diversity of ideas on the table is healthy, the two ideas do not seem to be in conflict with each other.

has worse performance

It’s a bit hard to speak about the performance of methods no engine has implemented, but you might be overestimating the cost of Array.prototype.map.

The [key, value] pattern was originally introduced for Maps to allow objects as keys [...]

You described earlier a previous unfamiliarity with the various entries methods on collections. This might have contributed to an impression that entries are a concern of Map specifically. While Map introduced entries, in real-world code, I think it’s safe to say confidently that the frequency of use of Object.entries for iteration (as illustrated in Logan Smyth’s earlier comment) and transformative operations (as mentioned in Jussi Kalliokoski’s comment) dwarfs that of its use for map initialization.

Yes unfortunately this doesn't apply to forEach, [...]

Are you referring to destructuring? It does:

const obj = { apples: 2, bananas: 17, blueberries: 9 };

const numberOfFruitsThatStartWithB = Object
  .entries(obj)
  .filter(([ key ]) => key.startsWith('b'))
  .reduce((acc, [ , value ]) => acc + value, 0);
# Jordan Harband (7 years ago)

If you have an object, you can already Object.assign({}, object), or Object.defineProperties({}, Object.getOwnPropertyDescriptors(object)), with no intermediary, as of ES2015 and ES2017 respectively.

The value here for something new would be Object.fromEntries(Object.entries(object).map(…).filter(…)) or similar.

I haven't seen any suggestions for anything different than those three patterns; am I missing something?

# Darien Valentine (7 years ago)

I haven't seen any suggestions for anything different than those three patterns; am I missing something?

The draft I assembled does follow that pattern, but there was another approach suggested which is not concerned with entries and is instead similar to lodash’s keyBy method; it also permits supplying a target object rather than always creating a new object.


Gah, I always manage to screw up the recipient field on these threads.

# Naveen Chawla (7 years ago)

Another benefit of iterable toObjectByProperty(element=>element.id) is that

it can be placed at the end of a transformation chain of map sort filter concat etc.:

const
    cache =
        sourceIterable
            .filter(item=>item.isValid)
            .toObjectByProperty(item=>item.id)

It'd be great to know if anyone from TC39 is going to propose these ideas (could I submit a README.md in order to help? If so where?)

# T.J. Crowder (7 years ago)

On Tue, Aug 8, 2017 at 7:18 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:

It'd be great to know if anyone from TC39 is going to propose these ideas (could I submit a README.md in order to help? If so where?)

Proposals come from the community (of which TC39 members are a part). What you've done so far is a suggestion. For it to proceed, someone has to create a proper proposal for it.

Darien's already created one based on his initial interpretation of your idea. I intend to do a separate one later this week when I have time and after more reflection/feedback, based on what I've wanted for several years -- which is, I think, closer to what you originally suggested. As he said, the two may or may not compete; I don't think they do, I think they complement one another.

Then we'll see if a TC39 committee member decides to champion either, both, or neither of them (or any other that may also be made).

-- T.J. Crowder

# Naveen Chawla (7 years ago)

I'd like to propose an enhancement to my proposal:

const cache = iterable.toObject(item=>item.key, item=>item); //2nd param is

optional, default shown

It offers the same functionality, but in addition a second optional parameter for the "value" in case you want something other than the array element as the value. By default, if the second parameter isn't populated, it should use the array element itself, like the example call redundantly passes.

# T.J. Crowder (7 years ago)

On Tue, Aug 8, 2017 at 9:39 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:

I'd like to propose an enhancement to my proposal: ... It offers the same functionality, but in addition a second optional parameter for the "value" in case you want something other than the array element as the value. By default, if the second parameter isn't populated, it should use the array element itself, like the example call redundantly passes.

This is part of why Darien's proposal and my suggestion from my first reply in this thread are complementary: That's exactly what you get with Darien's, by returning [key, value]. Conflating them makes each of them unnecessarily complicated in my view.

-- T.J. Crowder

# Naveen Chawla (7 years ago)

Yeah except in what I'm saying it's optional

# T.J. Crowder (7 years ago)

On Tue, Aug 8, 2017 at 10:01 AM, Naveen Chawla <naveen.chwl at gmail.com>

wrote:

Yeah except in what I'm saying it's optional

My point is you'd use the right tool for the job at hand, rather than having a single tool with a bunch of options making it complex to explain, use, and optimize. If you review my earlier suggestion and the back-and-forth with Darien, I think you'll see what I mean.

-- T.J. Crowder

# Naveen Chawla (7 years ago)

Philosophically I agree completely with this principle.

This does not have a bunch of options:

iterable.toObject(keyFromElement[, valueFromElement])

What I proposed has only 1 variant: the fact that valueFromElement has a default if you don't provide it.

Objects as they are are simple per insertion: 1. key, 2. value.

It's not straightforward to do my (latest) proposal without it, in particular if the object property values you want doesn't contain the key itself because it's derived from a different piece of data from which it doesn't exist.

# Naveen Chawla (7 years ago)

Furthermore, if you use entries, this allows [key, value] entries with object keys to be transformed into objects (which is not allowed by Object.fromEntries):

const cache = entries.toObject(entry=>entry[0].type, entry=>entry[1]);
# T.J. Crowder (7 years ago)

On Tue, Aug 8, 2017 at 11:39 AM, Naveen Chawla <naveen.chwl at gmail.com>

wrote:

Furthermore, if you use entries, this allows [key, value] entries with object keys to be transformed into objects (which is not allowed by Object.fromEntries):

With respect, please do have a thorough read of my first reply in this thread, the ensuing discussion with Darien, and Darien's proposal, in particular this bit of it. Those various sources explain my comment about complexity and how Object.fromEntries might do what you want.

-- T.J. Crowder

# Naveen Chawla (7 years ago)

OK thanks for the link - can you explain where the complexity is in my proposal?

# Naveen Chawla (7 years ago)

I do not use entries so I would not use Object.fromEntries. For arrays I could just use reduce, instead of transforming to [key, value] entries, before factoring it into an arrayToObject function in my code (which I already do) when I want to do it from more than one place in my code - if there existed no more direct way of transforming from iterable to object. All entries arrays are iterable, but not all iterables are entries arrays, which means that iterable is the more generic concept than entries, meaning that iterable, not entries, is the more suitable starting point for this functionality.

Object.fromIterable(iterable, keyFromElement[, valueFromElement]) is more verbose than iterable.toObject(keyFromElement[, valueFromElement]) and doesn't allow chaining after iterable transformation methods (likefilter` etc.)

# Jordan Harband (7 years ago)

An iterator is just an object with a next method. There's no consistent place to put any prototype methods on all iterables, so that's a nonstarter imo.

# Naveen Chawla (7 years ago)

You're thinking of iterator

# Naveen Chawla (7 years ago)

It would be in the iteratable protocol (interface)

# Naveen Chawla (7 years ago)

iterable, excuse me

# Jordan Harband (7 years ago)

Something is iterable if it has Symbol.iterator. Are you saying that every iterable would then need to add a toObject method? What happens if it doesn't add it? What value is it if most iterables don't have toObject but only some do?

# T.J. Crowder (7 years ago)

On Wed, Aug 9, 2017 at 8:35 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:

It would be in the iteratable protocol (interface)

As Jordan said, that's likely to be a nonstarter. The Iterable protocol is very lean (exactly one required property) for a reason: So it can be supported with minimum investment. Much better, IMHO, to put functions on Object and Map (which is why that's what I suggested).

-- T.J. Crowder

# Naveen Chawla (7 years ago)

The toObject behaviour doesn't need to be "implemented" on a per-iterable class basis. It has a constant behaviour: iterate and on each next(), pass the value to the toKeyFromElement and toValueFromElement callbacks to generate and return an object. There must be some construct by which that can be achieved. I wouldn't call it "better" to put it on Object (for the reasons stated), but rather a compromise in the absence of any such construct

# Naveen Chawla (7 years ago)

Java has a great example of such a construct: default interface methods

# Naveen Chawla (7 years ago)

But I accept that this a very tall order for ES

# Jordan Harband (7 years ago)

JS doesn't have interfaces (yet, tho there's a proposal) and regardless, the "interface" for "iterable" is "it has Symbol.iterator, nothing more".

The only place a method like this - that produces an object - could possibly exist, is a static method on Object.

I've already outlined two existing methods to copy one object's entries to another; the only new functionality would be "creating an object from entries", hence Object.fromEntries or similar.

I still haven't seen any use cases that aren't covered by the existing "copy one object to another", or by a possible "entries to object" - does anyone have any?

# Naveen Chawla (7 years ago)

Iterable to object via Object.fromIterable

# Naveen Chawla (7 years ago)

It is more generic than fromEntries

# Jordan Harband (7 years ago)

I think you're misunderstanding; the function would of course take an iterable. However, an iterable of what?

If it's an iterable of objects, then what's the key and what's the value? What if it's an iterable of strings?

The only thing that makes sense is if it's an iterable that provides both a key and a value - and "entries" is the idiomatic structure in the language to respect a list of key/value pairs (besides "an object", of course).

What would you suggest?

# T.J. Crowder (7 years ago)

On Wed, Aug 9, 2017 at 9:24 AM, Jordan Harband <ljharb at gmail.com> wrote:

I think you're misunderstanding; the function would of course take an iterable. However, an iterable of what?

If it's an iterable of objects, then what's the key and what's the value? What if it's an iterable of strings?

I can't speak for Naveen, he's gone off in his own direction (or rather, I have, as it was originally his thread).

But for my part, as I describe here, the common case I want to address is that the value is the entry. No round-trip through [key, value] pairs is required for the common use case of creating a map/object to provide lookup by key. I certainly see why Darien may also want to provide somthing for [key, value] pairs, but I see it as separate.

-- T.J. Crowder

# Naveen Chawla (7 years ago)

great question.

An iterable of anything!

This is the signature:

Object.fromIterable(iterable, keyFromElement[, valueFromElement])

Examples follow:

Supposing you had an array:

[
    {
        countryName: 'UK',
        population: 65640000
    },
    {
        countryName: 'USA',
        population: 323100000
    },
    {
        countryName: 'Denmark',
        population: 5731000
    }
]

...and you wanted to cache the items by country name for quick access, to get:

{
    UK: {
        countryName: 'UK',
        population: 65640000
    },
    USA: {
        countryName: 'USA',
        population: 323100000
    },
    Denmark: {
        countryName: 'Denmark',
        population: 5731000
    }
}

...you would simply do

const countriesByName = Object.fromIterable(countries, country=>country.countryName);

to get that result. (the 3rd parameter defaults to return the iterated value if not provided).

As callbacks, the keyFromElement and valueFromElement parameters allow you to supply anything you like to transform from the iterated element (or from elsewhere) into the keys and values you want:

e.g. if you had an array of strings called recentCountryNames with ['UK', 'Denmark']:

const recentCountryDetailsByName =
    Object.fromIterable(
        recentCountryNames,
        countryName=>countryName,
        countryName=>countriesByName[countryName]
    )

would produce:

{
    UK: {
        countryName: 'UK',
        population: 65640000
    },
    Denmark: {
        countryName: 'Denmark',
        population: 5731000
    }
}

As an aside, it can easily transform entries as follows: Object.fromIterable(entries, entry=>entry[0], entry=>entry[1]) (if the entry "keys" happen to be valid object keys, otherwise you can simply provide a different transformation for the keyFromElement callback) but works equally well with all types of iterables

# Jordan Harband (7 years ago)

Naveen: `Object.fromIterable(recentCountryNames, countryName=>countryName,

countryName=>countriesByName[countryName])`

could also be:

Object.fromEntries(Array.from(recentCountryNames, countryName => ([countryName, countriesByName[countryName]]))), without needing a potentially confusing "keyCallback, entryCallback" API nor without builtins needing to invoke user-supplied functions.

TJ: I'm confused, can you provide a code sample?

# Naveen Chawla (7 years ago)

fromEntries is much less generic than fromIterable (since not every iterable is an entries, but every entries is an iterable) and is much more verbose to use in the use cases we have just discussed. I have faced such cases often, but have NEVER faced a need for a fromEntries. Have you? Even if so, I've shown how fromEntries functionality can be achieved via fromIterable without any additional method calls, which is not the case vice versa

# Jordan Harband (7 years ago)

"Generic" can't apply when constructing an object, since you always need pairs of a key and a value - so it doesn't need to be generic; it needs to fit the use case.

Yes, I've had many a need for "fromEntries" or similar, which I've usually achieved with a reduce. I've never once needed to create an object from an iterable of "not entries". Needless to say, "use cases" are subjective.

new Map accepts an iterable of entries - that's the established pattern that any new additions would almost certainly use.

# T.J. Crowder (7 years ago)

On Wed, Aug 9, 2017 at 10:56 PM, Jordan Harband <ljharb at gmail.com> wrote:

TJ: I'm confused, can you provide a code sample?

So based on the signatures I mentioned here:

Object.from(iterable, indexer, target = Object.create(null))
// and
Map.from(iterable, indexer, target = new Map)

(although byKey or similar may be a better name), then for instance, if I have this array in stuff.users:

[
    {id: "naveen.chowla", foreNames: "Naveen", surname: "Chowla"},
    {id: "tj.crowder", foreNames: "T.J.", surname: "Crowder"},
    {id: "jordan.harband", foreNames: "Jordan", surname: "Harband"}
]

then

stuff.usersById = Object.from(stuff.users, "id");

gives me a stuff.usersById that looks like this:

{
    "naveen.chowla":  {id: "naveen.chowla", foreNames: "Naveen", surname:
"Chowla"},
    "tj.crowder":     {id: "tj.crowder", foreNames: "T.J.", surname:
"Crowder"},
    "jordan.harband": {id: "jordan.harband", foreNames: "Jordan", surname:
"Harband"}
}

(Those are the same objects.)

That's the common case: A known-in-advance string key. I do that all the time. But the indexer parameter can also be a Symbol or a function; the function receives the entry and returns the key, so it can be a computed key. The test for what the indexer is is trivially simple and fast and need be done only once.

I can supply the target as a third argument if I want to create it myself for any reason (specific prototype, or I already have one with partial data and I'm adding more, etc):

stuff.users.push(...newUsers);
stuff.usersById = Object.from(newUsers, "id", stuff.usersById);

The Map version does the same thing where the result (and optional third parameter) is a Map instead.

-- T.J. Crowder

element-property#content

# Naveen Chawla (7 years ago)

By "much more generic" I mean that fromIterable more effortlessly fits a much wider range of use cases.

Can you give a code example of where your entries originate from? I'm curious because you might find it easier to apply fromIterable (since most server side API data arrays don't come in the form of "entries").

# Naveen Chawla (7 years ago)

I think a property value callback is needed for completeness (not easy to achieve the same functionality without it. Consider the examples given in the recent posts).

Keying by a string property name should, in my view, be a separate function:

Object.fromIterableByPropertyName(iterable, propertyName[,
valueFromElement[, existingObjectToUse]])

vs

Object.fromIterable(iterable, keyFromElement[, valueFromElement[,
existingObjectToUse]])
# T.J. Crowder (7 years ago)

On Thu, Aug 10, 2017 at 9:49 AM, Naveen Chawla <naveen.chwl at gmail.com>

wrote:

I think a property value callback is needed for completeness (not easy to achieve the same functionality without it.

In my view, a Swiss Army knife is not a good API function. (These are always, of course, judgement calls.) The [key, value] case can be addressed with Darien's Object.fromEntries. I've literally never needed that, but if one does, having something specific for it which fits into the [key, value] ecosystem makes sense to me. That's why I've said several times now that my suggestion and Darien's draft proposal are complementary, not conflicting/competing. In fact, it may be useful to combine them (but not the functions they define).

Separately, I wonder how a second callback (in the case where it's a callback) compares in terms of performance and memory churn with returning [key, value] arrays.

Keying by a string property name should, in my view, be a separate function

I don't see the need. If it does exactly the same thing, just using a string directly rather than calling a function, then like String#replace I'm happy for it to handle that. Again, though, these are always judgement calls.

-- T.J. Crowder

# Naveen Chawla (7 years ago)

I've yet to see any demonstration code use case for a fromEntries where it would ever be preferable over a fromIterable, seeing as most back end API JSON data arrays, and most data arrays in general for that matter, do not come in the form of "entries". I would suggest that a fromIterable with a valueFromElement callback, rather than being a "Swiss Army knife", addresses the fundamental building block of an object, namely keys and values, covering e.g. the "recentCountries" use case example code a few posts back with minimal effort.

# Alexander Jones (7 years ago)

As far as I'm concerned, "most data arrays" — at least the ones you're speaking about — should actually be Maps. And their iterators (from, and to) yield key-value pairs. There is no syntax for a strict pair, so 2-element arrays have to do.

I'd still rather see some approach to generator expressions and/or iterator functions (like map, below) finally enter the standard somehow.

const allThePeople = [{name: "Joe", age: 24}, {name: "Barbara", age: 43},
...];

// Python generator expression
const myIndexByName = new Map([_.name, _] for _ of allThePeople);

// map function to transform iterables you can argue about the argument
order ;)
const myIndexByName = new Map(map(allThePeople, _ => [_.name, _]));

// equivalent to, but without the intermediate Array
const myIndexByName = new Map(allThePeople.map(_ => [_.name, _]));

Alex

# Naveen Chawla (7 years ago)

Map.fromIterable(allThePeople, person=>person.name) is less verbose for

your use case

On Fri, 11 Aug 2017 at 03:54 Alexander Jones <alex at weej.com> wrote:

As far as I'm concerned, "most data arrays" — at least the ones you're speaking about — should actually be Maps.

Why? What's the advantage?

# Naveen Chawla (7 years ago)

You lose at least the square bracket and dot notations for access, as disadvantages, and I'm not aware of any advantages. If there aren't any that compensate for the disadvantages, then it's a net negative

# T.J. Crowder (7 years ago)

On Thu, Aug 10, 2017 at 11:24 PM, Alexander Jones <alex at weej.com> wrote:

As far as I'm concerned, "most data arrays" — at least the ones you're speaking about — should actually be Maps.

I see the point you're trying to make, but

  1. I routinely want paging and other access by arbitrary index

  2. I routinely want to change the order, which is expensive with Maps

  3. I'm usually receiving these by deserializing JSON

Granted #3 could be solved by making the JSON even more verbose than it is ([["foo", {"id":"foo",..}], ["bar",{"id": "bar", ...], ...]) and then creating the Map from that temporary array of arrays but...no. :-) Not least because of the knock-on effects on the generating code. So I'd need a way to create the Map from the array even if I were using Maps from then on (such as my Map.from).

Arrays are a great random-access, sortable, tranferrable format; and highly-optimized on modern JavaScript engines. No need to change it. And if I did change it, I'd still need a way to create the Map in the first place from the obvious transfer format: An array.

-- T.J. Crowder

# Darien Valentine (7 years ago)

@Alexander The idea of a generalized map function (etc, I’m guessing) is appealing. From the way you talked about it, it sounds like there may have been past discussion on the topic. Are there any proposals for this or major ideas being batted around?

Why? What's the advantage? You lose at least the square bracket and dot notations for access, as disadvantages, and I'm not aware of any advantages. If there aren't any that compensate for the disadvantages, then it's a net negative

@Naveen — a handful of things. Objects are indeed a perfectly reasonable choice for modeling kvp collections a lot of the time. On the other hand, because objects in ES serve double duty as ways to model data and ways to "model code", they are not always ideal for the former case on account of the features that exist mainly to serve the second. Some examples of things that make maps useful: inherently iterable; no prototype chain access lookup; no collision with Object.prototype property name when hashing by arbitrary keys; potentially more efficient for situations where keys are frequently removed; map.has() is more straightforward than having to consider whether you want key in or Object.hasOwnProperty or which properties have been defined as enumerable or not; iteration order of entries is 1:1 with insertion order; and of course, keys can be of any type. Further, maps can be subclassed to constrain the types that they may hold or add other behaviors without needing to define custom Proxies.

Some general use cases: registries; hashing data where keys are from external input; kvp collections which are ordered; kvp collections which will be subject to later transformations; kvp collections to which new keys are frequently added or removed.

While I hope that information is somewhat helpful, there are probably much more detailed resources online (including, I suspect, past discussions on this list) which could explain some of those things better or which include cases I haven’t thought of.

# Naveen Chawla (7 years ago)

Of these, insertion ordering is the only one that may be compelling enough to me when I require that, to overcome the disadvantages. I never use the object prototype and I'm not convinced about the performance aspect. I reiterate though that Map.fromIterable(allThePeople, person=>person.name)

is less verbose for the stated use case

# Alexander Jones (7 years ago)

Map.fromIterable takes an iterable of values, and a key function. Would a Map.prototype.toIterable return only the values - that's already Map.prototype.values? It feels like there is a symmetry issue here. Perhaps this could be Map.fromValues?

Worth also remembering that compressing every possible use case down to an absolute minimum has a cost if the resultant language has too many features like this. I think new Map(...kvps) is a general solution that is actually good enough for "indexing" purposes, and means that people only have to internalise one method of construction via iterables.

That said, a helper function to allow constructing map-like objects from arbitrary iterables would maybe be a bit more composable?

// This works with Map, WeakMap, Immutable.Map, etc.
function* keyedBy(iterable, keyFn) {
  for (const element of iterable) {
    yield [keyFn(element), element];
  }
}

const allThePeople = [{name: "Joe", age: 24}, {name: "Barbara", age: 43},
...];

const index1 =
  new Map(keyedBy(allThePeople, _ => _.name));

// c.f.
const index2 =
  Map.fromValues(allThePeople, _ => _.name);
# Naveen Chawla (7 years ago)

My proposal was Map.fromIterable(iterable, keyFromElement[, valueFromElement[, existingMap]]) so it's not meant to be symmetrical with values anyway. It was as an equivalent of Object.fromIterable(iterable, keyFromElement[, valueFromElement[, existingObjectToUse]]) as a means to construct an object's keys and values from any iterable.

I also was just thinking that both can perfectly coexist with Array.prototype.toObject(keyFromElement[, valueFromElement]) which has the advantage of chain-ability after array transformation methods (like filter etc.). Array is such a fundamental construct that I would find myself using this one the most frequently

# Barret Furton (7 years ago)

Why not embrace Array.prototype.reduce instead of trying to abstract it away?

const identity = a => a

const toObject = (fk, fv = identity) =>
    (acc, curr) => (acc[fk(curr)] = fv(curr), acc)

const arr = [['a', '1'], ['b', '2'], ['c', '3']]

arr.map(a => [a[0], parseInt(a[1], 10)])
   .filter(a => a[0] !== 'c')
   .reduce(toObject(a => a[0]), {}) // { a: 1, b: 2 }

reduce(toObject) clearly communicates intent, and you can even provide an existing object for merging.

# Naveen Chawla (7 years ago)

Verbosity.

It's a task common across many project types and involves only 2 fundamental types in ES: array and object

Compare:

const cache = array.reduce((cache, element)=>{cache[element.id] = element;

return cache;}, {});

with

const cache = array.toObject(element=>element.id);

since the signature would offer additional optional valueFromElement and startingObject parameters nobody loses anything.

# Michał Wadas (7 years ago)

For the same reason why we have filter, forEach, map etc. Reduce is actually rather low level primitive (if we can call functional concept low level) and specialised methods should be preferred over it's usage if possible.

# Alexander Jones (7 years ago)

Honestly, promoting the use of Object for this, and coupling the solution to Array, feels like the wrong direction for the language to me personally. By definition, such a map constructed from a set of homogenous values, for indexing purposes, has a clear key and value type. This guidance from MDN seems to be the right message we should be sending to developers:

developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map

... ask yourself the following questions:

  • Are keys usually unknown until run time? Do you need to look them up

dynamically?

  • Do all values have the same type? Can they be used interchangeably?
  • Do you need keys that aren't strings?
  • Are key-value pairs frequently added or removed?
  • Do you have an arbitrary (easily changing) number of key-value pairs?
  • Is the collection iterated?

If you answered 'yes' to any of those questions, that is a sign that you

might want to use a Map. Contrariwise, if you have a fixed number of keys, operate on them individually, or distinguish between their usage, then you probably want to use an Object.

# Naveen Chawla (7 years ago)

Object self-promotes because it is nearly always a perfect fit for this: great performance, great literal syntax, concise access syntax. The only compelling case to use a map instead is when insertion ordering is required, despite the MDN article. I wouldn't oppose an equivalent Array.prototype.toMap and Map.fromIterable, I would just hardly ever use it

# Alexander Jones (7 years ago)

is nearly always a perfect fit for this

The fact that it is so close to being useful, but has silly surprises like 'toString' in everyObject, actually gives JS a bad reputation and contributes towards people being driven to other languages that have cleaner approaches to data types.

# Naveen Chawla (7 years ago)

For iteration, Object.keys() doesn't include any property in the prototype chain like "toString" etc. I've never been concerned about needing to allow "toString" etc. as valid cache keys, so it's never been an issue for me so far. However, if I did in future, I would honestly look to delete them from the Object.prototype in my application since I never use anything in there, as long as I knew it was safe to do so. If and only if not, then yes I might consider using a map for those cases. But just for consistency with my other caches I might just instead consider appending something to each object key in factored out access methods (or just at source data) for this cache so there can't be overlap

# T.J. Crowder (7 years ago)

On Mon, Aug 14, 2017 at 7:33 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:

I've never been concerned about needing to allow "toString" etc. as valid cache keys, so it's never been an issue for me so far. However, if I did in future, I would honestly look to delete them from the Object.prototype in my application since I never use anything in there, as long as I knew it was safe to do so.

I very much doubt it's safe; if you're using any library, I expect it would blow up fairly quickly.

It's also not remotely necessary. Just create an object with no prototype: Object.create(null).

-- T.J. Crowder

# Naveen Chawla (7 years ago)

OK, I have written up a proposal that I really hope satisfies every single requirement outlined in this discussion. If not, please recommend amendments. It is here: TheNavigateur/arrays-and-other-iterables-to-objects-and-maps . Do give comments here either way

# Michał Wadas (7 years ago)

As far as I remember Maps have the same performance as objects in case of dynamic property access for small collections and better for big ones (I have value of 255 in my mind, but I can't test it now). And objects do maintain insert order, at least in some cases.