Accessing (n)th key from an object
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?
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
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.
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.
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
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:
- He's adding the properties to a newly-created object in array order
- 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
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.
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
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
.
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 simplefor..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
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.
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
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?
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