You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					196 lines
				
				3.8 KiB
			
		
		
			
		
	
	
					196 lines
				
				3.8 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Mnemonist BiMap
							 | 
						||
| 
								 | 
							
								 * ================
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * JavaScript implementation of a BiMap.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								var forEach = require('obliterator/foreach');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Inverse Map.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @constructor
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function InverseMap(original) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.size = 0;
							 | 
						||
| 
								 | 
							
								  this.items = new Map();
							 | 
						||
| 
								 | 
							
								  this.inverse = original;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * BiMap.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @constructor
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function BiMap() {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.size = 0;
							 | 
						||
| 
								 | 
							
								  this.items = new Map();
							 | 
						||
| 
								 | 
							
								  this.inverse = new InverseMap(this);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Method used to clear the map.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return {undefined}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function clear() {
							 | 
						||
| 
								 | 
							
								  this.size = 0;
							 | 
						||
| 
								 | 
							
								  this.items.clear();
							 | 
						||
| 
								 | 
							
								  this.inverse.items.clear();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								BiMap.prototype.clear = clear;
							 | 
						||
| 
								 | 
							
								InverseMap.prototype.clear = clear;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Method used to set a relation.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param  {any} key - Key.
							 | 
						||
| 
								 | 
							
								 * @param  {any} value - Value.
							 | 
						||
| 
								 | 
							
								 * @return {BiMap|InverseMap}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function set(key, value) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // First we need to attempt to see if the relation is not flawed
							 | 
						||
| 
								 | 
							
								  if (this.items.has(key)) {
							 | 
						||
| 
								 | 
							
								    var currentValue = this.items.get(key);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // The relation already exists, we do nothing
							 | 
						||
| 
								 | 
							
								    if (currentValue === value)
							 | 
						||
| 
								 | 
							
								      return this;
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								      this.inverse.items.delete(currentValue);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this.inverse.items.has(value)) {
							 | 
						||
| 
								 | 
							
								    var currentKey = this.inverse.items.get(value);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (currentKey === key)
							 | 
						||
| 
								 | 
							
								      return this;
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								      this.items.delete(currentKey);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Here we actually add the relation
							 | 
						||
| 
								 | 
							
								  this.items.set(key, value);
							 | 
						||
| 
								 | 
							
								  this.inverse.items.set(value, key);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Size
							 | 
						||
| 
								 | 
							
								  this.size = this.items.size;
							 | 
						||
| 
								 | 
							
								  this.inverse.size = this.inverse.items.size;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								BiMap.prototype.set = set;
							 | 
						||
| 
								 | 
							
								InverseMap.prototype.set = set;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Method used to delete a relation.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param  {any} key - Key.
							 | 
						||
| 
								 | 
							
								 * @return {boolean}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function del(key) {
							 | 
						||
| 
								 | 
							
								  if (this.items.has(key)) {
							 | 
						||
| 
								 | 
							
								    var currentValue = this.items.get(key);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.items.delete(key);
							 | 
						||
| 
								 | 
							
								    this.inverse.items.delete(currentValue);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Size
							 | 
						||
| 
								 | 
							
								    this.size = this.items.size;
							 | 
						||
| 
								 | 
							
								    this.inverse.size = this.inverse.items.size;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return true;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								BiMap.prototype.delete = del;
							 | 
						||
| 
								 | 
							
								InverseMap.prototype.delete = del;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Mapping some Map prototype function unto our two classes.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								var METHODS = ['has', 'get', 'forEach', 'keys', 'values', 'entries'];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								METHODS.forEach(function(name) {
							 | 
						||
| 
								 | 
							
								  BiMap.prototype[name] = InverseMap.prototype[name] = function() {
							 | 
						||
| 
								 | 
							
								    return Map.prototype[name].apply(this.items, arguments);
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Attaching the #.values method to Symbol.iterator if possible.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								if (typeof Symbol !== 'undefined') {
							 | 
						||
| 
								 | 
							
								  BiMap.prototype[Symbol.iterator] = BiMap.prototype.entries;
							 | 
						||
| 
								 | 
							
								  InverseMap.prototype[Symbol.iterator] = InverseMap.prototype.entries;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Convenience known methods.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								BiMap.prototype.inspect = function() {
							 | 
						||
| 
								 | 
							
								  var dummy = {
							 | 
						||
| 
								 | 
							
								    left: this.items,
							 | 
						||
| 
								 | 
							
								    right: this.inverse.items
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Trick so that node displays the name of the constructor
							 | 
						||
| 
								 | 
							
								  Object.defineProperty(dummy, 'constructor', {
							 | 
						||
| 
								 | 
							
								    value: BiMap,
							 | 
						||
| 
								 | 
							
								    enumerable: false
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return dummy;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if (typeof Symbol !== 'undefined')
							 | 
						||
| 
								 | 
							
								  BiMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = BiMap.prototype.inspect;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								InverseMap.prototype.inspect = function() {
							 | 
						||
| 
								 | 
							
								  var dummy = {
							 | 
						||
| 
								 | 
							
								    left: this.inverse.items,
							 | 
						||
| 
								 | 
							
								    right: this.items
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Trick so that node displays the name of the constructor
							 | 
						||
| 
								 | 
							
								  Object.defineProperty(dummy, 'constructor', {
							 | 
						||
| 
								 | 
							
								    value: InverseMap,
							 | 
						||
| 
								 | 
							
								    enumerable: false
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return dummy;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if (typeof Symbol !== 'undefined')
							 | 
						||
| 
								 | 
							
								  InverseMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = InverseMap.prototype.inspect;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Static @.from function taking an arbitrary iterable & converting it into
							 | 
						||
| 
								 | 
							
								 * a bimap.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param  {Iterable} iterable - Target iterable.
							 | 
						||
| 
								 | 
							
								 * @return {BiMap}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								BiMap.from = function(iterable) {
							 | 
						||
| 
								 | 
							
								  var bimap = new BiMap();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  forEach(iterable, function(value, key) {
							 | 
						||
| 
								 | 
							
								    bimap.set(key, value);
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return bimap;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Exporting.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								module.exports = BiMap;
							 |