Ward D.S.
Ward D.S.

Reputation: 558

how to access objects in an array by a property of those objects?

So recently I had to check if a user had a certain plugin installed, which is apparently quite easily done:

if(typeof navigator.plugins['plugin name'] === 'undefined'){
  alert('Please install plugin')
}

This isn't surprising to me, I thought it just accessed the object assigned to plugin name of the plugins object. (like plugins['plugin name'] = {...}) The strange thing is, if you console.log the navigator.plugins variable, it comes out as such:

>PluginArray
 >0: Plugin
 >1: Plugin
 >2: Plugin

So, these Plugin objects are accessible by using both navigotor.plugins['Plugin name'] and navigator.plugins[i]!

My question is, how can I achieve something similar, so I can access objects of an array both by index (easy iteration and preserving array functionalities) and by a property of said objects (easy accessing by id or name)?

The navigator.plugins object has the PluginArray prototype, so I suppose this class it somehow overrides the obj['index'] accessor and looks for the object(s?) that have the given index as their 'name' property, either by iterating when accessing, maintaining an internal array that maps the numeric indexes to the names ([i] = 'index')or an internal object that maps names to their index (.index = i).

So any thoughts on the best approach?

EDIT: as pointed out by Oriol, you can assign the same object to both keys (arr[0] = arr[obj.name] = obj) and this can work quite well if obj.name is unique, if it is not you risk overriding the key that was set earlier. Definitely a cheap and easy solution if you want to use something like a unique database index as second key.

Upvotes: 1

Views: 327

Answers (4)

Alex K.
Alex K.

Reputation: 175748

This is the same behaviour that DOM collections implement; internally if the passed key is an int forward it to .item() if its a string forward it to .namedItem().

The closest you can get currently to this is an ES6 Proxy() with very limited support;

"use strict";
let Plugins = new Proxy(
    [{name: 'pdf', mime: 'pfft/pdf'}, {name: 'swf', type: 'flash'}],
    {
        get: function(obj, prop) 
        {
            if (!isNaN(prop)) // by index
                return obj[prop]

            for (var k in obj) // lookup a name
                if (obj[k].name == prop)
                    return obj[k];
        }
});

console.log(Plugins);        // Array [ Object, Object ]
console.log(Plugins[0]);     // Object { name: "pdf", type: "doc" }
console.log(Plugins['pdf']); // Object { name: "pdf", type: "doc" }

Upvotes: 1

Barmar
Barmar

Reputation: 780688

navigator.plugins has a prototype PluginArray, which links to native code that translates plugin names to entries in the array.

Use console.log(navigator.plugins.__proto__) to see this.

Upvotes: 0

Oriol
Oriol

Reputation: 287960

You can assign the same value to multiple properties:

var obj = {};
obj[0] = obj.name0 = value0;
obj[1] = obj.name1 = value1;
obj[2] = obj.name2 = value2;

Upvotes: 2

Rycochet
Rycochet

Reputation: 2938

An Array is at it's most basic an Object extended with a magic length key - you can add any key values to it you'd like. Iterating through it becomes slightly more complicated if you want to see the keys, and setting anything that can equate to a number (ie arr["1"]) will equate to setting a number index of the array.

Upvotes: 0

Related Questions