Skip to content

Commit

Permalink
Adding DefaultMap
Browse files Browse the repository at this point in the history
  • Loading branch information
Yomguithereal committed Sep 28, 2018
1 parent 6af0649 commit f691685
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* Adding `#.forEachMultiplicity` to `MultiSet`.
* Adding `#.forEachAssociation` to `MultiMap`.
* Adding `DefaultMap`.

## 0.23.0

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Full documentation for the library can be found [here](https://yomguithereal.git
**Utility classes**

* [BiMap](https://yomguithereal.github.io/mnemonist/bi-map)
* [DefaultMap](https://yomguithereal.github.io/mnemonist/default-map)
* [IncrementalMap](https://yomguithereal.github.io/mnemonist/incremental-map)

---
Expand Down
25 changes: 25 additions & 0 deletions default-map.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Mnemonist DefaultMap Typings
* =============================
*/
export default class DefaultMap<K, V> implements Iterable<[K, V]> {

// Members
size: number;

// Constructor
constructor(factory: (key?: K, index?: number) => V);

// Methods
clear(): void;
set(key: K, value: V): this;
delete(key: K): boolean;
has(key: K): boolean;
get(key: K): V | undefined;
forEach(callback: (value: V, key: K, map: this) => void, scope?: any): void;
keys(): Iterator<K>;
values(): Iterator<V>;
entries(): Iterator<[K, V]>;
[Symbol.iterator](): Iterator<[K, V]>;
inspect(): any;
}
137 changes: 137 additions & 0 deletions default-map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/**
* Mnemonist DefaultMap
* =====================
*
* JavaScript implementation of a default map that will return a constructed
* value any time one tries to access an inexisting key. It's quite similar
* to python's defaultdict.
*/

/**
* DefaultMap.
*
* @constructor
*/
function DefaultMap(factory) {
if (typeof factory !== 'function')
throw new Error('mnemonist/FuzzyMultiMap.constructor: expecting a function.');

this.items = new Map();
this.factory = factory;
this.size = 0;
}

/**
* Method used to clear the structure.
*
* @return {undefined}
*/
DefaultMap.prototype.clear = function() {

// Properties
this.items.clear();
this.size = 0;
};

/**
* Method used to get the value set for given key. If the key does not exist,
* the value will be set.
*
* @param {any} key - Target key.
* @return {any}
*/
DefaultMap.prototype.get = function(key) {
var value = this.items.get(key);

if (typeof value === 'undefined') {
value = this.factory(key, this.size);
this.items.set(key, value);
this.size++;
}

return value;
};

/**
* Method used to set a value for given key.
*
* @param {any} key - Target key.
* @param {any} value - Value.
* @return {DefaultMap}
*/
DefaultMap.prototype.set = function(key, value) {
this.items.set(key, value);
this.size = this.items.size;

return this;
};

/**
* Method used to test the existence of a key in the map.
*
* @param {any} key - Target key.
* @return {boolean}
*/
DefaultMap.prototype.has = function(key) {
return this.items.has(key);
};

/**
* Method used to delete target key.
*
* @param {any} key - Target key.
* @return {boolean}
*/
DefaultMap.prototype.delete = function(key) {
var deleted = this.items.delete(key);

this.size = this.items.size;

return deleted;
};

/**
* Method used to iterate over each of the key/value pairs.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
DefaultMap.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;

this.items.forEach(callback, scope);
};

/**
* Iterators.
*/
DefaultMap.prototype.entries = function() {
return this.items.entries();
};

DefaultMap.prototype.keys = function() {
return this.items.keys();
};

DefaultMap.prototype.values = function() {
return this.items.values();
};

/**
* Attaching the #.entries method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
DefaultMap.prototype[Symbol.iterator] = DefaultMap.prototype.entries;

/**
* Convenience known methods.
*/
DefaultMap.prototype.inspect = function() {
return this.items;
};

/**
* Exporting.
*/
module.exports = DefaultMap;
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export {default as BitVector} from './bit-vector';
export {default as BKTree} from './bk-tree';
export {default as BloomFilter} from './bloom-filter';
export {default as CircularBuffer} from './circular-buffer';
export {default as DefaultMap} from './default-map';
export {default as FibonacciHeap, MinFibonacciHeap, MaxFibonacciHeap} from './fibonacci-heap';
export {default as FixedReverseHeap} from './fixed-reverse-heap';
export {default as FixedStack} from './fixed-stack';
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module.exports = {
BloomFilter: require('./bloom-filter.js'),
BKTree: require('./bk-tree.js'),
CircularBuffer: require('./circular-buffer.js'),
DefaultMap: require('./default-map.js'),
StaticDisjointSet: require('./static-disjoint-set.js'),
FibonacciHeap: FibonacciHeap,
MinFibonacciHeap: FibonacciHeap.MinFibonacciHeap,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"circular buffer",
"counter",
"data structures",
"default map",
"disjoint set",
"fibonacci heap",
"fuzzy map",
Expand Down
92 changes: 92 additions & 0 deletions test/default-map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* Mnemonist DefaultMap Unit Tests
* ==============================
*/
var assert = require('assert'),
DefaultMap = require('../default-map.js'),
take = require('obliterator/take');

var FACTORY = function() {
return [];
};

describe('DefaultMap', function() {

it('should throw if passed factory is not a function.', function() {

assert.throws(function() {
new DefaultMap(null);
}, /function/);
});

it('should be possible to set & get keys.', function() {
var map = new DefaultMap(FACTORY);

map.get('one').push(1);
map.set('two', [2]);

assert.deepEqual(map.get('one'), [1]);
assert.deepEqual(map.get('two'), [2]);

assert.strictEqual(map.size, 2);

assert.deepEqual(map.get('unknown'), []);

assert.strictEqual(map.size, 3);

map.clear();

assert.strictEqual(map.size, 0);
assert.deepEqual(map.get('one'), []);
});

it('should be possible to delete keys.', function() {
var map = new DefaultMap(FACTORY);

map.set('one', 1);

assert.strictEqual(map.has('one'), true);
assert.strictEqual(map.delete('one'), true);
assert.strictEqual(map.size, 0);
assert.strictEqual(map.has('one'), false);
assert.strictEqual(map.delete('one'), false);
});

it('should be possible to iterate over the map\'s items.', function() {
var map = new DefaultMap(FACTORY);

map.get('one').push(1);
map.get('two').push(2);

var items = [];

map.forEach(function(list, key) {
items.push([key, list]);
});

assert.deepEqual(items, [
['one', [1]],
['two', [2]]
]);
});

it('should be possible to create iterators.', function() {
var map = new DefaultMap(FACTORY);

map.get('one').push(1);
map.get('two').push(2);

var entries = [
['one', [1]],
['two', [2]]
];

assert.deepEqual(take(map.entries()), entries);
assert.deepEqual(take(map.keys()), entries.map(function(e) {
return e[0];
}));
assert.deepEqual(take(map.values()), entries.map(function(e) {
return e[1];
}));
});
});
7 changes: 7 additions & 0 deletions test/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
BKTree,
BloomFilter,
CircularBuffer,
DefaultMap,
FibonacciHeap, MinFibonacciHeap, MaxFibonacciHeap,
FixedReverseHeap,
FixedStack,
Expand Down Expand Up @@ -74,6 +75,12 @@ bloomfilter.add('hello');
let circularbuffer: CircularBuffer<string> = new CircularBuffer(Array, 4);
circularbuffer.push('test');

/**
* DefaultMap.
*/
let defaultMap: DefaultMap<string, Array<number>> = new DefaultMap(() => []);
defaultMap.get('one').push(1);

/**
* FibonacciHeap.
*/
Expand Down

0 comments on commit f691685

Please sign in to comment.