代码之家  ›  专栏  ›  技术社区  ›  w0f

JavaScript:对象键的快速随机索引

  •  0
  • w0f  · 技术社区  · 6 年前

    我试图随机选择一个随机属性从一个对象,我用字典作为快速索引。

    请考虑以下代码:

    //Create test dictionary
    var dict = {};
    for(var i = 0; i < 10000; i++) {
      var num = Math.random() * 1000000 << 0;
      dict[num] = { Id: num };
    }
    
    //Fuzz
    const NUM_RUNS = 1000;
    var start = new Date();
    for(var i = 0; i < NUM_RUNS; i++)
      getRandom(dict);
    var end = new Date();
    
    var runTime = (end.getTime() - start.getTime()) / 1000;
    var timePerCall = (runTime / NUM_RUNS) * Math.pow(10,9);
    console.log('Total Calls: ' + NUM_RUNS);
    console.log('Total Runtime: ' + runTime + ' seconds');
    console.log('Time Per Call: ' + timePerCall + ' nanoseconds');
    
    
    
    function getRandom(dict) {
      var keys = Object.keys(dict);
      var index = keys[Math.random() * keys.length << 0];
      return dict[index];
    }

    如你所见,使用 Object.keys() 非常 昂贵的,尤其是随着字典的增长。

    我正在寻找一种最佳的方法来实现字典中元素的随机选择。当然,字典中有10000多个元素是一个极端的边缘情况,但是我还是希望能够处理它。

    1 回复  |  直到 6 年前
        1
  •  1
  •   AuxTaco    6 年前

    如果缓存 Object.keys 数组是不可行的,你可以用 Proxy :

    function generateKeyTracker (keysPropertyName) {
      const set = new Set();
      function defineProperty (target, property, descriptor) {
        target[property] = descriptor.value;
    
        if (set.has(property)) return true;
    
        set.add(property);
        target[keysPropertyName].push(property);
        return true;
      }
      function deleteProperty (target, property) {
        delete target[property];
        
        if (!set.delete(property)) return true;
    
        target[keysPropertyName] = target[keysPropertyName].filter(key => key !== property);
        return true;
      }
      return {defineProperty, deleteProperty};
    }
    
    //Create test dictionary
    var dict = new Proxy(
      Object.defineProperty({}, '__keys', {
        configurable: true,
        enumerable: false,
        writable: true,
        value: []
      }),
      generateKeyTracker('__keys')
    );
    
    for(var i = 0; i < 1e4; i++) {
      var num = Math.random() * 1e6 << 0;
      dict[num] = { Id: num };
    }
    
    //Fuzz
    const NUM_RUNS = 1e6;
    var start = performance.now();
    for(var i = 0; i < NUM_RUNS; i++)
      getRandom(dict);
    var end = performance.now();
    
    var runTime = end - start;
    var timePerCall = (runTime / NUM_RUNS);
    console.log(`Total Calls: ${NUM_RUNS}`);
    console.log(`Total Runtime: ${runTime} ms`);
    console.log(`Time Per Call: ${timePerCall * 1e6} ns`);
    
    function getRandom(dict) {
      var index = Math.random() * dict.__keys.length << 0;
      return dict[dict.__keys[index]];
    }

    它使用陷阱 property creation deletion 跟踪 dict 的属性键,存储在 non-enumerable 财产 dict.__keys .