Mnemonist
Fuzzy Map
The FuzzyMap
is a map whoses keys are transformed by a function before any read or write operation. This can often result in multiple keys being able to access the same resource.
const FuzzyMap = require('mnemonist/fuzzy-map');
Use case
Let’s say we want to store & query objects representing movies based on their lowercased title. One approach would be to create a JavaScript object & lowercase the correct movies’ property to use as key:
const movie = {
name: 'Great Movie',
year: 1999
};
const object = {};
// Let's insert our movie using the lowercased title as key
const key = movie.name.toLowerCase();
object[key] = movie;
// Now, to query:
const query = 'great MoVie';
console.log(object[query.toLowerCase()]);
That’s it, you have understood the FuzzyMap
. It’s just some sugar over a JavaScript Map
that will transform the given keys using a custom function to perform this kind of operations easily.
const map = new FuzzyMap([
// Hash function on add
(movie) => movie.title.toLowerCase(),
// Hash function on query
(query) => query.toLowerCase(),
]);
// FuzzyMaping several movies
map.add({title: 'Great movie', year: 1999});
map.add({title: 'Bad movie', year: 2001});
// Querying the index
map.get('bad Movie');
>>> {title: 'Bad movie', year: 2001}
map.get('boring movie');
>>> undefined
Note that if you need to store multiple values under a same key, you should probably check out the FuzzyMultiMap
instead.
Constructor
The FuzzyMap
either takes a single argument being a hash function that will process both inserted items or keys & the queries; or a tuple containing two hash functions, one for the inserted items or keys and the second one for the queries.
Example with one hash function
// Let's create an map using a single hash function:
const map = new FuzzyMap((value) => value.toUpperCase());
// Then you'll probably use #.set to insert items
map.set(movie.title, movie);
map.get(queryTitle);
Example with two hash functions
// Let's create an map using two different hash functions:
const map = new FuzzyMap([
// Hash function for inserted items:
movie => movie.title.toLowerCase(),
// Hash function for queries
(query) => query.toLowerCase(),
]);
// Then you'll probably use #.add to insert items
map.add(movie);
map.get(queryTitle);
Warning!: the map will not consider any falsy key processed by its hash functions.
const map = new FuzzyMap((item) => item.title && item.title.toLowerCase());
const movie = {year: 1999};
// This will not be indexed on `undefined`
map.set(movie.title, movie);
Static #.from
Alternatively, one can build a FuzzyMap
from an arbitrary JavaScript iterable likewise:
const map = FuzzyMap.from(list, hashFunction [, useSet=false]);
const map = FuzzyMap.from(list, hashFunctions [, useSet=false]);
Members
Methods
Mutation
Read
Iteration
#.size
Number of items stored in the map.
const map = new FuzzyMap();
map.add({title: 'Hello World!'});
map.size
>>> 1
#.add
Computes the item’s key by hashing the given item using the relevant function then adds the item to the map using the key.
O(1)
const map = new FuzzyMap();
map.add({title: 'Great movie', year: 1999});
// In fact, same as doing
const movie = {title: 'Great movie', year: 1999};
map.set(movie, movie);
#.set
Adds an item to the map using the provided key that will be processed by the relevant hash function.
O(1)
const map = new FuzzyMap();
const movie = {title: 'Great movie', year: 1999};
map.set(movie.title, movie);
#.clear
Completely clears the map of its items.
const map = new FuzzyMap();
map.add(item);
map.clear();
map.size
>>> 0
#.get
Retrieves an item in the map using the provided key that will be processed by the relevant hash function.
O(1)
const map = new FuzzyMap((string) => string.toLowerCase());
map.set('hello world', {name: 'hello world'});
map.get('Hello World');
>>> {name: 'hello world'}
#.has
Test whether the provided key, processed by the relevant hash function, would return an item.
O(1)
const map = new FuzzyMap((string) => string.toLowerCase());
map.set('hello world', {name: 'hello world'});
map.has('Hello World');
>>> true
#.forEach
Iterates over the values stored in the map.
const map = new FuzzyMap((string) => string.toLowerCase());
map.set('Hello', {name: 'hello'});
map.set('World', {name: 'world'});
map.forEach((value) => {
console.log(value)
});
>>> {name: 'hello'}
>>> {name: 'world'}
#.values
Creates an iterator over the map’s values.
const map = new FuzzyMap((string) => string.toLowerCase());
map.set('Hello', {name: 'hello'});
map.set('World', {name: 'world'});
const iterator = map.values();
iterator.next().value();
>>> {name: 'hello'}
Iterable
Alternatively, you can iterate over an map’ values using ES2015 for...of
protocol:
const map = new FuzzyMap((string) => string.toLowerCase());
map.set('Hello', {name: 'hello'});
map.set('World', {name: 'world'});
for (const value of map) {
console.log(value);
}