如何在 JavaScript 中创建双向映射,或以其他方式交换值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21070836/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 19:58:46  来源:igfitidea点击:

How can I create a two-way mapping in JavaScript, or some other way to swap out values?

javascriptdata-structuresmap

提问by Doorknob

I currently have a need to temporarily swap out values in a JavaScript string, and therefore I will need to have a two-way map/hash thing.

我目前需要临时交换 JavaScript 字符串中的值,因此我需要有一个双向映射/哈希的东西。

For example, let's say I want to change \*to __asterisk__(this is just an example, it's not what I'm actually trying to do). I'll have to be able to map *to __asterisk__(to swap out the value in the original string), but then I'll also have to be able to map __asterisk__back to *(to get the original string back).

例如,假设我想更改\*__asterisk__(这只是一个示例,这不是我真正想要做的)。我必须能够映射*__asterisk__(以换出原始字符串中的值),但是我还必须能够映射__asterisk__*(以取回原始字符串)。

Here's some quick pseudo-ish code of the kind of thing that I'm looking for, so you can understand it better:

这是我正在寻找的那种东西的一些快速伪代码,所以你可以更好地理解它:

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'

This is what I've thought about and tried so far:

这是我迄今为止所想和尝试过的:

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 } }

The obvious problem with this is that the way to get by value is much too complicated, and I don't want to have to write out the whole thing every single time I have to reverse lookup.

这样做的明显问题是按值获取的方法太复杂了,我不想每次必须反向查找时都必须写出整个内容。

I just wanted to know: Is there any way to solve this problem without resorting to looping through an object? If not, is there any way to make it easier or cleaner?

我只是想知道:有没有办法解决这个问题而不诉诸循环遍历一个对象?如果没有,有没有办法让它更容易或更干净?

采纳答案by Barmar

Use two objects. One object contains the * -> _asterisk_mapping, the other object contains _asterisk_ -> *.

使用两个对象。一个对象包含* -> _asterisk_映射,另一个对象包含_asterisk_ -> *

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

回答by Edgar Villegas Alvarado

With an extra internal object for reverse mapping. Best if we add a utility class ;) like this:

带有用于反向映射的额外内部对象。最好我们添加一个实用程序类 ;) 像这样:

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]; };

Then you instantiate like this:

然后你像这样实例化:

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

Then, for using it:

然后,使用它:

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

EDIT: Equivalent with ES6 syntax

编辑:等效于 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]; }
}

Usage is the same

用法是一样的

Hope this helps. Cheers

希望这可以帮助。干杯

回答by sparrow

I'd just use a plain object:

我只是使用一个普通的对象:

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

If you don't want to have to write all those out, take a look at the implementation of underscore's _.invert(object)here

如果您不想将所有这些都写出来,请在此处查看下划线的实现_.invert(object)

回答by harunurhan

I needed something similar, and created https://www.npmjs.com/package/bi-directional-map

我需要类似的东西,并创建了https://www.npmjs.com/package/bi-directional-map

It is implementing a 2-way map using 2 es6 Map, even though it doesn't have a much functionality, it is tested and since it's written typescriptcomes with official typings.

它正在使用 2 es6 Map 实现一个 2-way map,尽管它没有太多功能,但它已经过测试,并且因为它typescript是用官方类型编写的。

回答by AndyL

Some might prefer a more succinct functional style...

有些人可能更喜欢更简洁的功能风格......

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

usage:

用法:

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

回答by App-Devon

similar to @harunurhan's answer (but much less verbose) I created a small class that takes in a Typescript Map and generates a readonly two-way map:

类似于@harunurhan 的回答(但不那么冗长)我创建了一个小类,它接受一个 Typescript Map 并生成一个只读的双向映射:

export class TwoWayReadonlyMap<T, K> {
  map: Map<T, K>;
  reverseMap: Map<K, T>;
  constructor(map: Map<T, K>) {
    this.map = map;
    this.reverseMap = new Map<K, T>();
    map.forEach((value, key) => {
      this.reverseMap.set(value, key);
    });
  }
  get(key: T) {
    return this.map.get(key);
  }
  revGet(key: K) {
    return this.reverseMap.get(key);
  }
}