如何通过值获取JavaScript对象中的键?

720

我有一个非常简单的JavaScript对象,我将其用作关联数组。是否有一个简单的函数可以让我获取值对应的键名,还是我必须手动迭代对象并找到它?


没有这样的标准函数可以实现这个功能。如果映射是真正的双向的,那么构建一个“翻转”的映射并对其进行索引就很容易了。否则,一个简单的属性迭代器(可能带有hasOwnProperty保护)和一个隐藏在函数内部的早期返回就可以很好地解决问题... - user166390
如果一个对象被多个键引用,这该怎么办呢? var o = []; var map = {first: o, second: o}find_key(o) 会返回什么? - Gareth
5
没问题 ;) 我只是打算将它用于具有唯一键值对的数组。 - arik
可能是获取JavaScript键/值对象键的最佳方法的重复问题。 - Andy
1
我已经制作了一个没有迭代的版本 https://dev59.com/kmkw5IYBdhLWcg3waZ74#36705765。测试所有建议的解决方案在jsfiddle上将是有趣的。 - Pawel
31个回答

1137
function getKeyByValue(object, value) {
  return Object.keys(object).find(key => object[key] === value);
}

ES6,没有原型突变或外部库。

例如,

function getKeyByValue(object, value) {
  return Object.keys(object).find(key => object[key] === value);
}


const map = {"first" : "1", "second" : "2"};
console.log(getKeyByValue(map,"2"));


12
如果您不支持IE11,那么确实很干净 :-) 如果是这样,您需要一个polyfill。 - Chexpir
4
根据具体实现方式,这可能需要*O(n)*的空间,因为keys()会将键集合实例化。 - David Ehrmann
13
如果多个键具有相同的值,请使用过滤器(filter)而不是查找(find) function getKeyByValue(object, value) { return Object.keys(object).filter(key => object[key] === value); } - saketh
6
笑。这不是慢,它的运行时间是O(n),这几乎是最好的可能情况。 - Ben Wainwright
3
维护BiMap或等效的数据结构(Alethes的回答)通常是解决更广泛问题的高效O(1)方法,但即使是一次性操作,也可以使用for in循环迭代,在找到匹配项时中断,而不是预先创建完整的键数组,这会导致最坏情况下的最佳情况,即O(position)而非O(size)。(此页面上唯一比O(size)更糟糕的答案是愚蠢的JSON答案。) - Ry-
显示剩余9条评论

192

没有标准的可用方法。您需要进行迭代,并可以创建一个简单的助手:

Object.prototype.getKeyByValue = function( value ) {
    for( var prop in this ) {
        if( this.hasOwnProperty( prop ) ) {
             if( this[ prop ] === value )
                 return prop;
        }
    }
}

var test = {
   key1: 42,
   key2: 'foo'
};

test.getKeyByValue( 42 );  // returns 'key1'

一个警告:即使上述方法有效,通常来说扩展任何主机或本地对象的.prototype是一个不好的做法。我在这里这样做只是因为它很好地解决了问题。无论如何,你应该在.prototype之外使用这个函数,并将对象传递给它。


2
其实如果你知道像for-in循环会沿着属性链向下遍历这样的事情,那么"for(var key in obj)"在某个时候会将"getKeyByValue"作为"key"返回,这是没问题的。 - user659025
哦,我喜欢这种隐秘地返回未定义值的方式,如果该值不存在。干得好。另外,只是一个有趣的点,这将执行O(n),因此如果对象具有大量属性(例如大城市中人员和地址列表),则可能需要更有效的搜索。也许可以对值进行排序并进行二进制搜索?嗯? - corbin
非常感谢,当我看到这个坏主意时,我想知道为什么我还要搜索并将其添加到这里以增强答案和广泛阅读。https://dev59.com/J07Sa4cB1Zd3GeqP4pJk - 西門 正 Code Guy
2
@jAndy,不是 ===,而是 ==。你的代码不能使用 ===。它会返回未定义。 - Dexter
一定要遵循警告并将对象传递到标准函数中,这样可以为您节省一些麻烦 :) - markj
显示剩余3条评论

171

就像所言,需要进行迭代。例如,在现代浏览器中您可以使用以下代码:

var key = Object.keys(obj).filter(function(key) {return obj[key] === value})[0];

其中value包含您要查找的值。鉴于此,我可能会使用循环。

否则,您可以使用适当的 "哈希映射"对象 - 在JS中有几个实现 - 或自己实现。

更新2018年

六年过去了,但我仍然在这里得到一些投票,所以我觉得应该在答案中提到一个更现代的解决方案 - 适用于现代浏览器/环境 - 而不仅仅是在评论中:

const key = Object.keys(obj).find(key => obj[key] === value);

当然它也可以是一个函数:

const getKeyByValue = (obj, value) => 
        Object.keys(obj).find(key => obj[key] === value);

22
ES6: Object.keys(obj).or(o=>o[key] === value) 可以翻译为:获取对象中属性值等于特定值的属性名列表。 - Benjamin Gruenbaum
不幸的是,箭头函数在任何“现代”浏览器中都没有出现,所以目前它有点无用 - 我正在Firefox Nightly上使用jetpack,它将出现在Firefox 22中。无论如何,我不知道任何or数组方法,并且它在这里的目的对我来说不太清楚:我将感激一些额外的细节! :) - ZER0
1
关于箭头函数,它即将到来,我在等待着它的到来 :) 至于 or,当然可以!它最近被评估并接受了(我认为还没有人实现它)。它的作用是查找数组中与谓词匹配的第一个元素并返回它。因此 [1,2,3,4].or(x=>x>2) 将返回 3[1,2,3,4,5].or(x=>x<3) 将返回 1。类似于 C# 的 FirstOrDefault :) - Benjamin Gruenbaum
是的,箭头函数已经出现了,但要想广泛使用还需要一些时间——除非像我这样,有人正在开发一个特定的引擎。我不知道ES6的新提案,我认为它已经很封闭了:你有关于“or”方法的链接吗?从你提到的内容来看,它似乎返回与谓词匹配的项“或”数组本身? - ZER0
3
正如后面提到的一样,“or”已被重新命名。我认为现在你应该使用 find - ZER0
显示剩余6条评论

104

最短的一句话

let key = Object.keys(obj).find(k=>obj[k]===value);

返回所有具有该值的键:

let keys = Object.keys(obj).filter(k=>obj[k]===value);
如果value是一个ArrayObject
let keys = Object.keys(obj).filter(k=>JSON.stringify(obj[k])===JSON.stringify(value));

93

使用Underscore.js库:

var hash = {
  foo: 1,
  bar: 2
};

(_.invert(hash))[1]; // => 'foo'

442
并非每个人都希望为简单的键查找加载一个5kb的库 ;) - tckmn
5
任何试图获取匹配某个值的所有键的解决方案都不可行。提供此信息仅供参考。 - Brett
2
underscore的keys函数也可以工作。http://underscorejs.org/#keys _.keys({one: 1, two: 2, three: 3}); => ["one", "two", "three"] - Thaddeus Albers
1
一句警告:根据文档:“为了使其正常工作,您对象的所有值都应该是唯一的并且可串行化的字符串。” - bpromas
4
这不应该成为被接受的答案,它提出了一种通过使用一个强制改变当前代码结构的库来解决问题的方案。 - Matteo
显示剩余2条评论

52

lodash的方式 https://lodash.com/docs#findKey

var users = {
  'barney':  { 'age': 36, 'active': true },
  'fred':    { 'age': 40, 'active': false },
  'pebbles': { 'age': 1,  'active': true }
};

_.findKey(users, { 'age': 1, 'active': true });
// → 'pebbles'


1
Lodash 显然是解决这个问题的最佳方案。我认为,甚至比下划线方式更好。 - arik
4
FYI,“下划线方式”:_.findKey(users,{'age':1,'active':true});……它与原来的意思相同。 - craigmichaelmartin
12
如果你的值很简单,比如字符串或整数,那么与预期相反,这个方法将不起作用。例如,_.find_key({a: "A", b:"B"}, "B"}) 返回 undefined ,因此根据这里所述,你需要使用 _.find_key({a: "A", b:"B"}, _.partial(_.isEqual,"B")}) - ryan2johnson9
1
@ryan2johnson9 这就是我对 Lodash 的问题所在。有些函数让我很难理解(显然只有我一个人这样)。但无论如何,感谢你的帮助,它起作用了。我找到了另一个更短的解决方案。但是要注意,它会在较大的对象上引起过多堆积,所以要小心使用。 _.invert(haystack)[needle] - Empi
1
为了扩展@ryan2johnson9的评论,当值是原始类型(字符串、整数等)时,您需要使用_.findKey({a: "A", b: "B"}, value => value === "B") // => "b",因为第二个参数是一个谓词。简写形式_.findKey({...}, "B")将查找名为B的属性:{b: { B: ... } } - MeltedPenguin
显示剩余2条评论

43
function extractKeyValue(obj, value) {
    return Object.keys(obj)[Object.values(obj).indexOf(value)];
}

为了让闭包编译器提取在编译后将变为未知的键名而制作的。
更性感的版本,但使用未来的Object.entries函数。
function objectKeyByValue (obj, val) {
  return Object.entries(obj).find(i => i[1] === val);
}

编辑2023年:尽管.entries()现在得到了广泛支持,但最终第一个版本仍然更好,因为它不是通过JS回调进行迭代,而是使用本地的indexOf

9
我认为这是2017年以来最好的选择,因为它使用纯JavaScript。 - brainbag
如果您有两个或更多具有相同值的数字,则似乎无法正常工作。 - user6913790
2
2021年你获得了最快的解决方案。对于一个具有15个属性的对象,Object.values(...).find(...)要慢10%,我想知道对于一个大对象来说会更好吗? - Elmatou
@Elmatou 有趣的观察,我不知道那是不是在更大的样本上也成立。JS基准测试在小样本上的结果很随机。 - Pawel
这实际上不会使用Object.entries方法返回键。建议进行编辑。 (有太多待处理的编辑) - MrMesees
显示剩余2条评论

30

我使用这个函数:

Object.prototype.getKey = function(value){
  for(var key in this){
    if(this[key] == value){
      return key;
    }
  }
  return null;
};

用法:

// ISO 639: 2-letter codes
var languageCodes = {
  DA: 'Danish',
  DE: 'German',
  DZ: 'Bhutani',
  EL: 'Greek',
  EN: 'English',
  EO: 'Esperanto',
  ES: 'Spanish'
};

var key = languageCodes.getKey('Greek');
console.log(key); // EL

10
+1个简洁的解决方案。但我有一个问题:在这种情况下,难道不应该始终检查obj.hasOwnProperty(key)吗?或者在这种情况下是不必要的? - V-Light
6
修改对象原型是不良实践:https://dev59.com/emAg5IYBdhLWcg3wQpBX - Jon Koops

22

不可迭代的解决方案

主函数:

var keyByValue = function(value) {

    var kArray = Object.keys(greetings);        // Creating array of keys
    var vArray = Object.values(greetings);      // Creating array of values
    var vIndex = vArray.indexOf(value);         // Finding value index 

    return kArray[vIndex];                      // Returning key by value index
}

键值对组成的对象:

var greetings = {
    english   : "hello",
    ukranian  : "привіт"
};

测试:

keyByValue("привіт");
// => "ukranian"

3
这段代码的作用是从一个包含不同语言问候语的对象中,找到对应乌克兰语问候语 'привіт' 的键名。翻译后的代码如下:Object.keys(greetings)[Object.values(greetings).indexOf('привіт')] - shutsman

18

保持你的原型整洁。

function val2key(val,array){
    for (var key in array) {
        if(array[key] == val){
            return key;
        }
    }
 return false;
}

例子:

var map = {"first" : 1, "second" : 2};
var key = val2key(2,map); /*returns "second"*/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接