Mnemonist

Fuzzy MultiMap


The FuzzyMultiMap is basically a FuzzyMap that accepts more than one value per key and stores the colliding items in buckets.

const FuzzyMultiMap = require('mnemonist/fuzzy-multi-map');

Use case

Let’s say we need to store items in an index using a fuzzy key so that we may match them against not so precise queries.

Furthermore, since the key produced might be the same for several items and because we want to keep multiple values for each key, we could use a FuzzyMultiMap to help us in this task.

// Using lodash to create a naive fuzzy key using the given title
function fuzzyTitle(title) {
  return _.words(title.toLowerCase()).sort().join(' ');
}

// Creating our index
const map = new FuzzyMultiMap(fuzzyTitle);

// Adding some universities
const universities = [
  {name: 'University of Carolina'},
  {name: 'Carolina, university of.'},
  {name: 'Harvard university'}
];

universities.forEach((university) => {
  map.set(university.name, university)
});

// Now we can query the index
universities.get('university of carolina');
>>> [
  {name: 'University of Carolina'},
  {name: 'Carolina, university of.'}
]

Constructor

The FuzzyMultiMap 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.

As with the MultiMap, the FuzzyMultiMap can also take a container as second argument.

Example with one hash function

// Let's create an index using a single hash function:
const map = new FuzzyMultiMap((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 index using two different hash functions:
const map = new FuzzyMultiMap([
  
  // 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);

Example with Set containers

const map = new FuzzyMultiMap((value) => value.toUpperCase(), Set);

Warning!: the index will not consider any falsy key processed by its hash functions.

const map = new FuzzyMultiMap((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 FuzzyMultiMap from an arbitrary JavaScript iterable likewise:

const map = FuzzyMultiMap.from(list, hashFunctions);
const map = FuzzyMultiMap.from(list, hashFunctions, Container);

Members

Methods

Mutation

Read

Iteration

#.dimension

Number of item containers stored in the map.

const map = new FuzzyMultiMap();
map.set('hello', 3);
map.set('hello', 4);

map.size
>>> 2
map.dimension
>>> 1

#.size

Number of items stored in the map.

const map = new FuzzyMultiMap();
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 FuzzyMultiMap();

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 FuzzyMultiMap();
const movie = {title: 'Great movie', year: 1999};

map.set(movie.title, movie);

#.clear

Completely clears the map of its items.

const map = new FuzzyMultiMap();
map.add(item);
map.clear();

map.size
>>> 0

#.get

Hash the given key using the relevant function then returns the set of items stored by this key.

O(1)

const map = new FuzzyMultiMap((string) => string.toLowerCase());
map.set('John', {name: 'John', surname: 'Williams'});
map.set('John', {name: 'John', surname: 'Ableton'});

map.get('john');
>>> [
  {name: 'John', surname: 'Williams'},
  {name: 'John', surname: 'Ableton'}
]

#.has

Test whether the provided key, processed by the relevant hash function, would return a container.

O(1)

const map = new FuzzyMultiMap((string) => string.toLowerCase());
map.set('John', {name: 'John', surname: 'Williams'});
map.set('John', {name: 'John', surname: 'Ableton'});

map.get('john');
>>> true

#.forEach

Iterates over the values stored in the map.

const map = new FuzzyMultiMap((string) => string.toLowerCase());

map.set('John', {name: 'John', surname: 'Williams'});
map.set('John', {name: 'John', surname: 'Ableton'});

map.forEach((value) => {
  console.log(value);
});
>>> {name: 'John', surname: 'Williams'}
>>> {name: 'John', surname: 'Ableton'}

#.values

Creates an iterator over the map’s values.

const map = new FuzzyMultiMap((string) => string.toLowerCase());

map.set('John', {name: 'John', surname: 'Williams'});
map.set('John', {name: 'John', surname: 'Ableton'});

const iterator = map.values();
iterator.next().value();
>>> {name: 'John', surname: 'Williams'}

Iterable

Alternatively, you can iterate over an map’ values using ES2015 for...of protocol:

const map = new FuzzyMultiMap((string) => string.toLowerCase());

map.set('John', {name: 'John', surname: 'Williams'});
map.set('John', {name: 'John', surname: 'Ableton'});

for (const  value of map) {
  console.log(value);
}