Is there any good JavaScript hash(code/table) implementation out there?

前端 未结 4 1164
渐次进展
渐次进展 2020-12-05 05:58

Yes, I know you could use regular objects as associative arrays in JavaScript, but I\'d like to use something closer to java\'s Map\'s implementation (HashMap, LinkedHashMap

4条回答
  •  庸人自扰
    2020-12-05 06:26

    Here's a naive implementation I just put together - as keparo mentioned in a comment, one of the big issues is equality checking:

    var ObjectMap = function()
    {
        this._keys = [];
        this._values = [];
    };
    
    ObjectMap.prototype.clear = function()
    {
        this._keys = [];
        this._values = [];
    };
    
    ObjectMap.prototype.get = function(key)
    {
        var index = this._indexOf(key, this._keys);
        if (index != -1)
        {
            return this._values[index];
        }
        return undefined;
    };
    
    ObjectMap.prototype.hasKey = function(key)
    {
        return (this._indexOf(key, this._keys) != -1);
    };
    
    ObjectMap.prototype.hasValue = function(value)
    {
        return (this._indexOf(value, this._values) != -1);
    };
    
    ObjectMap.prototype.put = function(key, value)
    {
        var index = this._indexOf(key, this._keys);
        if (index == -1)
        {
            index = this._keys.length;
        }
    
        this._keys[index] = key;
        this._values[index] = value;
    };
    
    ObjectMap.prototype.remove = function(key)
    {
        var index = this._indexOf(key, this._keys);
        if (index != -1)
        {
            this._keys.splice(index, 1);
            this._values.splice(index, 1);
        }
    };
    
    ObjectMap.prototype.size = function()
    {
        return this._keys.length;
    };
    
    ObjectMap.prototype._indexOf = function(item, list)
    {
        for (var i = 0, l = list.length; i < l; i++)
        {
            if (this._equals(list[i], item))
            {
                return i;
            }
        }
        return -1;
    };
    
    ObjectMap.prototype._equals = function(a, b)
    {
        if (a === b)
        {
            return true;
        }
    
        // Custom objects can implement an equals method
        if (typeof a.equals == "function" &&
            typeof b.equals == "function")
        {
            return a.equals(b);
        }
    
        // Arrays are equal if they're the same length and their contents are equal
        if (a instanceof Array && b instanceof Array)
        {
            if (a.length != b.length)
            {
                return false;
            }
    
            for (var i = 0, l = a.length; i < l; i++)
            {
                if (!this._equals(a[i], b[i]))
                {
                    return false;
                }
            }
    
            return true;
        }
    
        // Checking object properties - objects are equal if they have all the same
        // properties and they're all equal.
        var seenProperties = {};
        for (var prop in a)
        {
            if (a.hasOwnProperty(prop))
            {
                if (!b.hasOwnProperty(prop))
                {
                    return false;
                }
    
                if (!this._equals(a[prop], b[prop]))
                {
                    return false;
                }
    
                seenProperties[prop] = true;
            }
        }
    
        for (var prop in b)
        {
            if (!(prop in seenProperties) && b.hasOwnProperty(prop))
            {
                if (!a.hasOwnProperty(prop))
                {
                    return false;
                }
    
                if (!this._equals(b[prop], a[prop]))
                {
                    return false;
                }
            }
        }
    
        return true;
    };
    

    Example usage:

    >>> var map = new ObjectMap();
    >>> var o = {a: 1, b: [1,2], c: true};
    >>> map.put(o, "buns");
    >>> map.get(o)
    "buns"
    >>> map.get({a: 1, b: [1,2], c: true});
    "buns"
    >>> map.get({a: 1, b: [1,2], c: true, d:"hi"});
    >>> var a = [1,2,3];
    >>> map.put(a, "cheese");
    >>> map.get(a);
    "cheese"
    >>> map.get([1,2,3]);
    "cheese"
    >>> map.get([1,2,3,4]);
    >>> var d = new Date();
    >>> map.put(d, "toast");
    >>> map.get(d);
    "toast"
    >>> map.get(new Date(d.valueOf()));
    "toast"
    

    This is in no way a complete implementation, just a pointer for a way to implement such an object. For example, looking at what I've given, you would also need to add constructor property checks before the object property check, as this currently works:

    >>> function TestObject(a) { this.a = a; };
    >>> var t = new TestObject("sandwich");
    >>> map.put(t, "butter");
    >>> map.get({a: "sandwich"})
    "butter"
    

提交回复
热议问题