Reputation: 1321
I have 3 dropdowns on my page, the options in the 3rd dropdown depend on the choices in the first two dropdowns.
So I am wondering if there is a way to implement a map in javaScript with a 2-dimensional key? Like <Key1, Key2> -> Value
.
I think an easy way is to concatenate two keys into one string. Is there any way that is more decent?
Thanks.
Upvotes: 24
Views: 37298
Reputation: 629
JS offers no way to do this out of the box. Objects or ES6 Maps with concatenated string keys are brittle and may fail depending on your delimiter.
Maps allow non-strings/symbols as keys. But array keys will not work:
Your safest bet is a nested map like Map<K1, Map<K2, V>>
, used like below:
const map = new Map();
function set(key1, key2, val) {
if (!map.has(key1)) {
map.set(key1, new Map());
}
map.get(key1).set(key2, val);
}
function get(key1, key2) {
return map.get(key1)?.get(key2);
}
Upvotes: 0
Reputation: 1993
A Map with two keys could be thought of as a graph, where the two keys are nodes with an edge between them.
class Graph {
nodes = new Map();
addNode(ref) {
if (!this.nodes.has(ref)) {
this.nodes.set(ref, new Map());
}
}
addEdge(ref1, ref2, value) {
this.nodes.get(ref1)?.set(ref2, value);
this.nodes.get(ref2)?.set(ref1, value);
}
getEdge(ref1, ref2) {
return this.nodes.get(ref1)?.get(ref2);
}
}
const graph = new Graph();
graph.addNode('left');
graph.addNode('right');
graph.addEdge('left', 'right', 12345);
console.log(graph.getEdge('left', 'right')); // 12345
console.log(graph.getEdge('right', 'left')); // 12345
You could even make it simpler by creating non-existent nodes in the addEdge
method itself.
Upvotes: -1
Reputation: 1100
You can achieve this by adding the two keys as a key array on a hash object using ES6 syntax. Then you can search using array filter and regular expression:
const key1 = "key1";
const key2 = "key2";
const value = "whatever";
const hashObject = {};
const firstEntry = [key1, key2];
const secondEntry = [key2, key1];
const entryValue = { value };
hashObject[firstEntry] = entryValue;
hashObject[secondEntry] = entryValue;
const regByFirstKey = new RegExp(`${key1},.`);
const regBySecondKey = new RegExp(`.,${key2}`);
const keysArray = Object.keys(hashObject);
const resultByFirstKey = (keysArray.filter((key) => regByFirstKey.test(key)))[0];
const resultBySecondKey = (keysArray.filter((key) => regBySecondKey.test(key)))[0];
resultByFirstKey ? console.log(hashObject[resultByFirstKey].value) : undefined;
resultBySecondKey ? console.log(hashObject[resultBySecondKey].value) : undefined;
Upvotes: 0
Reputation: 6955
You can set a key as an array. Just create your array [key1, key2];
Then set that value as your key and relate it to your value.
obj[[key1,key2]] = "my value";
Here is a jsFiddle http://jsfiddle.net/TwQLW/
Upvotes: -2
Reputation: 349
I was looking for a similar data structure, but could not find. I have been working with TypeScript, so I have developed a solution using TypeScript.
export class TwoMapKey<T> {
private __map__: object;
constructor() {
this.__map__ = new Object();
this.get = this.get.bind(this);
this.set = this.set.bind(this);
this.remove = this.remove.bind(this);
this.keys = this.keys.bind(this);
this.nestedKeys = this.nestedKeys.bind(this);
this.clear = this.clear.bind(this);
}
public get(key: string, nestedKey: string): T {
if (!this.__map__[key] || this.__map__[key] && !this.__map__[key][nestedKey])
return;
return this.__map__[key][nestedKey];
}
public set(key: string, nestedKey: string, value: T): void {
if (!this.__map__[key]) {
this.__map__[key] = new Object();
}
Object.defineProperty(this.__map__[key], nestedKey, { value: value, configurable: true, enumerable: true });
}
public remove(key, nestedKey): void {
if (!this.__map__[key]) {
return;
}
delete this.__map__[key][nestedKey];
}
public keys(): string[] {
return Object.getOwnPropertyNames(this.__map__);
}
public nestedKeys(): Array<string[]> {
return Object.getOwnPropertyNames(this.__map__).map(key => Object.keys(this.__map__[key]));
}
public clear(): void {
Object.getOwnPropertyNames(this.__map__).forEach(property => {
delete this.__map__[property];
});
} }
You can simply create a map with two keys as below:
let twoKeyMap = new TwoKeyMap<any>();
twoKeyMap.set('mainKey1', 'nestedKey1', {value: 'value'});
Its not as efficent as a HashMap. You should be able to convert the same in ES6 or ES5 easily.
Upvotes: 2
Reputation: 224877
You could have an object that contains more objects:
var options = {
'option 1': {
'option 1.1': [
'option 1.1.1',
'option 1.1.2',
'option 1.1.3',
'option 1.1.4'
],
'option 1.2': [
'option 1.2.1',
/* etc. */
};
Then, you would access the options for the third dropdown as options[firstSelectedValue][secondSelectedValue]
.
EDIT: Here's a demo, too, using some new features that you may need to implement if you're browsing using Internet Explorer 8 or lower :)
Upvotes: 6
Reputation: 16130
There is no other way. JS map is plain object, so the key the same as property name. Even if you try to use eg. array as a key, it'll be converted to string. You should concat this string with some special char between or store values in array, and scan it on every access.
Upvotes: 0
Reputation: 11184
What is wrong with concatenating the two keys? All you need is to be sure, that the concatenated keys are unique, and I guess that this can easily be achieved by separating the two values with a character that is not used by any of the two keys.
Upvotes: 2