Get size of ActionScript 3 Dictionary

2019-01-26 02:52发布

问题:

var d:Dictionary = new Dictionary();
d["a"] = "b";
d["b"] = "z";

How to get the length/size of the dictionary (which is 2) ?

回答1:

There is no built-in method to get the size/lenght/count of an AS3 dictionary. There are workarounds: for example, you can create a custom dictionary class which extends or wraps the flash.utils.Dictionary class, adding the counter functionality. You can manage the count as entries are added/removed, or count on-demand using a simple For loop iteration:

public static function countKeys(myDictionary:flash.utils.Dictionary):int 
{
    var n:int = 0;
    for (var key:* in myDictionary) {
        n++;
    }
    return n;
}


回答2:

One would ideally just implement a wrapper around Dictionary that extends the Proxy class. That allows you to override and intercept the addition and removal of properties from the Dictionary, allowing the new Dictionary class to be used exactly like the original, with the exact same syntax and abilities, with the addition of a length function that returns the number of keys.

This implementation works as follows. When a property is set or deleted, it checks whether the property already exists (whether it strictly equals undefined) and increments or decrements the internal length counter accordingly. This implementation also automatically deletes an entry when its value is set to undefined for consistency.

I wrote this Dictionary wrapper just for this question; it took about 5 minutes, and it provides a length function that returns the length. I chose to make it a function rather than a property so that it doesn't interfere with property names or enumeration of the Dictionary's properties.

DO NOT USE THIS IMPLEMENTATION; USE THE ONE THAT FOLLOWS IT INSTEAD. I explain why below.

package flos.utils 
{
    import flash.utils.flash_proxy;
    import flash.utils.Proxy;

    public class Dictionary extends Proxy
    {
        private var d:flash.utils.Dictionary;
        private var _length:int = 0;

        public function Dictionary( weakKeys:Boolean = false ) 
        {
            d = new flash.utils.Dictionary( weakKeys );
        }

        public function length():int
        {
            return _length;
        }

        override flash_proxy function getProperty(name:*):* 
        {
            return d[name];
        }

        override flash_proxy function setProperty(name:*, value:*):void 
        {
            if (value === undefined) //delete property when explicitly set to undefined, to enforce rule that an undefined property does not exist and is not counted
            {
                delete this[name];
                return;
            }
            if (d[name] === undefined)
                _length++;
            d[name] = value;
        }

        override flash_proxy function deleteProperty(name:*):Boolean 
        {
            if (d[name] !== undefined)
            {
                delete d[name];
                _length--;
                return true;
            }
            return false;
        }
    }
}

DISCLAIMER: The implementation above, although the most promising approach which could have worked in theory, is ultimately a dead end, since Dictionary is inherently incompatible with the Proxy interface methods.

First, the setProperty, getProperty, and deleteProperty methods appear to receive untyped name parameters, but they are actually strongly typed QName objects, which essentially restrict your keys to String type names, just like Object and associate Arrays. Dictionary is not bound by this limitation and allows to use object instances as unique keys, so it is inherently incompatible with the Proxy class methods. The Dictionary class's documentation also has a single note, and it explicitly states that QName objects cannot be used as keys.

Likewise, the nextName method of Proxy prevents you from enumerating over all Dictionary keys for the same reason, because it has a strongly-type return value of String. So even if setProperty, getProperty actually accepted untyped keys for the names, you still wouldn't be able to retrieve them via enumeration because the nextName method returns only type string. Dictionary is simply in a class of its own.

The best thing you could do is implement a wrapper like the one above that exposes the underlying dictionary for enumeration, but others requires calling explicit addKey/removeKey methods with untyped names and values, instead of using the Proxy behavior.

Given all of the above, a better implementation would be as follows, where you manipulate the dictionary by calling methods such as getValue/setValue/removeValue, and have access to a keys enumeration as well as a length property:

public class Dictionary
{
    private var d:flash.utils.Dictionary;
    private var _keys:Array;

    public function Dictionary( weakKeys:Boolean = false )
    {
        d = new flash.utils.Dictionary( weakKeys );
        _keys = new Array();
    }

    public function get keys():Array
    {
        return _keys.concat(); //return copy of keys array
    }

    public function get length():int
    {
        return _keys.length;
    }

    public function containsKey( key:* ):Boolean
    {
        return d[key] !== undefined;
    }

    public function setValue( key:*, value:* ):void
    {
        if (value === undefined) //delete property when explicitly set to undefined, to enforce rule that an undefined property does not exist and is not counted
        {
            removeValue( key );
            return;
        }
        if (d[key] === undefined)
        {
            _keys.push( key );
        }
        d[key] = value;
    }

    public function getValue( key:* ):*
    {
        return d[key];
    }

    public function removeValue( key:* ):Boolean
    {
        if (d[key] !== undefined)
        {
            delete d[key];
            var i:int = _keys.indexOf( key );
            if (i > -1)
                _keys.splice( i, 1 );
            return true;
        }
        return false;
    }


回答3:

You can use DictionaryUtil:

var dictionaryLength : int = DictionaryUtil.getKeys(d).length;