MultiKey Dictionary

While developing some kind of event monitoring Manager class i come across a problem of too many Object used : Each IEventDispatcher source can dispatch multiple eventNames, each eventName can have several function attached to it. The popular approach is using nested Dictionary/Object as the main data structure :

_sourceDict : Dictionary; /* map IEventDispatchers to _eventObjects */
_eventObject : Object; /* map eventName to _handlerDict */
_handlerDict : Dictionary; /* map handler to its params */

For faster check and overwrite the existed _handler for a specific source and eventName we need to use a Dictionary for _handlerDict instead of an Array, and by the way, add support for user parameters.

This data structure of course will still work without any problem even though the code looks busy by nested properties access. We might also got slow down by looping through those nested structure. Let’s have a look through another implementation where we save multiple values as the key instead of only one.

//how about :
function addListener(source, eventName, handler, params): void {
	mkd.setValue(function (e: Event): void {
		handler.apply(null, params);
	}, source, eventName, handler); // --> value, key1, key2, key3
}

//instead of 
function addListener(source, eventName, handler, params): void {
	var evtObj	: Object = _dict[ source] ||= {};
	var handlerDict	: Dictionary = evtObj[eventName] || new Dictionary();
	handlerDict[handler] = function (e: Event): void {
		handler.apply(null, params);
	}
}

package vn.core.data 
{
	import flash.utils.Dictionary;
	/**
	 * ...
	 * @author thienhaflash
	 */
	public class MultiKey 
	{
		private var keys 		: Dictionary;
		private var keyCnt		: int;
		private var key2Value	: Dictionary; /* Object should also be fine here */
		
		private function combineKeys(autoNew: Boolean, keyList: Array): Array {
			if (!keys) return null;
			var arr	 : Array = [];
			var ckey : * ;
			var l	 : int = keyList.length;
			
			for (var i: int = 0; i <l; i++) {//keep the order !
				ckey = keys[keyList[i]];
				if (!ckey) { //skip zero !
					if (autoNew) {
						keys[keyList[i]]	= ++keyCnt;
						keys[keyCnt]		= keyList[i];
						ckey				= keyCnt;
					} else {
						return null;
					}
				}
				arr.push(ckey);
			}
			//arr.sort(Array.NUMERIC); /* so the key is unique */
			return arr;
		}
		
		private function isValidKey(keys : Array, combine: Array): Boolean {
			var l: int = combine.length;
			for (var i: int = 0; i < l; i++) {
				if (keys.indexOf(''+combine[i]) == -1) {//can not found some key
					return false;
				}
			}
			return true;
		}
		
		public function break2Key(s: String): Array {
			var arr : Array = s.split('*');
			for (var i: int = arr.length - 1; i > -1; i--) {
				arr[i] = keys[arr[i]];
			}
			return arr;
		}
		
		public function removeValue(...keyList): * {
			var s 		: String = combineKeys(false, keyList).join('*');
			var value	: * ;
			if (s) {
				value = key2Value[s];
				delete key2Value[s];
			}
			return value;
		}
		
		public function filterValues(keyList: Array, returnValues: Boolean = true): Array {
			var combined	: Array = combineKeys(false, keyList);
			var result		: Array = [];
			for (var s : String in key2Value) {
				if (isValidKey(s.split('*'), combined)) {
					result.push(returnValues ? key2Value[s] : s);
				}
			}
			return result;
		}
		
		public function removeValues(keyList: Array, returnValues: Boolean = true): Object {
			var combined	: Array = combineKeys(false, keyList);
			var result		: Object = {};
			for (var s : String in key2Value) {
				if (isValidKey(s.split('*'), combined)) {
					result[s] = key2Value[s];
					delete key2Value[s];
				}
			}
			return result;
		}
		
		public function getValue(...keyList): * {
			var combined	: Array = combineKeys(false, keyList);
			return combined ? key2Value[combined.join('*')] : null;
		}
		
		public function setValue(value: *, ...keyList): void {
			if (!keys) {
				keys = new Dictionary();
				key2Value = new Dictionary();
			}
			key2Value[combineKeys(true, keyList).join('*')] = value;
		}
	}
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s