nababa
nababa

Reputation: 1321

Javascript map with two values in the key

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

Answers (8)

user3932000
user3932000

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:

  • The default lookup function will compare the array keys by reference, not value.
  • There is no way to specify a custom lookup function.
  • There is no immutable tuple type. (Though there is a proposal to add this)

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

Joe Maffei
Joe Maffei

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

Itay Merchav
Itay Merchav

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

Keith.Abramo
Keith.Abramo

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

Rahul Sethi
Rahul Sethi

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

Ry-
Ry-

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

Krzysztof
Krzysztof

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

Jan Aagaard
Jan Aagaard

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

Related Questions