【问题标题】:How can I get a key in a JavaScript 'Map' by its value?如何通过值获取 JavaScript 'Map' 中的键?
【发布时间】:2018-04-18 12:57:59
【问题描述】:

我有一个像这样的 JavaScript 'Map'

let people = new Map();
people.set('1', 'jhon');
people.set('2', 'jasmein');
people.set('3', 'abdo');

我想要一些方法通过它的值返回一个键。

let jhonKey = people.getKey('jhon'); // jhonKey should be '1'

【问题讨论】:

  • 那你为什么用错误的方式存储它呢? :-P
  • 为什么需要对需求提出质疑?

标签: javascript dictionary


【解决方案1】:

这是一个正确键入的 Typescript 解决方案,它不会不必要地创建数组。

function find_map_value<K, V>(m: Map<K, V>, predicate: (v: V) => boolean): [K, V] | undefined {
  for (const [k, v] of m) {
    if (predicate(v)) {
        return [k, v];
    }
  }
  return undefined;
}

如果你想要所有值,你可以使用生成器:

function* find_all_map_values<K, V>(m: Map<K, V>, predicate: (v: V) => boolean): Generator<[K, V]> {
  for (const [k, v] of m) {
    if (predicate(v)) {
        yield [k, v];
    }
  }
}

【讨论】:

  • 这只会返回 first 匹配的条目。如果多个键映射到相同的目标值,则其他匹配的键将被忽略。
  • 是的,没错。如果你想要它们,只需将 return 更改为 yield 并适当地更新返回类型。
【解决方案2】:

为什么不简单地利用地图内置的iterator prototype/instance reference 寻找目标值呢?注入原型链/polyfill 启发的各种解决方案使其对代码通用:

Map.prototype.getKey = function(targetValue){
  let iterator = this[Symbol.iterator]()
  for (const [key, value] of iterator) {
    if(value === targetValue)
      return key;
  }
}

const people = new Map();
people.set('1', 'jhon');
people.set('2', 'jasmein');
people.set('3', 'abdo');

const jhonKey = people.getKey('jhon');
console.log(`The key for 'jhon' is: ${jhonKey}`);

对于任何好奇我为什么添加另一个答案的人。这些答案中的大多数(例外,我喜欢Rajesh's answer,但我添加到原型链中)以使用展开运算符甚至直接制作数组的名义进行大量数据重复。 Object.keys() 请注意,您的表现也非常糟糕。

注意,我使用 for..of 迭代可迭代对象。如果需要,可以简单地使用for(const [key, value] of this){...} 进行速记。

【讨论】:

  • 在 forEach 迭代之外有结果变量对我来说感觉不太好 - 结果应该是 const,找到的关键是 const。 @Timmmm 的回答似乎是一个更好的版本。
  • @oldwizard -- 我根据您的反馈进行了更新,请注意您引用的答案是 typescript,并且该问题已标记为 javasript。
【解决方案3】:

JS:

// Returns keys for all instances
function findAll(obj) {
  return Array.from(items.keys()).map(k => items.get(k) === obj ? k : undefined).filter(k => k);
}

// Returns keys for the first instances
function findFirst(obj) {
  return Array.from(items.keys()).find(k => items.get(k) === obj);
}

打字稿:

protected items = new Map<TKey, TObject>();

public findAll(obj: TObject): Array<TKey> {
  return Array.from(this.items.keys()).map(k => this.items.get(k) === obj ? k : undefined).filter(k => !!k);
}

public findFirst(obj: TObject): TKey | undefined {
  return Array.from(this.items.keys()).find(k => this.items.get(k) === obj);
}

说明:

// Gets the keys as an array
Array.from(this.items.keys())

// Map the keys whose object matches the instance of `obj` to the key itself, undefined otherwise
.map(k => this.items.get(k) === obj ? k : undefined)

// Filter out array elements that are undefined
// (!! is for strict type-checking/readability practices, you can simply use k => k)
.filter(k => !!k)

// Finds the first occurrence of the key for the given object, undefined if not found
.find(k => this.items.get(k) === obj)

【讨论】:

    【解决方案4】:

    我的 TypeScript 版本:

    const getByValue = <A, B>(m: Map<A,B>, searchValue: B):[A, B] | undefined => {
      const l:IterableIterator<[A, B]> = m.entries();
      const a:[A, B][] = Array.from(l);
      return a.find(([_k,v]) => v === searchValue);
    }
    

    【讨论】:

      【解决方案5】:

      JavaScript MapObject

      给定一个 JavaScript 地图,我喜欢 Nitish's answer

      const map = new Map([
        [1, 'one'],
        [2, 'two'],
        [3, 'three'],
      ]);
      
      function getKey(val) {
        return [...map].find(([key, value]) => val === value)[0];
      }
      
      console.log(getKey('one'));   // 1
      console.log(getKey('two'));   // 2
      console.log(getKey('three')); // 3

      对于 JavaScript 对象,您可以这样做:

      const map = {
        1: 'one',
        2: 'two',
        3: 'three',
      };
      
      function getKey(val) {
        return Object.keys(map).find(key => map[key] === val);
      }
      
      console.log(getKey('one'));   // 1
      console.log(getKey('two'));   // 2
      console.log(getKey('three')); // 3

      【讨论】:

      • 很好,对于 Map 之一,如果从未找到匹配的值并返回 undefined 时不会出错,该函数可以是 [...map].find(([key, value]) =&gt; val === value)?.[0]
      【解决方案6】:

      虽然已经有迟到和其他很好的答案,但您仍然可以尝试以下“...”和“Array.find”:

      let people = new Map();
      people.set('1', 'jhon');
      people.set('2', 'jasmein');
      people.set('3', 'abdo');
      
      function getKey(value) {
        return [...people].find(([key, val]) => val == value)[0]
      }
      
      console.log('Jasmein - ', getKey('jasmein'))
      
      console.log('Jhon - ', getKey('jhon'))

      【讨论】:

      • 但是如果你想在两个方向上进行 O(1) 查找怎么办
      【解决方案7】:

      没有任何直接的方法可以在这个方向上挑选信息,所以如果你只有地图,你需要按照其他人的建议遍历集合。

      如果 map/array/other 足够大以至于这样的循环会成为性能问题,并且在项目中经常需要反向查找,您可以使用一对 maps/arrays/other 实现自己的结构一个是当前对象,另一个是键和值颠倒。

      这样,反向查找与正常查找一样有效。当然,您还有更多工作要做,因为您需要实现所需的每种方法作为对一个或两个基础对象的传递,因此如果地图很小和/或不需要反向查找,通常扫描-via-loop 选项可能更可取,因为它更易于维护并且可能更易于 JiT 编译器优化。

      在任何情况下,要警​​惕的一件事是多个键可能具有相同值的可能性。如果这是可能的,那么在遍历您的地图时,您需要决定是否可以任意返回一个可能的键(可能是第一个键),或者您是否要返回一个键数组,以及是否为可能具有重复值的数据也需要考虑相同的问题。

      【讨论】:

        【解决方案8】:

        您可以使用for..of 循环直接遍历 map.entries 并获取密钥。

        function getByValue(map, searchValue) {
          for (let [key, value] of map.entries()) {
            if (value === searchValue)
              return key;
          }
        }
        
        let people = new Map();
        people.set('1', 'jhon');
        people.set('2', 'jasmein');
        people.set('3', 'abdo');
        
        console.log(getByValue(people, 'jhon'))
        console.log(getByValue(people, 'abdo'))

        【讨论】:

        • 此解决方案的一个优势可能是速度,因为没有数组转换。不过,要真正融入 ES6 的精神,如果将 var 更改为 const 可能会很好。
        • 不错,。提醒一下constfor of 中也可以使用。在这方面,它不像普通的for
        • 我个人偏好使用let 而不是const
        • 你不需要map.entries(),地图本身就是一个迭代器。我提交了修改。
        【解决方案9】:

        您可以将其转换为条目数组(使用[...people.entries()])并在该数组中搜索它。

        let people = new Map();
        people.set('1', 'jhon');
        people.set('2', 'jasmein');
        people.set('3', 'abdo');
            
        let jhonKeys = [...people.entries()]
                .filter(({ 1: v }) => v === 'jhon')
                .map(([k]) => k);
        
        console.log(jhonKeys); // if empty, no key found otherwise all found keys.

        【讨论】:

        • 实际上我们需要的是键而不是索引或值.. :)
        • @NinaScholz 我很好奇,您是否真的在工作中使用这种单行代码,以及您是否正在以任何方式调试代码?
        • @NinaScholz 如果直接解构映射值,则不需要数组访问和|| 逻辑:[...people.values()]
        • @philraj,我改变了答案。另一种解决方案可能是使用 Array.from 和值的映射。
        • @NinaScholz 很奇怪,我不知道那个技巧。 edit { 1: v } 语法是什么?您将键值对解构为对象?为什么不用[k, v] 来代替
        【解决方案10】:

        可以反转 Map 以便键是值,值是键,然后查找原始值作为键。这是一个例子:

        let myMap = new Map([
          [1, 'one'],
          [2, 'two'],
          [3, 'three'],
        ]);
        
        let invertedMap = new Map([...myMap.entries()].map(
          ([key, value]) => ([value, key]))
        );
        
        console.log(invertedMap.get('one'))
        // => 1
        

        【讨论】:

        • 但是当值重复时会发生什么?
        • @Brooklyn99 使用重复值作为键是有问题的,因为键需要唯一性。但是,即使在这种情况下,invertedMap 也会反映最后一个重复值的值。
        猜你喜欢
        • 2019-02-02
        • 1970-01-01
        • 2021-01-26
        • 2012-04-12
        • 1970-01-01
        • 2012-07-20
        • 2016-08-21
        相关资源
        最近更新 更多