* ./utils/Comparator.js
/**
* Created by Mch on 8/18/18.
*/
function Comparator(comparefunction) {
this.compare = comparefunction || Comparator.defaultCompareFunction;
}
Comparator.defaultCompareFunction = function(a, b) {
if (a===b) {
return 0;
}
return a < b ? -1 : 1;
};
Comparator.prototype.equal = function(a, b) {
return this.compare(a, b) === 0;
};
Comparator.prototype.lessThan = function(a, b) {
return this.compare(a, b) < 0;
};
Comparator.prototype.greaterThan = function(a, b) {
return this.compare(a, b) > 0;
};
Comparator.prototype.lessThanOrEqual = function(a, b) {
return this.lessThan(a, b) || this.equal(a, b);
};
Comparator.prototype.greaterThanOrEqual = function(a, b) {
return this.greaterThan(a, b) || this.equal(a, b);
};
Comparator.prototype.reverse = function() {
const compareOriginal = this.compare;
this.compare = function (a, b) {
return compareOriginal(b, a)
}
};
exports.Comparator = Comparator;
* Collection/LinkedList.js
/**
* Created by Mch on 8/26/18.
*/
var Comparator = require('../utils/Comparator').Comparator;
function Node(e) {
this.element = e;
this.next = null;
}
function LinkedList(comparator) {
this.length = 0;
this.head = null;
this.comparator = comparator || Comparator.defaultCompareFunction;
}
LinkedList.prototype = {
append: function(element) {
var node = new Node(element), current;
if (this.head === null) {
this.head = node;
} else {
current = this.head;
while (current.next) {
current = current.next;
}
current.next = node;
}
this.length++;
},
push: function(element) {
return this.append(element);
},
insert: function(position, element) {
if (position <0 || position > this.length) {
return false;
}
var node = new Node(element),
current = this.head,
previous,
index = 0;
if (position === 0) {
node.next = current;
this.head = node;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;
}
this.length++;
return true;
},
_removeNode: function(previous) {
var current = previous.next;
previous.next = current.next;
current = null;
this.length--;
},
removeNode: function(fn) {
var current = this.head, previous = null;
if (current===null) {
return null;
}
// head
if (fn(current.element)) {
previous = this.head;
current = current.head;
delete previous;
this.length--;
return this.head = current;
}
while (current && current.next) {
if (fn(current.element)) {
this._removeNode(previous);
return current;
}
previous = current;
current = current.next;
}
if (current && fn(current.element)) {
this._removeNode(previous);
return current;
}
return null;
},
removeAt: function(position) {
if (position <0 || position > this.length) {
return null;
}
var current = this.head,
previous,
index = 0;
if (position=== 0) {
this.head = current.next;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
previous.next = current.next;
}
this.length--;
return current.element;
},
remove: function(element) {
var index = this.indexOf(element);
return this.removeAt(index);
},
indexOf: function(element) {
var current = this.head,
index = -1;
while (current) {
if (this.comparator.equal(element, current.element)) {
return index;
}
index++;
current = current.next;
}
return -1;
},
/**
* find node element
* @param fn {function(Node.element)} callback
* @returns {*}
*/
find: function(fn) {
var current = this.head;
if (current===null) {
return undefined;
}
while (current.next) {
if (fn(current.element)) {
return current.element;
}
current = current.next;
}
if (fn(current.element)) {
return current.element;
}
return undefined;
},
isEmpty: function() {
return this.length === 0;
},
size: function() {
return this.length;
},
forEach: function(/* function */ fn) {
var current = this.head;
while (current !== null && current !== undefined) {
fn(current.element);
current = current.next;
}
},
map: function(/* function */ fn) {
var current = this.head, list = new LinkedList();
while (current !== null && current !== undefined) {
list.append( fn(current.element) );
current = current.next;
}
return list;
},
/**
* reduce
* @param fn {function(*, *=, number=, LinkedList.<T>=)} callback
* @param init {*}
*/
reduce: function(/* function */ fn, /* Object */ init) {
var current = this.head, acc = init;
while (current !== null) {
acc = fn(acc, current.element);
current = current.next;
}
return acc;
},
toString: function() {
var s = '';
this.forEach(function(e) {
s += e + ", ";
});
return s;
},
print: function() {
console.log(this.toString());
},
getHead: function() {
return this.head;
},
destroy: function() {
if (this.head===null) {
return 0;
}
if (this.head.next===null) {
delete this.head;
return 1;
}
var c = this.head, n = c.next, l = 0;
while (c !== null) {
delete(c);
l++;
c = n;
if (n !== null) {
n = n.next;
}
}
return l;
}
};
exports.LinkedList = LinkedList;
* ./Collection/Dictionary.js
/**
* Created by Mch on 8/20/18.
*/
var LinkedList = require('./LinkedList').LinkedList,
Comparator = require('../utils/Comparator').Comparator;
function Dictionary(comparator) {
this.table = [];
// compare KeyValuePair.key
this.comparator = comparator || Comparator.defaultCompareFunction;
}
function KeyValuePair(key, value) {
this.key = key;
this.value = value;
}
KeyValuePair.prototype.toString = function() {
return '['+ this.key +'-'+ this.value +']';
};
var hash = {
loselose: function(key) {
var hash = 0;
for (var i = 0; i < key.length; i++) {
hash += key.charCodeAt(i);
}
return hash % 37;
},
djb2: function(key) {
var hash = 5381;
for (var i = 0; i < key.length; i++) {
hash = hash * 33 + key.charCodeAt(i);
}
return hash % 1013;
}
};
// pick a hash function
Dictionary.hashCode = hash.loselose;
Dictionary.prototype.has = function(key) {
return this._get(key) !== undefined;
};
Dictionary.prototype.set = function(key, value) {
var p = Dictionary.hashCode(key);
if (this.table[p] === undefined) {
this.table[p] = new LinkedList(this.comparator);
}
// unique key
if (!this.has(key)) {
this.table[p].append(new KeyValuePair(key, value));
}
};
/**
* get key-value
* @param key
* @returns KeyValuePair
* @private
*/
Dictionary.prototype._get = function (key) {
var pos = Dictionary.hashCode(key),
list = this.table[pos],
d = this;
if (list !== undefined) {
return list.find(function(/* KeyValuePair */ e) {
return d.comparator.equal(e.key, key);
});
}
return undefined;
};
Dictionary.prototype.get = function(key) {
var kv = this._get(key);
if (kv !== undefined && kv instanceof KeyValuePair) {
return kv.value;
}
return undefined;
};
Dictionary.prototype.remove = function(key) {
var position = Dictionary.hashCode(key),
list = this.table[position],
d = this;
if (list === undefined) {
return false;
}
list.removeNode(function(/* KeyValuePair */ kv) {
return d.comparator.equal(kv.key, key);
});
if (list.isEmpty()) {
this.table[position] = undefined;
}
return true;
};
Dictionary.prototype.keys = function() {
return this.table.reduce(function(/* Array */ a, /* LinkedList */ c) {
if (c !== undefined) {
c.forEach(function(/* KeyValuePair */ e) {
if (a instanceof Array) {
a.push(e.key);
}
});
}
return a;
}, []);
};
Dictionary.prototype.values = function() {
return this.table.reduce(function(a, c) {
if (c !== undefined) {
c.forEach(function(e) {
a.push(e.value);
});
return a;
}
}, []);
};
Dictionary.prototype.getItems = function() {
return this.table.reduce(function(o, /* LinkedList */ c) {
if (c !== undefined) {
c.forEach(function(/* KeyValuePair */ e) {
o[e.key] = e.value;
});
}
return o;
}, {});
};
Dictionary.prototype.clear = function() {
this.table = this.table.map(function (list) {
if (list !== undefined) {
list.destroy();
list = undefined;
}
return list;
});
this.table = [];
};
Dictionary.prototype.size = function() {
return this.table.reduce(function(a, c) {
if (c !== undefined) {
a += c.size();
}
return a;
}, 0);
};
Dictionary.prototype.print = function() {
this.table.forEach(function(list, index) {
if (list !== undefined) {
console.log(index + ": " + list.toString());
}
});
};
exports.Dictionary = Dictionary;
* TestDictionary.js
/**
* Created by Mch on 8/20/18.
*/
var Dictionary = require('./Collection/Dictionary').Dictionary,
Comparator = require('./utils/Comparator').Comparator;
var dictionary = new Dictionary(
new Comparator(function(/* String */a, /* String */b) {
return a.localeCompare(b);
}));
dictionary.set('Gandalf', 'gandalf@email.com');
dictionary.set('John', 'johnsnow@email.com');
dictionary.set('Tyrion', 'tyrion@email.com');
dictionary.set('Aaron', 'aaron@email.com');
dictionary.set('Donnie', 'donnie@email.com');
dictionary.set('Ana', 'ana@email.com');
dictionary.set('Jonathan', 'jonathan@email.com');
dictionary.set('Jamie', 'jamie@email.com');
dictionary.set('Sue', 'sue@email.com');
dictionary.set('Mindy', 'mindy@email.com');
dictionary.set('Paul', 'paul@email.com');
dictionary.set('Nathan', 'nathan@email.com');
dictionary.set('Gandalf', 'gandalf@email.com');
dictionary.set('John', 'johnsnow@email.com');
dictionary.set('Tyrion', 'tyrion@email.com');
console.log(dictionary.has('Gandalf'));
console.log(dictionary.print());
console.log(dictionary.size());
console.log(dictionary.keys());
console.log(dictionary.values());
console.log(dictionary.get('Tyrion'));
dictionary.remove('John');
console.log(dictionary.size());
console.log(dictionary.keys());
$ node TestDictionary.js
true
5: [Jonathan-jonathan@email.com], [Jamie-jamie@email.com], [Sue-sue@email.com],
10: [Nathan-nathan@email.com],
13: [Donnie-donnie@email.com], [Ana-ana@email.com],
16: [Tyrion-tyrion@email.com], [Aaron-aaron@email.com],
19: [Gandalf-gandalf@email.com],
29: [John-johnsnow@email.com],
32: [Mindy-mindy@email.com], [Paul-paul@email.com],
undefined
12
[ 'Jonathan',
'Jamie',
'Sue',
'Nathan',
'Donnie',
'Ana',
'Tyrion',
'Aaron',
'Gandalf',
'John',
'Mindy',
'Paul' ]
[ 'jonathan@email.com',
'jamie@email.com',
'sue@email.com',
'nathan@email.com',
'donnie@email.com',
'ana@email.com',
'tyrion@email.com',
'aaron@email.com',
'gandalf@email.com',
'johnsnow@email.com',
'mindy@email.com',
'paul@email.com' ]
tyrion@email.com
11
[ 'Jonathan',
'Jamie',
'Sue',
'Nathan',
'Donnie',
'Ana',
'Tyrion',
'Aaron',
'Gandalf',
'Mindy',
'Paul' ]