Implement a Map with multiple keys in JavaScript
This post will discuss how to implement a Map with multiple keys in JavaScript.
There are several ways to implement a Map with multiple keys in JavaScript, which is a data structure that allows us to map multiple keys to a single value. Here are some possible functions:
1. Using a nested map
One option to implement a map with multiple keys is to use a nested map object, where each level of the map represents a key and the last level of the map contains the value. This creates a map of maps, where each key is mapped to another map that contains the next key and the value. This method works well for a fixed number of keys, but it requires a lot of nesting and accessing. The following code illustrates this:
1 2 3 4 5 6 7 8 9 10 |
// Create a multikeymap with three keys const multikeymap = new Map(); // Set a value with three keys multikeymap.set('a', new Map()); multikeymap.get('a').set('b', new Map()); multikeymap.get('a').get('b').set('c', 'value'); // Get a value with three keys console.log(multikeymap.get('a').get('b').get('c')); // 'value' |
2. Using an array as a key
Another option is to create an array of keys and uses it as a single key for the map. This method works well for any number of keys, but it requires a custom hash function to use arrays as keys in a Map in JavaScript. The following code illustrates this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// Define a custom hash function for arrays const hash = array => array.join(','); // Set a value with an array of keys const keys = ['a', 'b', 'c']; // Create a multikeymap with an array as a key const multikeymap = new Map(); multikeymap.set(hash(keys), 'value'); // Map(1) { 'a,b,c' => 'value' } console.log(multikeymap); // Get a value with an array of keys console.log(multikeymap.get(hash(keys))); // 'value' // Check if the map has an array of keys console.log(multikeymap.has(hash(keys))); // true // Delete an array of keys from the map multikeymap.delete(hash(keys)); console.log(multikeymap.has(hash(keys))); // false |
3. Using a third-party library
A better option is to use an existing library that provides a multikeymap implementation for JavaScript. For example, we can use the multikeymap-js library, which allows us to create and manipulate multikeymaps with various functions and options. Here, an entry is keyed by an array of values. This library does not place restrictions on the order of keys to set and get. The following code example demonstrates its usage:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// Import the library const MultiKeyMap = require('multikeymap'); // Create a multikeymap with any number of keys const multikeymap = new MultiKeyMap(); // Set a value with three keys multikeymap.set(['a', 'b', 'c'], 'value'); // Get a value with three keys console.log(multikeymap.get(['a', 'b', 'c'])); // 'value' // Check if the map has three keys console.log(multikeymap.has(['a', 'b', 'c'])); // true console.log(multikeymap.has(['b', 'a', 'c'])); // true console.log(multikeymap.has(['a', 'c'])); // false // Delete three keys from the map multikeymap.delete(['a', 'b', 'c']); console.log(multikeymap.has(['a', 'b', 'c'])); // false |
4. Extending Map
class
This function creates a custom class that inherits from the built-in Map
class and overrides some of its functions to support multiple keys. The custom class can use an internal Map
object to store the key-value pairs, and use a hashing function to generate a unique hash for each combination of keys. The hashing function can be any function that returns a string or a number that is unique for each set of keys, regardless of their order. The custom class can then use the hashing function to create and retrieve the key-value pairs from the internal Map
object. For example, one possible implementation of the custom class could be:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
// A hashing function that joins the keys with a separator and sorts them alphabetically function hashKeys(...keys) { return keys.sort().join("|"); } // A custom class that extends the Map class and supports multiple keys class MultiKeyMap extends Map { constructor(iterable) { // call the super constructor super(); // create an internal Map object this._map = new Map(); // if an iterable is provided, initialize the map with it if (iterable) { for (let [keys, value] of iterable) { // use the spread operator to pass the keys and value this.set(...keys, value); } } } // Override the set function to support multiple keys set(...keysAndValue) { // get the last argument as the value let value = keysAndValue.pop(); // get the hash for the remaining arguments as the keys let hash = hashKeys(...keysAndValue); // set the key-value pair in the internal map this._map.set(hash, value); // return the map object to allow chaining return this; } // Override the get function to support multiple keys get(...keys) { // get the hash for the keys let hash = hashKeys(...keys); // get the value from the internal map return this._map.get(hash); } // Override the has function to support multiple keys has(...keys) { // get the hash for the keys let hash = hashKeys(...keys); // check if the internal map has the key return this._map.has(hash); } // Override the delete function to support multiple keys delete(...keys) { // get the hash for the keys let hash = hashKeys(...keys); // delete the key-value pair from the internal map return this._map.delete(hash); } // Override the clear function to clear the internal map clear() { // clear the internal map this._map.clear(); } // Override the size getter to return the size of the internal map get size() { // return the size of the internal map return this._map.size; } // Override the entries function to return an iterator of [keys, value] pairs *entries() { // iterate over the internal map entries for (let [hash, value] of this._map.entries()) { // split the hash by the separator to get an array of keys let keys = hash.split("|"); // yield an array of [keys, value] yield [keys, value]; } } // Override the keys function to return an iterator of keys *keys() { // iterate over the internal map entries for (let [hash, value] of this._map.entries()) { // split the hash by the separator to get an array of keys let keys = hash.split("|"); // yield an array of keys yield keys; } } // Override the values function to return an iterator of values values() { // return the values iterator of the internal map return this._map.values(); } // Override the forEach function to execute a callback for each [keys, value] pair forEach(callback, thisArg) { // iterate over the entries for (let [keys, value] of this.entries()) { // call the callback with the value, keys, and map object callback.call(thisArg, value, keys, this); } } } // A map with multiple keys let personMap = new MultiKeyMap(); personMap.set("Anne", "Gregory", {name: "Anne Gregory", age: 30}); personMap.set("Harry", "Brook", {name: "Harry Brook", age: 24}); // Access the value by any combination of keys // {name: "Anne Gregory", age: 30} console.log(personMap.get("Anne", "Gregory")); // {name: "Anne Gregory", age: 30} console.log(personMap.get("Gregory", "Anne")); // {name: "Harry Brook", age: 24} console.log(personMap.get("Harry", "Brook")); // {name: "Harry Brook", age: 24} console.log(personMap.get("Brook", "Harry")); |
That’s all about implementing a Map with multiple keys in JavaScript.
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)