Accessing (n)th key from an object

# somonek (7 years ago)

Assuming that

const myObject = { one: 123, two: 456, };

could myObject[in 0] === 'one' // true

be a better alternative of Object.keys(myObject)[0]

and myObject[in 3] === undefined

without the need to convert all the object keys into an array, but directly accessing one.

Best, Serghei

# Mike Samuel (7 years ago)

On Tue, Apr 24, 2018 at 9:54 AM, somonek <somonek at gmail.com> wrote:

Hi all,

Assuming that

const myObject = { one: 123, two: 456, };

could myObject[in 0] === 'one' // true

be a better alternative of Object.keys(myObject)[0]

and myObject[in 3] === undefined

without the need to convert all the object keys into an array, but directly accessing one.

Why is this something that warrants extra syntax and not an optimization better left to sufficiently smart engines?

# T.J. Crowder (7 years ago)

On Tue, Apr 24, 2018 at 2:54 PM, somonek <somonek at gmail.com> wrote:

...

could myObject[in 0] === 'one' // true

What's the use case? Relying on the order of the properties in the object is almost always a bad idea (although if I read the current spec correctly, Object.keys is no longer exempt from order as it once was). The only time I've seen this done that seemed reasonable was when the object was known to have a single own enumerable property but that property's name was unknown (which was, in itself, an X/Y problem -- the real problem was why that name was unknown/varied at runtime).

-- T.J. Crowder

# somonek (7 years ago)

The use case is: (long story short)

I have an array of items coming from an api. They're converted to an object having the ids as keys for faster access of single items and loaded to a global store. Then in React.js and I want to render only the first 2 items with a "See more" that would render the rest of them. It would be handy (and probably more performant when the object is big as in my case) to have:

const product1 = myItems[in 0]; const product2 = myItems[in 1];

instead of converting everything to an array first and then accessing the first items by index.

# David Teller (7 years ago)

Doing this sounds pretty fragile. Do you have any guarantee that the order of fields is the same as the order of the array? Even if that's the case in your scenario, it's pretty uncommon.

# T.J. Crowder (7 years ago)

In that use case, I'd just remember those two objects from when you converted the array to an object, e.g.:

const obj = Object.create(null);
for (const item of theArray) {
    obj[item.key] = item;
}
const firstItems = theArray.slice(0, 2);

...then use firstItems for showing the first two. Also has the advantage that you can easily change how many you show before the "See more."

-- T.J. Crowder

# T.J. Crowder (7 years ago)

On Tue, Apr 24, 2018 at 4:06 PM, David Teller <dteller at mozilla.com> wrote:

Doing this sounds pretty fragile. Do you have any guarantee that the order of fields is the same as the order of the array? Even if that's the case in your scenario, it's pretty uncommon.

If we make a couple of assumptions:

  1. He's adding the properties to a newly-created object in array order
  2. None of the keys is an integer index

...then yes, the order is guaranteed (even for Object.keys, nowadays -- that wasn't true in ES2015, not sure when it changed and the ECMA site is down so I can't check the 2016 or 2017 snapshots [note to self: keep local copies!]).

Agreed it's not the best way to go about it, though. :-)

-- T.J. Crowder

# Michael Luder-Rosefield (7 years ago)

I've found myself using that pattern, actually. Say you have multiple variables and want to pass one of them on to a function, which wants to know the value (of course) of that variable but also some kind of name to know which one was passed on.

The easiest way to do this in one pass at the stage you pass it on is for the variable name to match the one needed later, and send it to the function as the object { whateverName }.

It does require, alas, more fiddling about to get the key/value pair from the object after.

# T.J. Crowder (7 years ago)

On Tue, Apr 24, 2018 at 4:24 PM, Michael Luder-Rosefield <rosyatrandom at gmail.com> wrote:

I've found myself using that pattern, actually. Say you have multiple variables and want to pass one of them on to a function, which wants to know the value (of course) of that variable but also some kind of name to know which one was passed on.

The easiest way to do this in one pass at the stage you pass it on is for the variable name to match the one needed later, and send it to the function as the object { whateverName }.

On those rare occasions, the way I'd use would be ["whateverName", whateverName] (granted it's more verbose at the call site). That's what Object.entries and Map.prototype.entries do, for instance, and it's friendly to destructuring:

function example([name, value]) {
    console.log(`${name} = ${value}`);
}
example(["answer", 42]);

-- T.J. Crowder

# Augusto Moura (7 years ago)

Assuming Object.keys has the same order as a simple for..in (as stated in mdn)[ developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys], we can already implement a function to build a iterator from object keys, and then we can destruct or use others helpers to minimize the array creation. Something like:

function* keysIterator(obj) {
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      yield key;
    }
  }
}
const obj = { foo: 12, bar: 89 };

// Using destructuring
const [firstProp, secondProp, ...othersKeys] = keysIterator(obj);

// Using some helper functions
const props = take(2, 10)(obj);

The discussion IMHO is if we need that as a built in helper (maybe Object.keysIterator()), and if we need to extend it to Object.values and Object.entries.

# T.J. Crowder (7 years ago)

On Tue, Apr 24, 2018 at 5:58 PM, Augusto Moura <augusto.borgesm at gmail.com>

wrote:

Assuming Object.keys has the same order as a simple for..in (as stated in mdn)[

developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys]. ..

We'll have to fix that page, the spec does not define the order in which for-in will enumerate property names. (It does define the order in which Object.keys will provide the property names, now, but not for-in.) That said, all modern and modern-ish implementations I've tested (to be fair, just V8, SpiderMonkey, Chakra, and IE11's JScript) do indeed follow the defined order with for-in as well, with an object's own properties coming before inherited ones - jsfiddle.net/arhbn3k2/1 (IE11-friendly version without Symbols: jsfiddle.net/arhbn3k2/5). (Provided I haven't screwed up that test.) But it's specifically not specified - from EnumerateObjectProperties (the op for-in uses to initialize its loop):

The mechanics and order of enumerating the properties **is not

specified** but must conform to the rules specified below.

(my emphasis) It says it has to use [[OwnPropertyKeys]] to get the own property keys at each level, but doesn't -- as far as I can tell -- define the sequence of own vs. inherited properties.

Object.entries follows the order (own properties only):

const [first, second, ...rest] = Object.entries(obj);

...but I don't think that, or keysIterator, really helps somonek vs. what he's doing now (Object.keys).

-- T.J. Crowder

# Jordan Harband (7 years ago)

Regardless of what's in the spec, relying on objects having an order among their properties violates the conceptual mental model of objects: a bag of unordered key/value pairs.

If you want to convert an array - the best way to preserve order - into an object for "performance" reasons, then you may also want to preserve an array of IDs so that ordering can be relied upon.

# T.J. Crowder (7 years ago)

On Tue, Apr 24, 2018 at 7:07 PM, Jordan Harband <ljharb at gmail.com> wrote:

Regardless of what's in the spec, relying on objects having an order among their properties violates the conceptual mental model of objects: a bag of unordered key/value pairs.

If you want to convert an array - the best way to preserve order - into an object for "performance" reasons, then you may also want to preserve an array of IDs so that ordering can be relied upon.

Absolutely agree. As I said originally, relying on the order of the properties in the object is almost always a bad idea.

@somonek, an object with an array of keys is probably your best solution. Wrap it up in an object with an API that all of the code updating it can use, so you don't have consistency issues. You could even give it an iterator. :-)

-- T.J. Crowder

# somonek (7 years ago)

Thanks, hat's probably what I'll do.

I was also thinking looking at @Augusto's approach that if you replace the object with a Map you can iterate as Augusto mentioned const [firstProp, secondProp, ...othersKeys] = myMap; and the order of items should not be a worry.

Any comments on Map's performance in such a case?

# Cyril Auburtin (7 years ago)

just try it and see

repl.it/@caub/obj-vs-map-perf

2018-04-24 20:29 GMT+02:00 somonek <somonek at gmail.com>: