Как создать двустороннее сопоставление в JavaScript или какой-либо другой способ поменять значения?

В настоящее время у меня есть необходимость временно менять значения в строке JavaScript, и поэтому мне нужно иметь двунаправленную карту/хэш.

Например, скажем, я хочу изменить \* на __asterisk__ (это просто пример, это не то, что я на самом деле пытаюсь сделать). Мне нужно будет сопоставить * с __asterisk__ (чтобы поменять значение в исходной строке), но тогда мне также нужно будет отобразить __asterisk__ на * (to вернуть исходную строку).

Вот какой-то быстрый псевдоиш-код того, что я ищу, чтобы вы могли понять его лучше:

var myString = 'this is \* a test';

// ???
var twoWayMap = new TwoWayMap('*' <---> '__asterisk__', '%' <---> '__percent__', ...);

var newString = myString.replace(/\\(.)/g, function(m, c) {
    return twoWayMap.getKey(c);
});
// newString is now 'this is __asterisk__ a test'

// ... later in the code ...

var oldString = newString.replace(/__([^_]+)__/g, function(m, c) {
    return twoWayMap.getValue(c);
});
// oldString is now 'this is * a test'

Это то, о чем я думал и пытался до сих пор:

var twoWayMap = {'*': '__asterisk__', '%': '__percent__', ...};

// getKey would be like this:
twoWayMap[c];
// getValue would be like:
var val; for (var x in twoWayMap) { if (twoWayMap[x] === c) { val = x; break } }

Очевидная проблема заключается в том, что способ получить по значению слишком сложный, и я не хочу, чтобы каждый раз записывать все это каждый раз, когда я должен искать обратный поиск.

Я просто хотел знать: есть ли способ решить эту проблему, не прибегая к переходу через объект? Если нет, есть ли способ сделать это проще или чище?

Ответ 1

Используйте два объекта. Один объект содержит отображение * -> _asterisk_, другой объект содержит _asterisk_ -> *.

var forwardMap = {'*': '__asterisk__', '%': '__percent__', ...};
var reverseMap = {};
for (var key in forwardMap) {
    if (forwardMap.hasOwnProperty(key)) {
        reverseMap[forwardMap[key]] = key;
    }
}

Ответ 2

С дополнительным внутренним объектом для обратного отображения. Лучше всего, если мы добавим служебный класс;) вот так:

function TwoWayMap(map) {
   this.map = map;
   this.reverseMap = {};
   for(var key in map) {
      var value = map[key];
      this.reverseMap[value] = key;   
   }
}
TwoWayMap.prototype.get = function(key){ return this.map[key]; };
TwoWayMap.prototype.revGet = function(key){ return this.reverseMap[key]; };

Затем вы создаете экземпляр как это:

var twoWayMap = new TwoWayMap({
   '*' : '__asterisk__', 
    '%' : '__percent__',
   ....
});

Тогда для его использования:

twoWayMap.get('*')   //Returns '__asterisk__'
twoWayMap.revGet('__asterisk__')  //Returns '*'

РЕДАКТИРОВАТЬ: эквивалент с синтаксисом ES6

class TwoWayMap {
    constructor(map) {
       this.map = map;
       this.reverseMap = {};
       for(let key in map) {
          const value = map[key];
          this.reverseMap[value] = key;   
       }
    }
    get(key) { return this.map[key]; }
    revGet(key) { return this.reverseMap[key]; }
}

Использование такое же

Надеюсь это поможет. ура

Ответ 3

Мне нужно что-то подобное, и создал https://www.npmjs.com/package/bi-directional-map

Он реализует 2-стороннюю карту с использованием 2 es6 Map, хотя она не имеет большой функциональности, она протестирована и, поскольку она написана typescript, поставляется с официальными типизациями.

Ответ 4

Я бы просто использовал простой объект:

var map = { '*': '__asterisk__', '__asterisk__': '*', .... }

Если вы не хотите писать все это, посмотрите на реализацию подчеркивания _.invert(object) здесь

Ответ 5

Некоторые могут предпочесть более сжатый функциональный стиль...

const create2WayMap = (seedMap, mapName, reversemapName) => ({
  [mapName]: { ...seedMap },
  [reversemapName]: Object.keys(seedMap).reduce((map, key) => {
    const value = seedMap[key]
    return { ...map, [value]: key }
  }, {})
})

использование:

const myIDMap = create2WayMap(
  {
    1: 'SomeString',
    2: 'Another String',
    3: 'Another'
  },
  'idStrings',
  'idNumbers'
)

let id = 2
const str = myIDMap.idStrings[id] // yields 'Another String'
id = myIDMap.idNumbers[str] // yields 2