Как получить ключ в карте JavaScript по его значению?
У меня есть карта JS, как эта
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'
12 ответов
Вы можете найти его в массиве записей.
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 foudn otherwise all found keys.
Ты можешь использовать 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'))
Хотя поздние и другие замечательные ответы уже существуют, все же вы можете попробовать ниже "..." и "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'))
Карта и объект JavaScript
Учитывая карту JavaScript, мне нравится ответ @Nitish:
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
Не существует прямого метода для выбора информации в этом направлении, поэтому, если у вас есть только карта, вам нужно пройтись по набору, как предлагают другие.
Если карта / массив / другое достаточно велика, чтобы такой цикл был проблемой производительности, а требование обратного просмотра является распространенным в проекте, вы могли бы реализовать свою собственную структуру, используя пару карт / массивов / других с одним как для текущего объекта, а другой с обратным ключом и значением. Таким образом, обратный поиск так же эффективен, как и обычный. Конечно, вам нужно проделать большую работу, поскольку вам нужно реализовать каждый метод, который вам нужен, в качестве прохода к одному или обоим базовым объектам, поэтому, если карта небольшая и / или обратный поиск часто не требуется, сканирование Опция -via-loop, вероятно, будет предпочтительнее, поскольку ее проще поддерживать и, возможно, проще оптимизировать для компилятора JiT.
В любом случае следует опасаться, что несколько ключей могут иметь одно и то же значение. Если это возможно, то при циклическом прохождении карты вам нужно решить, можете ли вы возвратить один из возможных ключей произвольно (возможно, первый) или хотите ли вы вернуть массив ключей, и если вы реализуете обратный индекс для данных, которые могут иметь повторяющиеся значения, эту проблему также необходимо учитывать.
Вот правильно напечатанное решение Typescript, которое не создает массив без надобности.
function find_map_value<K, V>(m: Map<K, V>, predicate: (v: V) => boolean): [K, V] | undefined {
for (const [k, v] of m.entries()) {
if (predicate(v)) {
return [k, v];
}
}
return undefined;
}
Можно инвертировать карту так, чтобы ключи были значениями, а значения были ключами, а затем искать исходное значение как ключ. Вот пример:
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
Почему бы просто не перебрать карту, используя встроенную
let jhonKey;
people.forEach((value, key) => {
if (value === 'jhon')
jhonKey = key
});
Или что-то лучше, возможно, инъекция в своего рода решение, вдохновленное цепочкой прототипов / полифиллом:
Для всех, кому интересно, почему я добавил еще один ответ. Большинство этих ответов (исключение, мне нравится ответ Раджеша , но я воспользовался встроенным и добавленным в цепочку прототипов) делают много дублирования данных во имя поиска значения с помощью оператора распространения или даже прямо создание массивов. Имейте в виду, что Object.keys() также ужасно неэффективен. Я не был удовлетворен тем, какие ответы присутствовали после того, как наткнулся на это в поисках чего-то лучшего, чем то, что я придумал.
Если вас особенно беспокоит производительность, рассмотрите этот ответ, чтобы получить бонусные баллы за разрыв цикла после нахождения значения.
Моя машинописная версия
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);
}
В завершение того, что здесь предложил Мацей Кравчик , — общая реализация круговой карты для этого.
class ReferenceMap {
#left = new Map();
#right = new Map();
constructor(iterable = []) {
this.#left = new Map(iterable);
this.#right = new Map(ReferenceMap.swapKeyValues(iterable));
}
has(key) {
return this.#left.has(key) || this.#right.has(key);
}
get(key) {
return this.#left.has(key) ? this.#left.get(key) : this.#right.get(key);
}
set(key, value) {
this.#left.set(key, value);
this.#right.set(value, key);
}
delete(key) {
if (this.#left.has(key)) {
let ref = this.#left.get(key);
this.#left.delete(key);
this.#right.delete(ref);
} else if (this.#right.has(key)) {
let ref = this.#right.get(key);
this.#right.delete(key);
this.#left.delete(ref);
}
}
entries() {
return this.#left.entries();
}
keys() {
return this.#left.keys();
}
values() {
return this.#left.values();
}
[Symbol.iterator]() {
return this.entries();
}
get size() {
return this.#left.size;
}
static * swapKeyValues(entries) {
for (let [key, value] of entries) yield [value, key];
}
}
Кэш
Вопрос немного неправильный, потому что одно значение может быть присвоено многим ключам. Следовательно, результатом для заданного значения должен быть массив ключей (а не один ключ). Если вы хотите часто выполнять такой поиск, вы можете использовать следующий генератор кеша для обратной карты
let genRevMapCache = map => [...map.entries()].reduce((a,[k,v]) => {
if(!a.get(v)) a.set(v,[]);
a.get(v).push(k);
return a;
}, new Map() );
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)