-
-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9a43b8d
commit 9a36119
Showing
5 changed files
with
337 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
## 0.10.0 | ||
|
||
* Adding `Index`. | ||
* Adding `MultiMap`. | ||
* Adding `MultiSet`. | ||
* Adding `SymSpell`. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/** | ||
* Mnemonist Index | ||
* ==================== | ||
* | ||
* The Index is basically an abstract HashMap where given keys or items | ||
* are hashed by a function to produce a specific key so that one may | ||
* query it using the same strategies. | ||
*/ | ||
var iterateOver = require('./utils/iterate.js'); | ||
|
||
var identity = function(x) { | ||
return x; | ||
}; | ||
|
||
/** | ||
* Index. | ||
* | ||
* @constructor | ||
* @param {array|function} descriptor - Hash functions descriptor. | ||
*/ | ||
function Index(descriptor) { | ||
this.items = new Map(); | ||
this.clear(); | ||
|
||
if (Array.isArray(descriptor)) { | ||
this.writeHashFunction = descriptor[0]; | ||
this.readHashFunction = descriptor[1]; | ||
} | ||
else { | ||
this.writeHashFunction = descriptor; | ||
this.readHashFunction = descriptor; | ||
} | ||
|
||
if (!this.writeHashFunction) | ||
this.writeHashFunction = identity; | ||
if (!this.readHashFunction) | ||
this.readHashFunction = identity; | ||
|
||
if (typeof this.writeHashFunction !== 'function') | ||
throw new Error('mnemonist/Index.constructor: invalid hash function given.'); | ||
|
||
if (typeof this.readHashFunction !== 'function') | ||
throw new Error('mnemonist/Index.constructor: invalid hash function given.'); | ||
} | ||
|
||
/** | ||
* Method used to clear the structure. | ||
* | ||
* @return {undefined} | ||
*/ | ||
Index.prototype.clear = function() { | ||
this.items.clear(); | ||
|
||
// Properties | ||
this.size = 0; | ||
}; | ||
|
||
/** | ||
* Method used to add an item to the index. | ||
* | ||
* @param {any} item - Item to add. | ||
* @return {Index} | ||
*/ | ||
Index.prototype.add = function(item) { | ||
var key = this.writeHashFunction(item); | ||
|
||
this.items.set(key, item); | ||
this.size = this.items.size; | ||
|
||
return this; | ||
}; | ||
|
||
/** | ||
* Method used to set an item in the index using the given key. | ||
* | ||
* @param {any} key - Key to use. | ||
* @param {any} item - Item to add. | ||
* @return {Index} | ||
*/ | ||
Index.prototype.set = function(key, item) { | ||
key = this.writeHashFunction(key); | ||
|
||
this.items.set(key, item); | ||
this.size = this.items.size; | ||
|
||
return this; | ||
}; | ||
|
||
/** | ||
* Method used to retrieve an item from the index. | ||
* | ||
* @param {any} key - Key to use. | ||
* @return {Index} | ||
*/ | ||
Index.prototype.get = function(key) { | ||
key = this.readHashFunction(key); | ||
|
||
return this.items.get(key); | ||
}; | ||
|
||
/** | ||
* Method used to iterate over each of the index's values. | ||
* | ||
* @param {function} callback - Function to call for each item. | ||
* @param {object} scope - Optional scope. | ||
* @return {undefined} | ||
*/ | ||
Index.prototype.forEach = function(callback, scope) { | ||
scope = arguments.length > 1 ? scope : this; | ||
|
||
this.items.forEach(function(value) { | ||
callback.call(scope, value, value); | ||
}); | ||
}; | ||
|
||
|
||
/** | ||
* Index Iterator class. | ||
*/ | ||
function IndexIterator(next) { | ||
this.next = next; | ||
} | ||
|
||
/** | ||
* Method returning an iterator over the index's values. | ||
* | ||
* @return {IndexIterator} | ||
*/ | ||
Index.prototype.values = function() { | ||
var iterator = this.items.values(); | ||
|
||
Object.defineProperty(iterator, 'constructor', { | ||
value: IndexIterator, | ||
enumerable: false | ||
}); | ||
|
||
return iterator; | ||
}; | ||
|
||
/** | ||
* Attaching the #.values method to Symbol.iterator if possible. | ||
*/ | ||
if (typeof Symbol !== 'undefined') | ||
Index.prototype[Symbol.iterator] = Index.prototype.values; | ||
|
||
/** | ||
* Convenience known method. | ||
*/ | ||
Index.prototype.inspect = function() { | ||
var array = Array.from(this.items.values()); | ||
|
||
Object.defineProperty(array, 'constructor', { | ||
value: Index, | ||
enumerable: false | ||
}); | ||
|
||
return array; | ||
}; | ||
|
||
/** | ||
* Static @.from function taking an abitrary iterable & converting it into | ||
* a structure. | ||
* | ||
* @param {Iterable} iterable - Target iterable. | ||
* @param {array|function} descriptor - Hash functions descriptor. | ||
* @param {boolean} useSet - Whether to use #.set or #.add | ||
* @return {Index} | ||
*/ | ||
Index.from = function(iterable, descriptor, useSet) { | ||
var index = new Index(descriptor); | ||
|
||
iterateOver(iterable, function(value, key) { | ||
if (useSet) | ||
index.set(key, value); | ||
else | ||
index.add(value); | ||
}); | ||
|
||
return index; | ||
}; | ||
|
||
/** | ||
* Exporting. | ||
*/ | ||
module.exports = Index; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
/* eslint no-new: 0 */ | ||
/** | ||
* Mnemonist Index Unit Tests | ||
* =========================== | ||
*/ | ||
var assert = require('assert'), | ||
Index = require('../index.js'); | ||
|
||
describe('Index', function() { | ||
|
||
it('should throw if given invalid hash functions.', function() { | ||
|
||
assert.throws(function() { | ||
new Index({hello: 'world'}); | ||
}, /hash/); | ||
|
||
assert.throws(function() { | ||
new Index([{hello: 'world'}]); | ||
}, /hash/); | ||
|
||
assert.throws(function() { | ||
new Index([null, {hello: 'world'}]); | ||
}, /hash/); | ||
}); | ||
|
||
it('should be possible to add items to the index.', function() { | ||
var index = new Index(function(item) { | ||
return item.title.toLowerCase(); | ||
}); | ||
|
||
index.add({title: 'Hello'}); | ||
index.add({title: 'World'}); | ||
|
||
assert.strictEqual(index.size, 2); | ||
}); | ||
|
||
it('should be possible to set values.', function() { | ||
var index = new Index(function(item) { | ||
return item.toLowerCase(); | ||
}); | ||
|
||
index.set('Hello', {title: 'Hello'}); | ||
index.set('World', {title: 'World'}); | ||
|
||
assert.strictEqual(index.size, 2); | ||
}); | ||
|
||
it('should be possible to clear the index.', function() { | ||
var index = new Index(function(item) { | ||
return item.title.toLowerCase(); | ||
}); | ||
|
||
index.add({title: 'Hello'}); | ||
index.add({title: 'World'}); | ||
|
||
index.clear(); | ||
|
||
assert.strictEqual(index.size, 0); | ||
}); | ||
|
||
it('should be possible to get items from the index.', function() { | ||
var index = new Index(function(item) { | ||
return item.toLowerCase(); | ||
}); | ||
|
||
index.set('Hello', {title: 'Hello'}); | ||
index.set('World', {title: 'World'}); | ||
|
||
assert.deepEqual(index.get('HELLO'), {title: 'Hello'}); | ||
assert.deepEqual(index.get('shawarama'), undefined); | ||
}); | ||
|
||
it('should be possible to iterate over an index\'s values.', function() { | ||
var index = new Index(function(item) { | ||
return item.toLowerCase(); | ||
}); | ||
|
||
index.set('Hello', {title: 'Hello'}); | ||
index.set('World', {title: 'World'}); | ||
|
||
var i = 0; | ||
|
||
index.forEach(function(value) { | ||
assert.deepEqual(value, !i ? {title: 'Hello'} : {title: 'World'}); | ||
i++; | ||
}); | ||
|
||
assert.strictEqual(i, 2); | ||
}); | ||
|
||
it('should be possible to create an iterator over an index\'s values.', function() { | ||
var index = new Index(function(item) { | ||
return item.toLowerCase(); | ||
}); | ||
|
||
index.set('Hello', {title: 'Hello'}); | ||
index.set('World', {title: 'World'}); | ||
|
||
var iterator = index.values(); | ||
|
||
assert.deepEqual(iterator.next().value, {title: 'Hello'}); | ||
assert.deepEqual(iterator.next().value, {title: 'World'}); | ||
assert.strictEqual(iterator.next().done, true); | ||
}); | ||
|
||
it('should be possible to iterate over an index\'s values using for...of.', function() { | ||
var index = new Index(function(item) { | ||
return item.toLowerCase(); | ||
}); | ||
|
||
index.set('Hello', {title: 'Hello'}); | ||
index.set('World', {title: 'World'}); | ||
|
||
var i = 0; | ||
|
||
for (var value of index) { | ||
assert.deepEqual(value, !i ? {title: 'Hello'} : {title: 'World'}); | ||
i++; | ||
} | ||
|
||
assert.strictEqual(i, 2); | ||
}); | ||
|
||
it('should be possible to create an index from arbitrary iterables.', function() { | ||
function writeHash(item) { | ||
return item.title.toLowerCase(); | ||
} | ||
|
||
function readHash(query) { | ||
return query.toLowerCase(); | ||
} | ||
|
||
var index = Index.from([{title: 'Hello'}, {title: 'World'}], [writeHash, readHash]); | ||
|
||
assert.strictEqual(index.size, 2); | ||
assert.deepEqual(index.get('hellO'), {title: 'Hello'}); | ||
|
||
var map = new Map([ | ||
['Hello', {title: 'Hello'}], | ||
['World', {title: 'World'}] | ||
]); | ||
|
||
index = Index.from(map, readHash, true); | ||
|
||
assert.strictEqual(index.size, 2); | ||
assert.deepEqual(index.get('WOrlD'), {title: 'World'}); | ||
}); | ||
}); |