Sign up ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I found Python's default dict and ordered dict incredibly useful, hence I attempted an implementation in JS. As JavaScript objects only supports string as key, it is not as powerful as the Python version. Can anyone suggest a way of using an object as a key in JS, like some kind of hash function? Also, is there anything wrong with this implementation?

(function () {
  'use strict';

  function forEach(collection, func, thisArg) {
    var i;
    if (Array.isArray(collection)) {
      if (thisArg) {
        for (i = 0; i < collection.length; ++i) {
          if (func.call(thisArg, collection[i], i) === false) {
            break;
          }
        }
      } else {
        for (i = 0; i < collection.length; ++i) {
          if (func.call(collection[i], collection[i], i) === false) {
            break;
          }
        }
      }
    } else {
      if (thisArg) {
        for (i in collection) {
          if (func.call(thisArg, collection[i], i) === false) {
            break;
          }
        }
      } else {
        for (i in collection) {
          if (func.call(collection[i], collection[i], i) === false) {
            break;
          }
        }
      }
    }
    return collection;
  }

  function deepCopy(obj) {
    if (typeof obj != 'object') {
      return obj;
    }
    var newObj = Array.isArray(obj) ? new Array(obj.length) : {};
    forEach(obj, function (v, k) {
      newObj[k] = v;
    });
    return newObj;
  }

  function assert(expression) {
    if (!expression) {
      throw 'Assertion faild';
    }
  }

  function implicitEqual(val1, val2) {
    return val1 == val2;
  }

  function explicitEqual(val1, val2) {
    return val1 === val2;
  }

  function deleteFromArray(arr, value, flag) {
    var ret = null;
    var method = flag == 'i' ? implicitEqual : explicitEqual;
    forEach(arr, function (val, index) {
      if (method(val, value)) {
        arr.splice(index, 1);
        ret = val;
        return false;
      }
    })
    return ret;
  }

  function createChildClass(parent, constructor) {
    constructor.prototype = deepCopy(parent.prototype);
    constructor._parent = parent;
    return constructor;
  }

  var DictionaryCollection = (function () {

    function DictionaryCollection() {
      this.dictionary = {};
    }

    DictionaryCollection.prototype.set = function (key, value) {
      this.dictionary[key] = value;
      return this;
    };

    DictionaryCollection.prototype.applyFunction = function (key, func) {
      this.set(key, func(this.get(key)));
      return this;
    };

    DictionaryCollection.prototype.get = function (key) {
      return this.dictionary[key];
    };

    DictionaryCollection.prototype.delete = function (key) {
      this.dictionary[key] = null;
      delete this.dictionary[key];
    };

    return DictionaryCollection;
  })();

  var DefaultDict = (function () {
    var DefaultDict = createChildClass(DictionaryCollection, function DefaultDict(defaultFunction) {
      if (typeof defaultFunction != 'function') {
        throw 'Default function must be a function';
      }
      DictionaryCollection.call(this);
      this.defaultFunction = defaultFunction;
    });

    DefaultDict.prototype.get = function (key) {
      return this.dictionary.hasOwnProperty(key) ? this.dictionary[key] : (this.dictionary[key] = this.defaultFunction());
    };
    return DefaultDict;

  })();

  var OrderedDict = (function () {
    var OrderedDict = createChildClass(DictionaryCollection, function OrderedDict() {
      DictionaryCollection.call(this);
      this.orderArray = [];
    });

    // @override
    OrderedDict.prototype.set = function (key, value) {
      if (!this.dictionary.hasOwnProperty(key)) {
        this.orderArray.push(key);
      }
      DictionaryCollection.prototype.set.apply(this, arguments);
      return this;
    };

    // @override
    OrderedDict.prototype.delete = function (key) {
      DictionaryCollection.prototype.delete.apply(this, arguments);
      deleteFromArray(this.orderArray, key);
    };

    OrderedDict.prototype.getNth = function (index) {
      return this.dictionary[this.orderArray[index]];
    };

    OrderedDict.prototype.toArray = function () {
      var result = [];
      forEach(this.orderArray, function (key) {
        result.push([key, this.get(key)]);
      }, this);
      return result;
    };

    return OrderedDict;
  })();



  function testDC() {
    var dict = new DictionaryCollection();
    dict.set('a', 1);
    dict.set('a', 2);
    dict.set('b', 3);
    assert(dict.get('a') === 2);
    assert(dict.get('b') === 3);
    dict.applyFunction('b', function (val) {
      return ++val;
    });
    assert(dict.get('b') === 4);
    dict.delete('b');
    assert(dict.get('b') === undefined);
  }

  function testDD() {
    var dict = new DefaultDict(function () {
      return 70;
    });
    dict.set('a', 50);
    assert(dict.get('a') === 50);
    assert(dict.get('b') === 70);
  }

  function testOD() {
    var dict = new OrderedDict();
    dict.set('a', 50);
    dict.set('b', -1);
    dict.set('c', -3);

    assert(dict.getNth(0) === 50);
    assert(dict.getNth(1) === -1);
    assert(dict.getNth(2) === -3);

    var arr = dict.toArray();

    assert(arr[0][0] == 'a');
    assert(arr[0][1] === 50);

    assert(arr[1][0] == 'b');
    assert(arr[1][1] === -1);

    assert(arr[2][0] == 'c');
    assert(arr[2][1] === -3);

    dict.delete('b');
    assert(dict.get('b') === undefined);

    arr = dict.toArray();
    assert(arr[1][0] != 'b');
    assert(arr[1][1] !== -1);
    assert(arr[1][0] == 'c');
    assert(arr[1][1] === -3);
  }

  testDC();
  testDD();
  testOD();


  /*
   * example, using DefaultDict to count duplicates in an array
   */

  function int(value) {
    return value ? parseInt(value) : 0;
  }

  function countDuplicatesArray(arr) {
    var dict = new DefaultDict(int);
    forEach(arr, function (val) {
      dict.applyFunction(val, function (x) {
        return ++x;
      });
    });
    return dict.dictionary;
  }

  console.log(
    countDuplicatesArray([0, 0, 0, 0, 5, 0, 7, 8, 7, 5, 78, 8, 7, 8, 78, 0, 0, 0, 1, 1, 5, 7])
  );

})();
share|improve this question

1 Answer 1

In pycollections.js, I implemented non-string keys by using one object for each type of value (including some fuzzier ones like NaN).

Check out pycollections.js:

var collections = require('pycollections');
var dd = new collections.DefaultDict([].constructor);
console.log(dd.get('123'));  // []
dd.get(123).push('yay!');
console.log(dd.items()); // [['123', []], [123, ['yay!']]]
share|improve this answer
1  
You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and how it improves upon the original) so that the author can learn from your thought process. – Quill Jul 18 at 0:45
    
I second Quill's comment. If you explain what you've done and why exactly it's better than the code in the question, ping me and the downvote can turn into an upvote maybe. – janos Sep 18 at 16:11

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.