为javascript对象键分配索引号

时间:2016-08-19 11:43:01

标签: javascript

目标:最终目标是拥有一种访问有序列表中对象数据的方法/技术,以及调用有序列表中的排序或映射等方法。如果我可以为每个键值分配索引号,那么这应该是可能的。如果我们可以为对象的每个键值分配一个索引,那么其余的应该通过创建map版本并将它们排序或继承Array对象来实现。

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)

问题:有没有办法为javascript对象键分配索引编号?

思考:我知道javascript对象是哈希表,因此它们有一个分配给它们的密钥,但它们是完全随机的,并且可能会随着更改哈希表的大小而改变。所以那不行。

我们可以遍历对象并拥有一个单独的数组,该数组只包含键值,然后通过该键值得到值。

我真的不确定这里有什么好的解决方案我只是抛弃我的想法,想法?

4 个答案:

答案 0 :(得分:0)

嗯,一种方式就是这样:

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

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

答案 1 :(得分:0)

JavaScript不支持指针,所以要做到这一点,我担心你必须复制对象的内容。

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

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

这会使一个具有重复内容的对象,因为执行上述代码后的属性将是:

  • bar
  • foobar
  • 0
  • 1

你可以做的是使用for ... in statement迭代你的对象,允许你使用整数作为数组索引来遍历它,以迭代你的对象。

来自MDN's example

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

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

所以你可以这样做:

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

请注意,这不会在特定属性与其数组索引之间提供强绑定。为了实现这种一致性,您可以为同时更新两者的变量创建一个setter属性。

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

我不确定这是否会在性能方面表现良好,因为您的对象仍然会被复制,但是现在我可以想到解决这个问题。

答案 2 :(得分:0)

JavaScript对象不是哈希表,而现代引擎哈希表仅在其实现中使用,如果您从中删除属性。否则,它们是高度优化的运行时生成的类的实例。其中没有一个对手头的问题非常重要。 : - )

假设您希望0属性与其中一个命名属性之间存在持续链接,那么在ES5及更早版本中无法执行此操作。相反,您需要getter / setter方法来进行索引访问:

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;
    }
});

...其中orderingFunction是定义键顺序概念的函数。 (在ES5及以下,属性没有订单。虽然在ES2015及以上属性确实有订单,但它是A)没用,B)不保证是Object.keys返回的订单。)

用法:

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

或者如果您愿意,可以为getter和setter使用单独的函数。

请注意,这是相当昂贵的,所有这些都是排序。

在ES2015及以上版本中的一种方法:一个Proxy对象,可以在这里做两件事:

  1. 实现可用于数字访问的默认属性访问功能

  2. 允许您维护密钥数组,而无需不断对其进行重新排序

  3. &#13;
    &#13;
    // 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));
    }
    &#13;
    &#13;
    &#13;

    代理也不便宜,但希望能比你不断生成和排序的数组表现得更好。

    显然上面的只是一个例子,而不是高度优化的通用工具。它没有使用数组索引的正确定义(隐藏在this answer中的细节),它使用更简单的&#34;它是全部数字?&#34;定义。它只处理对象拥有可枚举属性;如果您想处理继承的属性或标记为不可枚举的属性,那么可能只需要调整上面的内容。

答案 3 :(得分:0)

也许这有帮助。

(我没有看到其他答案,也没有阅读评论;当我开始编写此代码时,这篇文章很空白)

<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>