//============================================================================== // Copyright 2015-2019 Moraware, Inc. // // Purpose: JavaScript-based collections. // //============================================================================== /* eslint no-unused-vars: "off" */ /*----------------------------------------------------------------------------*/ function MJTSet(options_) { var self = this, arrInitialValues = (options_ || {}).arrInitialValues; self.array = []; // Will contain a list of the key/value items in the // set. Don't rely on it being ordered in any // particular way! self.mapOneBasedIndex = {}; // The value with key=mjtKeyFromVal(value) will map to the // ONE-based index into array of the corresponding item. // As currently implemented, removal is a potentially // expensive operation because it may involve re-setting // the values of every remaining item in // {this.mapOneBasedIndex}. if (arrInitialValues) { self.addFromArray(arrInitialValues); } } /*----------------------------------------------------------------------------*/ function mjtKeyFromVal(value_) { return 'k' + (typeof value_) + ':' + value_; } /*----------------------------------------------------------------------------*/ // This method will either add the new value or replace the already added // value in place. // // Since the object's key is based on it's string representation, it's possible // for two different objects to have the same key. Hence, we always replace the // stored value, even if the value was found to have already been added. MJTSet.prototype.add = function (value_) { var self = this, mapOneBasedIndex = self.mapOneBasedIndex, array = self.array, strKey = mjtKeyFromVal(value_), tmpCurrentOneBasedIndex = mapOneBasedIndex[strKey], tmpIndexOfItemToBeAdded = tmpCurrentOneBasedIndex ? (tmpCurrentOneBasedIndex - 1) : array.length; // Add or set the new value. array[tmpIndexOfItemToBeAdded] = value_; mapOneBasedIndex[strKey] = tmpIndexOfItemToBeAdded + 1; }; /*----------------------------------------------------------------------------*/ MJTSet.prototype.contains = function (value_) { return this.mapOneBasedIndex[mjtKeyFromVal(value_)]; }; /*----------------------------------------------------------------------------*/ MJTSet.prototype.count = function () { return this.array.length; }; /*----------------------------------------------------------------------------*/ MJTSet.prototype.itemAt = function (zeroBasedIndex_) { var array = this.array, tmpCount = array.length; if (zeroBasedIndex_ > -1 && zeroBasedIndex_ < tmpCount) { return array[zeroBasedIndex_]; } }; /*----------------------------------------------------------------------------*/ MJTSet.prototype.remove = function (value_) { var strKey = mjtKeyFromVal(value_), mapOneBasedIndex = this.mapOneBasedIndex, array = this.array, oneBasedIndexOfItem = mapOneBasedIndex[strKey], idx, tmpCount = array.length; if (oneBasedIndexOfItem) { // The item is present. // First, remove it from the array... array.splice(oneBasedIndexOfItem - 1/*index*/, 1/*howMany, item1, item2, ...*/); // ...then remove the keyed entry from the map... delete mapOneBasedIndex[strKey]; // ...finally, update the keys of any items that followed it in the now-spliced array. for (idx = oneBasedIndexOfItem - 1; idx < array.length; ++idx) { strKey = mjtKeyFromVal(array[idx]); mapOneBasedIndex[strKey] = idx + 1; } return 1; } }; /*----------------------------------------------------------------------------*/ // If a match is found for the given item, the matching stored item will be // returned. MJTSet.prototype.item = function (value_) { var strKey = mjtKeyFromVal(value_), mapOneBasedIndex = this.mapOneBasedIndex, oneBasedIndexOfItem = mapOneBasedIndex[strKey]; if (oneBasedIndexOfItem) { return this.array[oneBasedIndexOfItem - 1]; } }; /*----------------------------------------------------------------------------*/ // fnMap_ will be given as its first parameter a value from the set and should // return the value to be used in the resulting array. MJTSet.prototype.mapValuesToArray = function (fnMap_) { return this.array.map(fnMap_); }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ MJTSet.prototype.addFromArray = function (array_) { var self = this, idx; for (idx = 0; idx < array_.length; ++idx) { self.add(array_[idx]); } return self; }; /*----------------------------------------------------------------------------*/ function MJTKeyValuePair(key_, value_) { this.key = key_; this.value = value_; } /*----------------------------------------------------------------------------*/ // Use mjtKeyFromVal() on the key-value's key as the toString() value for this // object so that an MJTSet can be used to store the KeyValue contents of the // dictionary. MJTKeyValuePair.prototype.toString = function () { return mjtKeyFromVal(this.key); }; /*----------------------------------------------------------------------------*/ function MJTDictionary() { this.kvSet = new MJTSet(); // Set of Key-Value pairs used to manage the // dictionary. } /*----------------------------------------------------------------------------*/ MJTDictionary.prototype.add = function (key_, value_) { var self = this, kvSet = self.kvSet, kvPair = new MJTKeyValuePair(key_, value_); kvSet.add(kvPair); }; /*----------------------------------------------------------------------------*/ MJTDictionary.prototype.containsKey = function (key_) { return this.kvSet.contains(new MJTKeyValuePair(key_, 0)); }; /*----------------------------------------------------------------------------*/ MJTDictionary.prototype.count = function () { return this.kvSet.count(); }; /*----------------------------------------------------------------------------*/ MJTDictionary.prototype.itemAt = function (zeroBasedIndex_) { return this.kvSet.itemAt(zeroBasedIndex_); }; /*----------------------------------------------------------------------------*/ MJTDictionary.prototype.removeItemWithKey = function (key_) { return this.kvSet.remove(new MJTKeyValuePair(key_, 0)); }; /*----------------------------------------------------------------------------*/ MJTDictionary.prototype.item = function (key_) { var rc = this.kvSet.item(new MJTKeyValuePair(key_, 0)); if (rc) { return rc.value; } }; /*----------------------------------------------------------------------------*/ MJTDictionary.prototype.getItemWithDefaultValue = function (key_, default_) { var rc = this.kvSet.item(new MJTKeyValuePair(key_, 0)); if (rc) { return rc.value; } return default_; }; /*----------------------------------------------------------------------------*/ function mjtKeyFromKV(kvPair_) { return kvPair_.key; } /*----------------------------------------------------------------------------*/ function mjtValueFromKV(kvPair_) { return kvPair_.value; } /*----------------------------------------------------------------------------*/ MJTDictionary.prototype.getKeysArray = function () { return this.kvSet.mapValuesToArray(mjtKeyFromKV); }; /*----------------------------------------------------------------------------*/ MJTDictionary.prototype.getValuesArray = function () { return this.kvSet.mapValuesToArray(mjtValueFromKV); };