Як отримати ключ в об’єкті JavaScript за його значенням?


387

У мене є досить простий об’єкт JavaScript, який я використовую як асоціативний масив. Чи є якась проста функція, яка дозволяє мені отримати ключ для значення, чи потрібно перебирати об'єкт і знаходити його вручну?


Немає такої стандартної функції для цього. Якщо відображення дійсно двонаправлене, тоді тривіально побудувати «перевернуту» карту та індексувати це. Інакше простий ітератор властивості (мабуть, має гарард hasOwnProperty) та раннє повернення, приховане всередині функції, просто чудово ...

Як це могло працювати, якщо на об'єкт посилалося більше одного ключа? var o = []; var map = {first: o, second: o}. Що б find_key(o)повернути?
Гарет

3
не має значення;) Я мав намір використовувати його лише для масиву з унікальними парами ключ-значення.
арік


Я створив версію без ітерації stackoverflow.com/a/36705765/696535 . Цікаво було б протестувати всі запропоновані рішення у jsfiddle
Pawel

Відповіді:


91

За допомогою бібліотеки Underscore.js :

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

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

381
@GeorgeJempty Не всі хочуть завантажити бібліотеку 5 кб для простого пошуку ключа;)
tckmn

4
Просто FYI для тих, хто шукає рішення, яке отримає ВСІ ключі, які відповідають значенню: це не працюватиме.
Бретт

2
Клавіші підкреслення також будуть працювати. underscorejs.org/#keys _.keys ({один: 1, два: 2, три: 3}); => ["один", "два", "три"]
Тадеус Альберс

1
_.invert не працює, коли значення включають об'єкти, коли стикається серіалізація рядків за замовчуванням. Ви можете використати цю гидоту:_.chain(hash).pairs().findWhere({1: 1}).value()[0]
DanHorner

3
Це не повинно бути прийнятою відповіддю, воно пропонує рішення через бібліотеку, яке примусить змінити діючу структуру коду
Маттео

591
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"));


8
Ну, по-справжньому чисто, якщо ви не підтримуєте IE11 :-) Якщо так, то вам потрібен
поліфіл

4
Залежно від реалізації, це, ймовірно, займає O (n) простір, оскільки keys()матеріалізується набір ключів.
Девід Ерман

2
Чисто, але повільно.
Дирижабль

4
Якщо кілька клавіш мають function getKeyByValue(object, value) { return Object.keys(object).filter(key => object[key] === value); }
однакове

Лол. Це не повільно, це O (n), що є майже найкращим можливим часом виконання.
Бен Уейнрайт

179

Немає стандартних методів. Вам потрібно повторити, і ви можете створити простий помічник:

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 ключ в obj)", який би дав вам "getKeyByValue" як "ключ" в якийсь момент.

О людино, я люблю, як це крадькома повертається невизначеним, якщо значення не існує. Молодці. Крім того, просто цікавий момент, це буде виконувати O (n), тому якби об’єкт мав тону властивостей (наприклад, список людей у ​​великому місті та їх адреси), ви, ймовірно, хочете більш ефективного пошуку. Можливо, сортувати значення та двійковий пошук? Так?
corbin

Велике спасибі, коли я побачив погану ідею, мені цікаво, чому тоді я переглянув це і додав сюди для розширення відповіді та широкого читання. stackoverflow.com/questions/3085240 / ...
simongcc

1
@jAndy це НЕ ===, це ==. Ваш код не працює з ===. Він повертається невизначеним.
Декстер

Я думаю, що перетворити його в рядок було б краще виправити помилки типу, просто додати .toString()вподобання obj[ key ].toString()та значення, якщо потрібно ...
CrandellWS

129

Як було сказано, потрібна ітерація. Наприклад, у сучасному браузері ви могли мати:

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);

18
ES6:Object.keys(obj).or(o=>o[key] === value)
Бенджамін Груенбаум

На жаль, функція стрілки ще не є жодним "сучасним" браузером, тому наразі вона є трохи марною - я використовую її в jetpack на Firefox Nightly, це буде в 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 # 's FirstOrDefault :)
Бенджамін Груенбаум

Так, стрілка наближається, але її потрібно буде широко використовувати - якщо не я, хтось працює над певним двигуном. Мені не було відомо про нову пропозицію щодо ES6, я вважав, що це досить закрито: у вас є посилання про orметод? З того, що ви згадали, здається, що він повертає елемент, який відповідає предикату "чи" самому масиву?
ZER0

1
@ sg552, як згадувалося пізніше, orбуло перейменовано. Я вважаю, що вам слід скористатися знахідкою .
ZER0

42

Шлях 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'


Лодаш, очевидно, найкраще рішення цього питання. Я вважаю, що навіть краще, ніж підкреслити.
арік

4
FYI, "спосіб підкреслення": _.findKey(users, { 'age': 1, 'active': true });... це те саме
craigmichaelmartin

якщо масив - це значення, яке потрібно використовувати: - нехай obj = {a: [1, 2], b: [3, 4], c: [5, 6]} _.findKey (obj, function (val) { повернути _.isEqual (val, [5, 6]);});
ashishyadaveee11

8
якщо ваші значення прості, як рядки чи цілі числа, то всупереч очікуванню це не вийде. наприклад, _.find_key({a: "A", b:"B"}, "B"})повертається undefinedтак, як зазначено тут, що вам потрібно зробити _.find_key({a: "A", b:"B"}, _.partial(_.isEqual,"B")})
ryan2johnson9

1
@ ryan2johnson9 Це моя проблема з Лодашем. Мені важко зрозуміти деякі функції (мабуть, я єдиний). Але все-таки спасибі, це працює. Я знайшов інше, коротше рішення. Це призводить до перегріву великих об'єктів, тому будьте обережні з цим. _.invert(haystack)[needle]
Емпі

33
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);
}

7
Я думаю, що це найкращий варіант для 2017+, оскільки він використовує звичайний JavaScript.
мішечок

Схоже, це не працює, якщо у вас є два чи більше номерів, які мають однакове значення
JuicY_Burrito

@SamuelChen це правильно, але якщо він працював, це означає, що в результаті потрібен масив. Де Object.entries(obj).find(i => i[1] === val);використовувати filterзамістьObject.entries(obj).filter(i => i[1] === val);
Pawel

24

ES6 + Один вкладиш

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

Повернути всі ключі зі значенням:

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

1
Має бути прийнятою відповіддю, оскільки немає залежностей.
Ендрю Шульц

22

Я використовую цю функцію:

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

5
Мутація прототипу Об’єкта є поганою практикою: stackoverflow.com/questions/23807805/…
Jon Koops

18

Неітерабельне рішення

Основна функція:

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"

простіше: Object.keys(greetings )[Object.values(greetings ).indexOf('привіт')]
shutsman

16

Зберігайте прототип в чистоті.

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"*/

15

Якщо ви працюєте з бібліотекою Underscore або Lodash , ви можете використовувати функцію _.findKey :

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

_.findKey(users, function(o) { return o.age < 40; });
// => 'barney' (iteration order is not guaranteed)

// The `_.matches` iteratee shorthand.
_.findKey(users, { 'age': 1, 'active': true });
// => 'pebbles'

// The `_.matchesProperty` iteratee shorthand.
_.findKey(users, ['active', false]);
// => 'fred'

// The `_.property` iteratee shorthand.
_.findKey(users, 'active');
// => 'barney'

13

Я створив бібліотеку bimap ( https://github.com/alethes/bimap ), яка реалізує потужний, гнучкий та ефективний інтерфейс двонаправленої карти JavaScript. Він не має залежностей і може використовуватися як на стороні сервера (в Node.js, ви можете встановити його npm install bimap), так і в браузері ( посилаючись на lib / bimap.js ).

Основні операції дійсно прості:

var bimap = new BiMap;
bimap.push("k", "v");
bimap.key("k") // => "v"
bimap.val("v") // => "k"

bimap.push("UK", ["London", "Manchester"]);
bimap.key("UK"); // => ["London", "Manchester"]
bimap.val("London"); // => "UK"
bimap.val("Manchester"); // => "UK"

Отримання відображення ключових значень однаково швидко в обох напрямках. Під кришкою немає дорогих об'їздів об'єктів / масивів, тому середній час доступу залишається постійним незалежно від розміру даних.


Одне з єдиних рішень, що не потребує ітерації (або в самому рішенні, стандартній бібліотеці чи іншій бібліотеці).
Скотт

6

не бачив наступного:

const obj = {
  id: 1,
  name: 'Den'
};

function getKeyByValue(obj, value) {
  return Object.entries(obj).find(([, name]) => value === name);
}

const [ key ] = getKeyByValue(obj, 'Den');
console.log(key)
  


5

Оскільки значення є унікальними, слід додати значення як додатковий набір ключів. Це можна зробити за допомогою наступного ярлика.

var foo = {};
foo[foo.apple = "an apple"] = "apple";
foo[foo.pear = "a pear"] = "pear";

Це дозволить отримати або через ключ, або за значенням:

var key = "apple";
var value = "an apple";

console.log(foo[value]); // "apple"
console.log(foo[key]); // "an apple"

Це передбачає, що між ключами та значеннями немає загальних елементів.


Переглядаючи всі рішення, я пішов з цим. Дуже інтуїтивно зрозумілий.
nehem

1
Одне з єдиних рішень, що не потребує ітерації (або в самому рішенні, стандартній бібліотеці чи іншій бібліотеці).
Скотт

В ОП сказали, що пари ключів / значень були унікальними, тому ця низькотехнологічна відповідь просто фантастична! Молодці;)
customcommander

4

Дано input={"a":"x", "b":"y", "c":"x"}...

  • Щоб використовувати перше значення (наприклад output={"x":"a","y":"b"}):

input = {
  "a": "x",
  "b": "y",
  "c": "x"
}
output = Object.keys(input).reduceRight(function(accum, key, i) {
  accum[input[key]] = key;
  return accum;
}, {})
console.log(output)

  • Щоб використовувати останнє значення (наприклад output={"x":"c","y":"b"}):

input = {
  "a": "x",
  "b": "y",
  "c": "x"
}
output = Object.keys(input).reduce(function(accum, key, i) {
  accum[input[key]] = key;
  return accum;
}, {})
console.log(output)

  • Щоб отримати масив ключів для кожного значення (наприклад output={"x":["c","a"],"y":["b"]}):

input = {
  "a": "x",
  "b": "y",
  "c": "x"
}
output = Object.keys(input).reduceRight(function(accum, key, i) {
  accum[input[key]] = (accum[input[key]] || []).concat(key);
  return accum;
}, {})
console.log(output)


1
це, безумовно, найкраща відповідь, але я дряпав голову над способом її перетворення, щоб повернути лише ключ для даного об'єкта, тобто бути функціонально еквівалентним indexOf для масиву.
Souhaieb Besbes

Якщо пам'ять не є обмеженням, і ви готові витратити багато процесорних потужностей на перегляд об'єкта багато разів, просто збережіть "вихід", як зазначено вище, до змінної і шукайте результат там ... як output['x']. Це те, про що ви питали?
Фабіо Белтраміні

2

http://jsfiddle.net/rTazZ/2/

var a = new Array(); 
    a.push({"1": "apple", "2": "banana"}); 
    a.push({"3": "coconut", "4": "mango"});

    GetIndexByValue(a, "coconut");

    function GetIndexByValue(arrayName, value) {  
    var keyName = "";
    var index = -1;
    for (var i = 0; i < arrayName.length; i++) { 
       var obj = arrayName[i]; 
            for (var key in obj) {          
                if (obj[key] == value) { 
                    keyName = key; 
                    index = i;
                } 
            } 
        }
        //console.log(index); 
        return index;
    } 

@ Fr0zenFyr: Наступне посилання може відповісти на ваше питання краще - stackoverflow.com/questions/8423493/…
Atur

2

Або ще простіше - створити новий об’єкт за допомогою ключів і значень у порядку, який ви хочете, тоді виберіть проти цього об'єкта. У нас виникли конфлікти, використовуючи коди прототипу вище. Не потрібно використовувати функцію String навколо клавіші, тобто необов'язково.

 newLookUpObj = {};
 $.each(oldLookUpObj,function(key,value){
        newLookUpObj[value] = String(key);
    });

2

Це невелике розширення до методу Underscorejs, і він використовує Lodash :

var getKeyByValue = function(searchValue) {
  return _.findKey(hash, function(hashValue) {
    return searchValue === hashValue;
  });
}

FindKey буде шукати і повертати перший ключ , який відповідає значенню.
Якщо ви хочете замість цього останнього матчу, використовуйте FindLastKey .


2

Наче це питання не було побито до м'якоті ...

Ось лише для будь-якої цікавості, яка вона вам приносить ...

Якщо ви впевнені, що ваш об’єкт матиме лише рядкові значення, ви можете справді вичерпати себе, щоб створити цю реалізацію:

var o = { a: '_A', b: '_B', c: '_C' }
  , json = JSON.stringify(o)
  , split = json.split('')
  , nosj = split.reverse()
  , o2 = nosj.join('');

var reversed = o2.replace(/[{}]+/g, function ($1) { return ({ '{':'}', '}':'{' })[$1]; })
  , object = JSON.parse(reversed)
  , value = '_B'
  , eulav = value.split('').reverse().join('');

console.log('>>', object[eulav]);

Можливо, тут є щось корисне для побудови ...

Сподіваюся, це вас забавляє.


2

Ось рішення Лодаша для цього, яке працює для об'єкта "flat key => value", а не для вкладеного об'єкта. Пропозиція прийнятої відповіді використовувати_.findKey твори для об’єктів з вкладеними об'єктами, але це не працює в цій загальній обставині.

Цей підхід інвертує об'єкт, замінюючи клавіші на значення, а потім знаходить ключ, шукаючи значення на новому (перевернутому) об'єкті. Якщо ключ не знайдений, то falseповертається, що я віддаю перевагу undefined, але ви можете легко поміняти це на третій параметр _.getметоду в getKey().

// Get an object's key by value
var getKey = function( obj, value ) {
	var inverse = _.invert( obj );
	return _.get( inverse, value, false );
};

// US states used as an example
var states = {
	"AL": "Alabama",
	"AK": "Alaska",
	"AS": "American Samoa",
	"AZ": "Arizona",
	"AR": "Arkansas",
	"CA": "California",
	"CO": "Colorado",
	"CT": "Connecticut",
	"DE": "Delaware",
	"DC": "District Of Columbia",
	"FM": "Federated States Of Micronesia",
	"FL": "Florida",
	"GA": "Georgia",
	"GU": "Guam",
	"HI": "Hawaii",
	"ID": "Idaho",
	"IL": "Illinois",
	"IN": "Indiana",
	"IA": "Iowa",
	"KS": "Kansas",
	"KY": "Kentucky",
	"LA": "Louisiana",
	"ME": "Maine",
	"MH": "Marshall Islands",
	"MD": "Maryland",
	"MA": "Massachusetts",
	"MI": "Michigan",
	"MN": "Minnesota",
	"MS": "Mississippi",
	"MO": "Missouri",
	"MT": "Montana",
	"NE": "Nebraska",
	"NV": "Nevada",
	"NH": "New Hampshire",
	"NJ": "New Jersey",
	"NM": "New Mexico",
	"NY": "New York",
	"NC": "North Carolina",
	"ND": "North Dakota",
	"MP": "Northern Mariana Islands",
	"OH": "Ohio",
	"OK": "Oklahoma",
	"OR": "Oregon",
	"PW": "Palau",
	"PA": "Pennsylvania",
	"PR": "Puerto Rico",
	"RI": "Rhode Island",
	"SC": "South Carolina",
	"SD": "South Dakota",
	"TN": "Tennessee",
	"TX": "Texas",
	"UT": "Utah",
	"VT": "Vermont",
	"VI": "Virgin Islands",
	"VA": "Virginia",
	"WA": "Washington",
	"WV": "West Virginia",
	"WI": "Wisconsin",
	"WY": "Wyoming"
};

console.log( 'The key for "Massachusetts" is "' + getKey( states, 'Massachusetts' ) + '"' );
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>


2

Ось моє рішення спочатку:

Наприклад, я припускаю, що у нас є об'єкт, який містить три пари значень:

function findKey(object, value) {

    for (let key in object)
        if (object[key] === value) return key;

    return "key is not found";
}

const object = { id_1: "apple", id_2: "pear", id_3: "peach" };

console.log(findKey(object, "pear"));
//expected output: id_2

Ми можемо просто написати findKey (масив, значення), який приймає два параметри, які є об'єктом і значенням ключа, який ви шукаєте. Таким чином, цей метод є багаторазовим і вам не потрібно щоразу вручну повторювати об'єкт, лише передаючи два параметри для цієї функції.


2

Методи ES6 :

Object.fromEntries(Object.entries(a).map(b => b.reverse()))['value_you_look_for']

0

Я зазвичай рекомендую лодаш, а не підкреслення.

Якщо у вас є, використовуйте його.

Якщо ви цього не зробите, то вам варто розглянути можливість використання пакету lodash.invert npm, який досить крихітний.

Ось як ви можете протестувати це за допомогою gulp:

1) Створіть файл під назвою gulpfile.js із наступним вмістом:

// Filename: gulpfile.js
var gulp = require('gulp');
var invert = require('lodash.invert');   
gulp.task('test-invert', function () {
  var hash = {
    foo: 1,
    bar: 2
  };
  var val = 1;
  var key = (invert(hash))[val];  // << Here's where we call invert!
  console.log('key for val(' + val + '):', key);
});

2) Встановіть пакет lodash.invert і глотайте

$ npm i --save lodash.invert && npm install gulp

3) Перевірте, чи працює він:

$ gulp test-invert
[17:17:23] Using gulpfile ~/dev/npm/lodash-invert/gulpfile.js
[17:17:23] Starting 'test-invert'...
key for val(1): foo
[17:17:23] Finished 'test-invert' after 511 μs

Список літератури

https://www.npmjs.com/package/lodash.invert

https://lodash.com/

Відмінності між лодашем і підкресленням

https://github.com/gulpjs/gulp


Чому тут задіяний Галп? Просто запустіть сценарій…
Ry-

0

Рішення підкреслення js

let samplLst = [{id:1,title:Lorem},{id:2,title:Ipsum}]
let sampleKey = _.findLastIndex(samplLst,{_id:2});
//result would be 1
console.log(samplLst[sampleKey])
//output - {id:2,title:Ipsum}

0

це працювало для мене, щоб отримати ключ / значення об'єкта.

let obj = {
        'key1': 'value1',
        'key2': 'value2',
        'key3': 'value3',
        'key4': 'value4'
    }
    Object.keys(obj).map(function(k){ 
    console.log("key with value: "+k +" = "+obj[k])    
    
    })
    


-1

Дійсно прямо.

const CryptoEnum = Object.freeze({
                    "Bitcoin": 0, "Ethereum": 1, 
                    "Filecoin": 2, "Monero": 3, 
                    "EOS": 4, "Cardano": 5, 
                    "NEO": 6, "Dash": 7, 
                    "Zcash": 8, "Decred": 9 
                  });

Object.entries(CryptoEnum)[0][0]
// output => "Bitcoin"

6
Ніякої гарантії, що порядок об’єкта буде однаковим ...
Пуховик

-2

Не ускладнювати!

Вам не потрібно фільтрувати об’єкт за допомогою складних методів або libs, Javascript має вбудовану функцію під назвою Object.values .

Приклад:

let myObj = {jhon: {age: 20, job: 'Developer'}, marie: {age: 20, job: 
'Developer'}};

function giveMeTheObjectData(object, property) {
   return Object.values(object[property]);
}

giveMeTheObjectData(myObj, 'marie'); // => returns marie: {}

Це поверне дані про властивості об'єкта.

Список літератури

https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Object/values

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.