最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Assign an index number to a javascript object key - Stack Overflow

programmeradmin1浏览0评论

Goal: The end goal is to have a method/technique of accessing the data of an object in a ordered list as well as calling methods like sort or map on the ordered list. If I can assign an index number to each key value then this should be possible. If we can assign an index to each key value of the object then the rest should fall into place either by creating a version of map and sort or inheriting them the Array object.

var foo = { 
  bar : 'value',
  foobar : 'value2'
};

//the magic here

foo.bar // 'value'
foo[0] // 'value' (This is the ideal solution it does not have to be this way)

Question: Is there a way to assign an index number to a javascript object key?

Thoughts: I understand that javascript objects are hash tables so they have a key assigned to them but they are pletely random and might change as you change the size of the hash table. So that wont work.

We can loop over the object and have a separate array that holds just the key value and then get the value by that key value number.

I'm really not sure of a good solution here I'm just throwing out my ideas, thoughts?

Goal: The end goal is to have a method/technique of accessing the data of an object in a ordered list as well as calling methods like sort or map on the ordered list. If I can assign an index number to each key value then this should be possible. If we can assign an index to each key value of the object then the rest should fall into place either by creating a version of map and sort or inheriting them the Array object.

var foo = { 
  bar : 'value',
  foobar : 'value2'
};

//the magic here

foo.bar // 'value'
foo[0] // 'value' (This is the ideal solution it does not have to be this way)

Question: Is there a way to assign an index number to a javascript object key?

Thoughts: I understand that javascript objects are hash tables so they have a key assigned to them but they are pletely random and might change as you change the size of the hash table. So that wont work.

We can loop over the object and have a separate array that holds just the key value and then get the value by that key value number.

I'm really not sure of a good solution here I'm just throwing out my ideas, thoughts?

Share Improve this question edited Aug 19, 2016 at 11:50 Michael Warner asked Aug 19, 2016 at 11:43 Michael WarnerMichael Warner 4,2773 gold badges24 silver badges48 bronze badges 5
  • What should happen when new properties are added to the object? – T.J. Crowder Commented Aug 19, 2016 at 11:45
  • 1 I'm assuming here that foo.bar = "new value"; followed by foo[0] should give "new value", not "value"? – T.J. Crowder Commented Aug 19, 2016 at 11:46
  • This sounds like an interesting problem; I'm just not sure how you want to push a new item, and how/why/when you need a result – Emmanuel Delay Commented Aug 19, 2016 at 11:51
  • I'm trying to bine the attributes of an array and an object so you have instant lookup time on data while still having the method arrays give you like order and the possibility of having or creating functions like map reduce and so on. – Michael Warner Commented Aug 19, 2016 at 11:55
  • 1 Sorry @T.J.Crowder I was answerig Emmanuel Yes T.J. Crowder if you change the value it should reflect that change. As you seem to have done in the answer. – Michael Warner Commented Aug 19, 2016 at 12:28
Add a ment  | 

4 Answers 4

Reset to default 1

JavaScript objects aren't hash tables, and on a modern engine hash tables are only used in their implementation if you delete properties from them. Otherwise, they're instances of highly-optimized runtime-generated classes. None of which really matters to the problem at hand. :-)

Assuming you want an ongoing link between the 0 property and one of the named properties, there's just no way to do this in ES5 and earlier. Instead, you'll need getter/setter methods for the indexed access:

Object.defineProperty(foo, "item", {
    value: function(index, value) {
        var key = Object.keys(this).sort(orderingFunction)[index];
        if (arguments.length < 2) {
            // Getter
            return this[key];
        }
        // Setter
        this[key] = value;
        return this;
    }
});

...where orderingFunction is the function that defines your concept of the order of the keys. (In ES5 and below, properties had no order. While in ES2015 and above properties do have an order, it's A) Not useful, and B) Not guaranteed to be the order returned by Object.keys.)

Usage:

foo.item(0); // "value"
foo.item(0, "new value");

Or if you prefer, use separate functions for the getter and setter.

Note that this is fairly expensive, what with all that sorting.


There is a way to do it in ES2015 and above: a Proxy object, which can do two key things here:

  1. Implement a default property access function that you can use for numeric access

  2. Allow you to maintain your keys array without having to constantly re-sort it

// REQUIRES ES2015+ SUPPORT IN YOUR BROWSER
let foo = {
  question: "Life, the Universe, and Everything!",
  answer: 42
};
let rexNumber = /^\d+$/;
let fooProxy = indexedProxy(foo);
console.log(fooProxy[0]); // 42
fooProxy.answer = 67;
console.log(fooProxy[0]); // 67
fooProxy.bar = "foo";
console.log(fooProxy[1]); // "foo"

function indexedProxy(obj) {
  // An array of the current keys
  let keys = getKeys(foo);

  // Return the proxy
  return new Proxy(obj, {
    // Called when any property is gotten from the target
    get(target, property) {
      if (rexNumber.test(property)) {
        // Indexed access
        let key = keys[property];
        return key === undefined ? undefined : target[key];
      }
      // Normal access
      return target[property];
    },
    // Called when any property is set on the target
    set(target, property, value) {
      if (rexNumber.test(property)) {
        // Indexed access
        let key = keys[property];
        if (key === undefined) {
          throw new Error(`No property for index #{property}`);
        }
        target[key] = value;
      } else {
        // Normal access, do we know the key?
        let newProp = !keys.includes(property);
        target[property] = value;
        if (newProp) {
          // Rebuild our array
          keys = getKeys(target);
        }
      }
    }
  });
}

function getKeys(obj) {
  // Let's go for alphabetical
  return Object.keys(obj).sort((a, b) => a.localeCompare(b));
}

Proxies are also not cheap, but hopefully would perform better than an array you constantly have to generate and sort.

Obviously the above is just an example, not a highly-optimized general-purpose tool. It doesn't use the correct definition of an array index (details buried in this answer), it uses a simpler "is it all digits?" definition. It only handles an objects own, enumerable properties; if you want to handle inherited properties or ones marked non-enumerable, that's possible, it just needs tweaking thea above.

Well, one way will be like that:

var foo = { 
  bar : 'value',
  foobar : 'value2'
}

var i = 0;
for (el in foo) {
  foo[i] = foo[el];
  i++;
}

JavaScript does not support pointers, so to do this I am afraid you would have to duplicate the contents of your object.

var foo = { 
  bar : 'value'
  foobar : 'value2'
};

foo[0] = foo.bar;
foo[1] = foo.foobar;

This would yeld an object with duplicate content, as its properties after the code above is executed would be:

  • bar
  • foobar
  • 0
  • 1

What you can do is iterate over your objects using the for ... in statement, what allows you to loop through it using integer numbers as array indexes to iterate on your object.

From MDN's example:

var obj = {a:1, b:2, c:3};

for (var prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}

So you could do something like:

var i = 0;
for (var prop in foo) {
  foo[i] = foo[prop];
    i++;
}

Note that this will provide no strong binding between a specific property and its array index. To achieve such consistency, you could create a setter property to a variable that updates both at the same time.

function setProperty(propertyName, index, value) {
    foo.propertyName = value;
    foo[index] = value;
}

I'm not sure if this would be good in terms of performance as your object would still be duplicated, but for now it is what I could think of to tackle this issue.

Maybe this helps.

(I haven't seen the other answers, nor read the ments; when I started writing this code this post was pretty empty)

<input type="button" value="GO" onclick="doStuff()">
<div id="log"></div>

<script>

function doStuff() {
var foo = {};

  // INSERT items
  log('Inserting items');
  var index1 = pushItem(foo, 'bar', 'value');
  var index2 = pushItem(foo, 'foobar', 'value2');

  log('getting items');
  // get items
  var item2 = getItem(foo, 1);

  // map
  log('map');
  mapItems(foo, function(value, index, key) {
    log('map. value: ' + value + ' - index: ' + index + ' - key: ' + key);
  });


  // display a log on screen.  Feel free to use console.log instead
  function log(message) {
    document.getElementById('log').innerHTML += message + '<br/>';
  }
}

////////////////////////////////////////////////////////////////

// global variable, contains the keys in an array, example ['bar', 'foobar']
var key_indexes = [];

// pushes an item to the object, returns the index
function pushItem(object, key, value) {
 key_indexes.push(key);
 object[key] = value;
 return (key_indexes.length - 1);   
}

// get an item
function getItem(object, index) {
  var key = key_indexes[index];
  return object[key];   
}

// map
function mapItems(object, callback) {
  for(var index in key_indexes) {
    var key = key_indexes[index];
    callback(object[key], index, key);
  }
}
</script>
发布评论

评论列表(0)

  1. 暂无评论