Skip to content

Commit

Permalink
Adding Index
Browse files Browse the repository at this point in the history
  • Loading branch information
Yomguithereal committed Jan 23, 2017
1 parent 9a43b8d commit 9a36119
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 0.10.0

* Adding `Index`.
* Adding `MultiMap`.
* Adding `MultiSet`.
* Adding `SymSpell`.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Full documentation for the library can be found [here](https://yomguithereal.git
* [Bloom Filter](https://yomguithereal.github.io/mnemonist/bloom-filter)
* [Fibonacci Heap](https://yomguithereal.github.io/mnemonist/fibonacci-heap)
* [Heap](https://yomguithereal.github.io/mnemonist/heap)
* [Index](https://yomguithereal.github.io/mnemonist/index-structure)
* [Linked List](https://yomguithereal.github.io/mnemonist/linked-list)
* [MultiMap](https://yomguithereal.github.io/mnemonist/multi-map)
* [MultiSet](https://yomguithereal.github.io/mnemonist/multi-set)
Expand Down
3 changes: 2 additions & 1 deletion endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ module.exports = {
Heap: Heap,
MinHeap: Heap.MinHeap,
MaxHeap: Heap.MaxHeap,
Index: require('./index.js'),
LinkedList: require('./linked-list.js'),
MultiMap: require('./multi-map.js'),
MultiSet: require('./multi-set.js'),
LinkedList: require('./linked-list.js'),
Queue: require('./queue.js'),
Stack: require('./stack.js'),
SuffixArray: SuffixArray,
Expand Down
185 changes: 185 additions & 0 deletions index.js
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;
148 changes: 148 additions & 0 deletions test/index.js
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'});
});
});

0 comments on commit 9a36119

Please sign in to comment.