Resizing a hash table

Create a hash table with insert(), retrieve(), and remove() methods.

  • Be sure to handle hashing collisions correctly.
  • Set your hash table up to double the storage limit as soon as the total number of items stored is greater than 3/4th of the number of slots in the storage array.
  • Resize by half whenever utilization drops below 1/4.

    var makeHashTable = function(){
      var result = {};
      var storage = [];
      var storageLimit = 4;
      var size = 0;
      result.insert = function(key, value){
        // TODO: implement `insert`
        size++;
        var index = getIndexBelowMaxForKey(key, storageLimit);
        var bucket = storage[index];
        if (bucket) {
          // check if value is already in the bucket and update it
          for (var i = 0; i < bucket.length; i++) {
            if (bucket[i][0] === key) {
              bucket[i][1] = value;
            }
          }
          // the value is not in the bucket so just insert it
          bucket.push([key, value]);
        } else {
          // no bucket at the storage location
          storage[index] = [];
          storage[index].push([key, value]);
        }
        if (size / storageLimit >= 0.75) {
          result.resize(storageLimit * 2);
        }
      };
    
      result.retrieve = function(key){
        // TODO: implement `retrieve`
        var index = getIndexBelowMaxForKey(key, storageLimit);
        var bucket = storage[index];
        if (bucket) {
          for (var i = 0; i < bucket.length; i++) {
            // Does the value match
            if (bucket[i][0] === key) {
              // We have found the value in the bucket
              return bucket[i][1];
            }
          }
        }
    
        return undefined;
      };
    
      result.remove = function(key){
        // TODO: implement `remove`
        var index = getIndexBelowMaxForKey(key, storageLimit);
        var bucket = storage[index];
        if (bucket) {
          for (var i = 0; i < bucket.length; i++) {
            if (bucket[i][0] === key) {
              // Found the value so decrease size by 1
              size--;
              bucket.splice(i, 1);
            }
          }
        }
        if (size / storageLimit <= 0.25) {
          result.resize(storageLimit / 2);
        }
      };
    
      result.resize = function (newLimit) {
        // Keep a reference to the storage to reset it
        var oldStorage = storage;
        // Update the size limit of the storage
        storageLimit = newLimit;
        // Clear the storage
        storage = [];
    
        // Go thru each bucket in the storage
        for (var i = 0; i < oldStorage.length; i++) {
          var bucket = oldStorage[i];
          if (bucket) {
            // Reassign for each bucket
            for (var j = 0; j < bucket.length; j++) {
              var index = getIndexBelowMaxForKey(bucket[j][0], storageLimit);
              var newBucket = storage[index];
              if (newBucket) {
                newBucket.push([bucket[j][0], bucket[j][1]]);
              } else {
                newBucket = [];
                newBucket.push([bucket[j][0], bucket[j][1]]);
              }
            }
          }
        }
      };
    
      return result;
    };
    
    // This is a "hashing function". You don't need to worry about it, just use it
    // to turn any string into an integer that is well-distributed between
    // 0 and max - 1
    var getIndexBelowMaxForKey = function(str, max){
      var hash = 0;
      for (var i = 0; i < str.length; i++) {
        hash = (hash<<5) + hash + str.charCodeAt(i);
        hash = hash & hash; // Convert to 32bit integer
        hash = Math.abs(hash);
      }
      return hash % max;
    };
    
Written on March 2, 2015