Учитывая хеш-таблицу (или словарь), например:
$HashTable = @{ 'One' = 1; 'Two' = 2; 'Three' = 3 }
(который по умолчанию нечувствителен к регистру, но не обязательно должен быть в каждой ситуации для моего случая. Фактически, я понятия не имею о компараторе, который используется в данной хеш-таблице или словаре)
Получить соответствующее значение для конкретного элемента легко:
$HashTable['two'] # Or: $HashTable.get_item('two')
2
Но возможно ли также вернуть фактический ключ (определенного элемента), например:
$HashTable.get_Key('two')
Что вернет Two
(с большой буквы T
), а не ошибку или two
в нижнем регистре?
Видимо я не совсем ясно сформулировал свой вопрос, поэтому перефразирую его:
Для любой хеш-таблицы (или словаря) с неизвестным компаратором я ищу способ получить исходный key
конкретного элемента, аналогично тому, как я получил бы value
конкретного элемента. Значение:
Между тем я начинаю верить, что ответ на мой вопрос такой:
Это невозможно
Просто потому, что ключ хеш-таблицы не переупорядочивается, а только hashcode
и данные восстановления коллизий, но не весь ключ (/string, размер которого может быть практически неограниченным).
Это также не имеет смысла, потому что весь ключ по-прежнему доступен в коллекции .keys
, но, очевидно, (обратная) ссылка на реальный предмет исчезла...
Честно говоря, то, что я ожидал от этого вопроса, было либо:
Но оказалось, что у меня есть список интересных альтернатив, которые я мог бы использовать вместо этого. Для моего реального варианта использования производительность важна, но более важным является тот факт, реагирует ли предложение предоставления в соответствии с моими ожиданиями. Поэтому я установил тестовый стенд Pester, чтобы подтвердить это:
Describe 'Retrive the original key from a hashtable or dictionary' { # https://stackoverflow.com/a/78656228
BeforeAll {
$CaseInsensitive = [Ordered]@{
One = 1
Two = 2
Three = 3
4 = 'Four'
}
$CaseSensitive = [System.Collections.Specialized.OrderedDictionary]::new()
$CaseSensitive['One'] = 1
$CaseSensitive['two'] = 2
$CaseSensitive['Two'] = 2
$CaseSensitive['three'] = 3
$CaseSensitive['Three'] = 3
$CaseSensitive['THREE'] = 3
$CaseSensitive[[Object]4] = 'Four'
# mklement0 https://stackoverflow.com/a/78656228/1701026
function GetKey($Dictionary, $lookupKey) {
if ($Dictionary.Contains($lookupKey)) {
$candidateKeys = $Dictionary.Keys.Where({ $_ -eq $lookupKey }, 0, 2)
if ($candidateKeys.Count -ge 2) { $lookupKey }
else { $candidateKeys[0] }
}
}
# Fabrice SANGA https://stackoverflow.com/a/78656281/1701026
# $CaseInsensitive | Add-Member -Force -MemberType ScriptMethod -Name get_Key -Value {
# param([string] $CaseInsensitiveIndex)
# return $this.Keys.Where{ $_ -ceq ([CultureInfo]::InvariantCulture).TextInfo.ToTitleCase($CaseInsensitiveIndex) }
# }
# $CaseSensitive | Add-Member -Force -MemberType ScriptMethod -Name get_Key -Value {
# param([string] $CaseInsensitiveIndex)
# return $this.Keys.Where{ $_ -ceq ([CultureInfo]::InvariantCulture).TextInfo.ToTitleCase($CaseInsensitiveIndex) }
# }
# function GetKey($Dictionary, $lookupKey) {
# $Dictionary.get_Key($lookupKey)
# }
# Abraham Zinala https://stackoverflow.com/a/78657526/1701026
# function GetKey($HashTable, $to_match) {
# [System.Linq.Enumerable]::Where(
# ($entries = [System.Collections.DictionaryEntry[]]@($HashTable.GetEnumerator())),
# [Func[System.Collections.DictionaryEntry, bool]]{
# param($entry)
# $entry.Key -match $to_match -and $entries.Where{$_.Key -match $to_match}.Count -eq 1 -or
# $entry.Key -cmatch $to_match -and $entries.Where{$_.Key -match $to_match}.Count -ge 1
# }
# ).Key
# }
# iRon
# function GetKey($Dictionary, $LookupKey) {
# if ($Dictionary -is [System.Collections.IDictionary]) {
# if (-not $Dictionary.Contains($LookupKey)) { return }
# } else {
# if (-not $Dictionary.ContainsKey($LookupKey)) { return }
# }
# foreach ($Key in $Dictionary.get_Keys()) {
# if ($Key -ceq $LookupKey) { return $Key }
# if ($Key -ieq $LookupKey) { $iKey = $Key }
# }
# if ($Null -ne $iKey) { $iKey } else { $LookupKey }
# }
}
Context 'Case insensitive, existing key' {
It 'One' {
$CaseInsensitive['One'] | Should -Be 1
GetKey $CaseInsensitive 'One' | Should -BeExactly 'One'
}
It 'ONE' {
$CaseInsensitive['ONE'] | Should -Be 1
GetKey $CaseInsensitive 'ONE' | Should -BeExactly 'One'
}
It 'two' {
$CaseInsensitive['two'] | Should -Be 2
GetKey $CaseInsensitive 'two' | Should -BeExactly 'Two'
}
It 'Two' {
$CaseInsensitive['Two'] | Should -Be 2
GetKey $CaseInsensitive 'Two' | Should -BeExactly 'Two'
}
It 'TWO' {
$CaseInsensitive['TWO'] | Should -Be 2
GetKey $CaseInsensitive 'TWO' | Should -BeExactly 'Two'
}
It 'three' {
$CaseInsensitive['three'] | Should -Be 3
GetKey $CaseInsensitive 'three' | Should -BeExactly 'Three'
}
It 'Three' {
$CaseInsensitive['Three'] | Should -Be 3
GetKey $CaseInsensitive 'Three' | Should -BeExactly 'Three'
}
It 'THREE' {
$CaseInsensitive['THREE'] | Should -Be 3
GetKey $CaseInsensitive 'THREE' | Should -BeExactly 'Three'
}
It '4' {
$CaseInsensitive[[Object]4] | Should -Be 'Four'
GetKey $CaseInsensitive 4 | Should -Be 4
}
}
Context 'Case insensitive, missing key' {
It '"4"' {
$CaseInsensitive['4'] | Should -BeNullOrEmpty
GetKey $CaseInsensitive '4' | Should -BeNullOrEmpty
}
}
Context 'Case sensitive, existing key' {
It 'One' {
$CaseSensitive['One'] | Should -Be 1
GetKey $CaseSensitive 'One' | Should -BeExactly 'One'
}
It 'two' {
$CaseSensitive['Two'] | Should -Be 2
GetKey $CaseSensitive 'Two' | Should -BeExactly 'Two'
}
It 'Two' {
$CaseSensitive['Two'] | Should -Be 2
GetKey $CaseSensitive 'Two' | Should -BeExactly 'Two'
}
It 'three' {
$CaseSensitive['three'] | Should -Be 3
GetKey $CaseSensitive 'three' | Should -BeExactly 'three'
}
It 'Three' {
$CaseSensitive['Three'] | Should -Be 3
GetKey $CaseSensitive 'Three' | Should -BeExactly 'Three'
}
It 'THREE' {
$CaseSensitive['THREE'] | Should -Be 3
GetKey $CaseSensitive 'THREE' | Should -BeExactly 'THREE'
}
It '4' {
$CaseInsensitive[[Object]4] | Should -Be 'Four'
GetKey $CaseInsensitive 4 | Should -Be 4
}
}
Context 'Case sensitive, missing key' {
It 'one' {
$CaseSensitive['one'] | Should -BeNullOrEmpty
GetKey $CaseSensitive 'one' | Should -BeNullOrEmpty
}
It 'ONE' {
$CaseSensitive['ONE'] | Should -BeNullOrEmpty
GetKey $CaseSensitive 'ONE' | Should -BeNullOrEmpty
}
It 'TWO' {
$CaseSensitive['TWO'] | Should -BeNullOrEmpty
GetKey $CaseSensitive 'TWO' | Should -BeNullOrEmpty
}
It 'Four' {
$CaseSensitive['Four'] | Should -BeNullOrEmpty
GetKey $CaseSensitive 'Four' | Should -BeNullOrEmpty
}
It '"4"' {
$CaseInsensitive['4'] | Should -BeNullOrEmpty
GetKey $CaseInsensitive '4' | Should -BeNullOrEmpty
}
}
}
@SantiagoSquarzon, я думал об этом, но что, если я не знаю, чувствительна ли данная хеш-таблица (или словарь) к регистру или нет. Это приводит меня в замешательство22 с другим моим вопросом (вы ответили): Проверьте, чувствителен ли словарь к регистру
Что ж, тогда вы уже получили ответ :) вам нужно будет выполнить проверку для каждого типа, реализующего IDIctionary. Для хеш-таблицы вы можете получить ее из поля _keycomparer
.
Что должно произойти в $HashTable.get_Key('two')
, если в вашем примере хеш-таблица нечувствительна к регистру? Каков вариант использования? Вы хотите получить ключ, принадлежащий заданному значению? Я думаю, что тебе нужно $hashTable.GetKeyByValue(2)
, и в этом случае ты можешь использовать ReferenceEquals
, чтобы получить это.
@SantiagoSquarzon, по первому вопросу: если хеш-таблица нечувствительна к регистру, я бы ожидал Two
, если хеш-таблица чувствительна к регистру, я бы ничего не ожидал взамен, поскольку нет совпадающих элементов. Что касается вашего второго вопроса, я углублюсь в метод GetKeyByValue
, но подозреваю, что это тоже может не сработать, поскольку value
может не быть уникальным.
Для первого ответа: Разве не для этого нужен .ContainsKey
?
Если бы словарь был нечувствителен к регистру, мог бы быть только один ключ с другим регистром, если бы словарь был чувствителен к регистру, тогда нужно было бы использовать точный регистр, чтобы .ContainsKey
возвращал true
?
@SantiagoSquarzon, я обновил вопрос и надеюсь, что он станет более понятным, когда я ожидаю чего-то (оригинала key
) взамен.
Вам нужен DictionaryEntry
? $HashtTable.GetEnumerator().Where{ $_.Key -match 'two' }[0]
@iRon, я согласен, что это в целом невозможно, и я переработал нижнюю часть своего ответа, чтобы обсудить гипотетически более эффективное решение, основанное на гипотетическом доступе к экземпляру компаратора. Тем не менее, решение, основанное на трех нелинейных поисках, возможно, в частности, для System.Collections.Generic.SortedDictionary`2
, хотя на практике вы можете не заметить разницу по сравнению с оптимизированным линейным поиском, за исключением больших словарей.
@AbrahamZinala, нет, вопрос в том, чтобы получить регистро-точную форму исходного ключа, которая использовалась при добавлении записи.
PS, @iRon: Я хотел сказать: в целом невозможно сделать эффективно, но возможно, как показано в средней части моего ответа.
Спасибо за добавление тестов; Я понимаю, что и мое решение, и решение Абрахама возвращают ожидаемые результаты. Что касается производительности: я добавил альтернативное решение, которое оптимизирует случай, когда ключ поиска уже находится в точной форме; оно также бывает короче.
@mklement0 mklement0, я обновил тесты, поскольку обнаружил ошибку в решении Авраама (см. комментарий к его ответу). это означает, что на данный момент вашим решением является только то, которое действительно работает и выдерживает тесты Pester.
Предисловие:
Мне неизвестен эффективный способ получения исходного ключа в точной по регистру форме, но ниже приводится краткий способ (в ущерб производительности 'First'
можно опустить) с использованием встроенного .Where() метод:
@{
'One' = 1
'Two' = 2
'Three' = 3
}.Keys.Where({ $_ -eq 'two' }, 'First')[0] # -> 'Two'
Примечание:
Вышеупомянутое надежно работает только со словарями, нечувствительными к регистру, которыми являются хеш-таблицы PowerShell и [ordered]
литералы хэш-таблицы (обратите внимание, что использование 'First'
не является строго обязательным, но является оптимизацией производительности: он перестает следить за первым - и определение только в регистронезависимом словаре — match [0]
извлекает единственный элемент коллекции совпадений, который .Where()
неизменно возвращает).
В случае словарей, чувствительных к регистру, вышеизложенное может сообщать о ложных срабатываниях — см. ниже решение, которое также работает с такими словарями.
.Keys.Where({ $_ -eq 'two' }, 'First')[0]
с.Keys.Where({ $_ -eq 'two' })
Причина, по которой это неэффективно, заключается в необходимости выполнения линейного (O(N)) поиска по коллекции ключей, а тот факт, что блок сценария должен вызываться для каждого элемента коллекции, не помогает.
Работа со словарем, чувствительность к регистру которого неизвестна, требует дополнительной работы, включая, возможно, проверку всех ключей:
# Note: [System.Collections.Specialized.OrderedDictionary] is the type
# underlying [ordered] hashtable literals in PowerShell.
# It - like [System.Collections.Hashtable] - is case-SENSITIVE by default;
# it is PowerShell that makes them case-INsensitive behind the scenes.
$dictionary = [System.Collections.Specialized.OrderedDictionary]::new()
$dictionary['One'] = 1
$dictionary['TWO'] = 2
$dictionary['two'] = 2 # case variation
$dictionary['Three'] = 2
$lookupKey = 'two'
# -> Stores 'two' in $actualKey, the case-exact match.
$actualKey =
if ($dictionary.Contains($lookupKey)) {
$candidateKeys = $dictionary.Keys.Where({ $_ -eq $lookupKey }, 0, 2)
if ($candidateKeys.Count -ge 2) { $lookupKey }
else { $candidateKeys }
}
.Contains($lookupKey)
, отсеивает ключи поиска, которые не соответствуют существующему ключу, независимо от регистра или без учета регистра.
.Where({ $_ -eq $lookupKey }, 0, 2)
ищет не более 2
кандидатов (потенциальных) ключевых совпадений, без учета регистра; ограничение поиска двумя кандидатами является оптимизацией производительности: по наличию двух (или более) ключей, которые являются вариациями регистра друг друга, можно сделать вывод, что имеющийся словарь чувствителен к регистру.
Если есть (по крайней мере) два потенциальных ключа ($candidateKeys.Count -ge 2
), это означает, что хеш-таблица чувствительна к регистру и, следовательно, ключ поиска по определению уже находится в форме с точным регистром (в противном случае .Contains()
не возвращался бы $true
) , поэтому $lookupKey
возвращается.
В противном случае, если возвращается только один ключ-кандидат, этот ключ по определению является правильным и возвращается (обратите внимание, что в этом случае нет необходимости использовать [0]
для вывода самого единственного элемента, поскольку использование Оператор if
вызывает автоматическое перечисление любой коллекции (перечисляемой), как в конвейере).
Альтернатива, которая оптимизируется для случая, когда ключ поиска уже находится в точной по регистру форме:
$actualKey =
if ($dictionary.Contains($lookupKey)) {
if (-1 -ne [Array]::IndexOf($dictionary.Keys, $lookupKey)) { $lookupKey }
else { $dictionary.Keys.Where({ $_ -eq $lookupKey }, 'First') }
}
Как только существование соответствующего ключа подтверждено с помощью .Contains()
, сначала выполняется регистрозависимый (линейный) поиск с использованием [Array]::IndexOf()
(который выполняет сравнения с учетом регистра и региональных параметров), который работает намного лучше, чем фильтр .Where()
.
Если обнаружено совпадение с точным регистром, ключ поиска по определению уже находится в форме с точным регистром.
В противном случае подразумевается, что словарь нечувствителен к регистру и, следовательно, этого достаточно для поиска первого (и по определению только) ключа в коллекции .Keys
, который без учета регистра соответствует ключу поиска, используя .Where()
с -eq
.
Гипотетическое эффективное решение потребует эффективных (O(1)) поисков в коллекции .Keys
, которые сами возвращают совпадающие ключи, но, как вы заметили, словари не предназначены для этого, поскольку они переводят ключи в абстрактные хеши (числа) для поиска. значений (и эти хэши не имеют обратной связи с ключами, из которых они были получены).
Если бы это было возможно - это не так[1] - получить System.Collections.IEqualityComparer / System.Collections.Generic.IEqualityComparer`1 , используемый данным экземпляром словаря, и конкретный используемый экземпляр имеет тип предоставляется через статические свойства класса System.StringComparer , например [System.StringComparer]::OrdinalIgnoreCase, вы можете, по крайней мере, сделать вывод о чувствительности к регистру и оптимизировать на основе этого: если словарь чувствителен к регистру и поиск значения (.Contains()
) завершается успешно, вы по определению имеете дело с формой ключа, точной по регистру; в противном случае достаточно (линейно) найти первый ключ, совпадающий без учета регистра, в .Keys
.
Если данный словарь имеет тип System.Collections.Generic.SortedDictionary`2, вы можете выполнить бинарный поиск по коллекции .Keys
, воспользовавшись тем фактом, что коллекция ключей по определению отсортирована по строки:[Array]::BinarySearch($sortedDict.Keys, $lookupKey, [StringComparer]::InvariantCultureIgnoreCase)
В частности, с помощью System.Collections.Generic.SortedDictionary`2
вы можете добиться относительно эффективного решения, даже не зная компаратора, с помощью двух-трех нелинейных поисков (хотя на практике вы можете не заметить разницы с линейным подходом, за исключением больших словарей):
Проверьте, соответствует ли ключ поиска .Contains()
, и остановитесь, если нет.
Если это так, сначала используйте двоичный поиск с учетом регистра: если он успешен, ключ поиска по определению уже находится в форме с учетом регистра, поэтому верните его.
В противном случае (словарь должен быть нечувствителен к регистру) повторите поиск без учета регистра, который по определению найдет единственный соответствующий ключ, который является регистрозависимым вариантом ключа поиска, и вернет его.
[1]
+1
за альтернативу (и предположение, что, вероятно, это невозможно сделать). Проблема с альтернативой заключается в том, что если хеш-таблица чувствительна к регистру, я могу получить взамен несколько ключей (или только первый). Я не думаю, что есть способ вытащить компаратор из хеш-таблицы и каким-то образом использовать его в фильтре where
.
@железо. Используйте Add-Member
и определите это альтернативное решение как новый член объекта $Hashtable
. Вот так $Hashtable | Add-Member -MemberType ScriptMethod -Name get_Key -Value { param([string] $a) return $this.Keys.Where{ $_ -eq $a } }
. Если компаратор чувствителен к регистру, вполне нормально получить несколько ключей. Если компаратор не чувствителен к регистру, вы получите только один ключ.
@FabriceSANGA, «Если компаратор чувствителен к регистру, вполне нормально получить несколько ключей». Я уверен, что понимаю, что вы имеете в виду. Как я это вижу: (независимо от компаратора) хеш-таблица (или словарь) обычно возвращает один элемент (или ни одного).
@железо. Если компаратор чувствителен к регистру, вы сможете ввести клавиши 'Two'
, 'tWo'
, 'two'
как три разных ключа. Но вызов ScriptMethod таким образом get_Key('two')
соответствует трем клавишам. Вот почему @mklment0 использует 'First'
в методе where()
, чтобы получить только первый из них.
Спасибо, @iRon. Хорошая мысль относительно хеш-таблиц с учетом регистра - я обновил ответ, чтобы показать решение и для них (и, следовательно, также для хеш-таблиц с неизвестным статусом чувствительности к регистру), а также добавил раздел для решения (очевидно, невыполнимого) желание получить компаратор.
Вы можете добавить член метода сценария get_Key
в хеш-таблицу, которая выполняет поиск по списку ключей, который точно соответствует формату заголовка (без учета всех заглавных букв*) указанной строки параметра $CaseInsensitiveKey
, используя метод System.Globalization.TextInfo.ToTitleCase()
:
$Hashtable | Add-Member -Force -MemberType ScriptMethod -Name get_Key -Value {
param([string] $CaseInsensitiveKey)
if (($Keys = @($this.Keys.Where{ $_ -eq $CaseInsensitiveKey })).Count -gt 1) {
return $Keys.Where{ $_ -ceq $CaseInsensitiveKey }
}
return $Keys[0]
}
* Формат Title-Case включает все прописные буквы, которые в этом случае игнорируются.
$this.Keys
возвращает список ключей, который фильтруется по индексу в методе Where()
. Это означает, что возвращаемое значение не является сфабрикованным. Из отредактированного решения добавлен дополнительный тест на то, что ключ поиска возвращает значение, когда он индексируется хеш-таблицей.
Однако решение не самое оптимальное, поскольку поиск производится дважды.
Результат теста Пестера
Starting discovery in 1 files.
Discovery found 20 tests in 19ms.
Running tests.
[+] W:\PowerShell\markdown-to-html-shortcut\MarkdownToHtmlShortcut\test.ps1 119ms (39ms|63ms)
Tests completed in 123ms
Tests Passed: 20, Failed: 0, Skipped: 0 NotRun: 0
Я не ищу value
, я ищу оригинал (актуальный) key
независимо от используемого компаратора. Другими словами: если ключ существует, я хотел бы получить его исходное имя (которое может незначительно отличаться от ключа поиска из-за используемого компаратора, а может и не отличаться).
@iRon, я думаю, что реализовал ваш вариант использования, который не знает о реализованном средстве сравнения, и он возвращает только один ключ, если он существует и записан в формате заголовка.
iRon, Он извлекает ключ.
Хорошо, завтра проведу еще несколько тестов.
iRon ([CultureInfo]::InvariantCulture).TextInfo.ToTitleCase('two')
возвращается Two
. Таким образом в списке ключей выглядит ключ Two
и сравнение выполняется с учетом регистра.
Обратите внимание, что вопрос не связан с регистром заголовка. Цель состоит в том, чтобы получить исходный ключ - в том виде, в котором он использовался при добавлении записи - точно по регистру, для любого словаря с неизвестной чувствительностью к регистру, чего ваш код не делает. Также обратите внимание, что класс [StringComparer]
имеет статические свойства, которые предоставляют готовые средства сравнения на равенство (например, [StringComparer]::CurrentCultureIgnoreCase
), поэтому нет необходимости создавать свои собственные. Наконец, хотя показать обертку метода сценария для желаемой функциональности не помешает, это не является целью вопроса.
@mklement0. Спасибо, я упростил пример использования, используя компаратор равенства [StringComparer]::InvariantCulture
. Но если вы прочитаете метод сценария get_Key
, который я предлагаю добавить, вы увидите, что ключ возврата фильтруется с помощью индекса поиска по регистру заголовка. Ключ не сфабрикован, а сфабрикован индекс поиска. Возвращается ключ, соответствующий индексу, а не индекс. Индекс — это аргумент метода get_Key
.
Я до сих пор не понимаю угол заголовка-регистра, но позвольте мне проиллюстрировать мою точку зрения: Учитывая $HashTable = @{ 'One' = 1; 'twO' = 2; 'Three' = 3 }
с добавленным вашим методом сценария, $HashTable.get_key('two')
, $HashTable.get_key('TWO')
и $HashTable.get_key('twO')
все должны вернуть 'twO'
- ключ в том случае, когда он был определен изначально - но ваш код возвращает ничего.
@mklement0, я понимаю вашу точку зрения, но определение варианта использования зависит от @iRon. То, что он объяснил, предполагает, что именно ключ заголовка и регистра должен быть возвращен. То есть если $HashTable = @{ 'One' = 1; 'twO' = 2; 'Three' = 3 }
, то $HashTable.get_key('two')
ничего не возвращает, а $HashTable.get_key('three')
вернет Three
.
Я не думаю, что в вопросе («получить настоящий ключ») есть какая-либо двусмысленность, и ничто не указывает на то, что пример заглавной буквы T
в Two
был чем-то иным, кроме примера вариации падежа. Но я позволю @iRon высказать свое мнение.
Давайте продолжим обсуждение в чате.
@mklement0, спасибо, что помог прояснить вопрос. @**Fabrice**, я добавил тестовый стенд Pester в обновление 2, чтобы вы могли видеть, какую реакцию я ожидаю от функции (к сожалению, ваше предложение не удалось при 6 баллах в тесте)
@iRon, есть решение, основанное на вариантах использования. Я просто изменил предыдущий метод сценария и добавил дополнительный тест, соответствующий случаю использования, когда ключ отсутствует, и дополнительную строковую операцию с индексным ключом. Я завершил рассуждения доказательством того, что возвращенный ключ оригинальный, а не поддельный.
В моих тестах на приставание есть примеры случаев заголовков, но для этого есть ограничение. Другими словами: эта хеш-таблица: $HashTable = @{ 'oNe' = 1 }
должна вернуть oNe
для: $HashTable.get_Key('oNe')
На основе статей about_Hash_Tables и about_Arrays:
Обновлять:
$HashTable = @{ 'One' = 1; 'Two' = 2; 'Three' = 3}
$HashTable.Keys | Where-Object { $_ -eq 'two' } # returns string of value 'Two'
$HashTable.Keys | Where-Object { $_ -eq 'foo' } # returns $null
# or
$HashTable.Keys.Where( { $_ -eq 'two' } ) # returns Collection`1 of value @('Two')
$HashTable.Keys.Where( { $_ -eq 'foo' } ) # returns Collection`1 of value @()
Оригинальный ответ:
$HashTable = @{ 'One' = 1; 'Two' = 2; 'Three' = 3; 'Single' = 1 }
$HashTable.Keys | Where-Object { $HashTable.$_ -eq 1 }
'--- or using GetEnumerator method ---'
$HashTable.GetEnumerator().Where( {$_.Value -eq 1}) |
Select-Object -ExpandProperty Name
Я не ищу key
на основе какого-либо value
. Я ищу оригинал key
на основе поиска в хеш-таблице (или словаре), которая может быть чувствительной или нечувствительной к регистру.
Ответ @iRon обновлен…
К вашему сведению, я добавил к вопросу несколько (назойливых) тестов, чтобы сделать ожидаемый результат более ясным.
Хорошо! Я считаю, что решение, охватывающее оба сценария сопоставления с учетом регистра и без учета регистра на основе количества, может выглядеть примерно так:
$HashTable = @{ 'One' = 1; 'Two' = 2; 'Three' = 3 }
$to_match = 'two'
[System.Linq.Enumerable]::Where(
($entries = [System.Collections.DictionaryEntry[]]@($HashTable.GetEnumerator())),
[Func[System.Collections.DictionaryEntry, bool]]{
param($entry)
$entry.Key -match $to_match -and $entries.Where{$_.Key -match $to_match}.Count -eq 1 -or
$entry.Key -cmatch $to_match -and $entries.Where{$_.Key -match $to_match}.Count -ge 1
}
)
где мы превращаем хеш-таблицу в набор явных DictionaryEntry
, а затем фильтруем записи с помощью Linq.Enumerable
Where() метода.
Так,
Теперь мы получаем, что 'one'
соответствует одной записи 'one'
,'One'
,'oNe'
и т. д., но несколько записей, таких как 'two'
,'TWO'
, сопоставляются с тем, что фильтруется в $to_match
.
+1
. Извините, за позднее подтверждение того, что это решение действительно работает, но мне нужно было время, чтобы его протестировать (см. мое тестирование в разделе «Обновление 2»).
При более внимательном рассмотрении оказывается, что на самом деле в вашем подходе есть недостаток: вы предполагаете, что всегда существует несколько ключей с одним и тем же именем (но с разным регистром), если это касается словаря, чувствительного к регистру, но это не обязательно. случай. Я обновил тест Pester, чтобы уловить эту ловушку...
@iRon, можешь подробнее рассказать об этом утверждении? В приведенном выше примере это тот же самый $HashTable
, который вы использовали, без каких-либо изменений и без других ключей, соответствующих другому регистру. Другими словами, он работает как для записей с несколькими ключами, так и для записей с одним ключом. Возможно я что-то упустил, так что заранее прошу прощения за невежество.
$to_match = 'one'
при наличии единственной записи в $HashTable
, соответствующей ей, возвращается исходный ключ. Таким образом, если существует несколько записей одного и того же ключа (с разным регистром), он соответствует точному регистру. Итак, 'one','One','onE'
вернется только 'one'
на основе $to_match
.
Общее ограничение заключается в том, что функция get_Key(<key>)
должна реагировать аналогично получению значения из хеш-таблицы (с учетом или без учета регистра). Это означает, что если get_Item(<key>)
($HashTable[<key>]
) возвращает (единственное) Value
, get_Key(<key>)
должно возвращать (единственное) key
, а если $HashTable[<key>]
ничего не возвращает, функция get_Key(<key>)
должна ничего не возвращать. Для вашей функции, если я создаю хеш-таблицу с учетом регистра с одним элементом (просто .add('One')
), функция get_Key('ONE')
не должна ничего возвращать (как $HashTable['ONE']
), но ваша функция это делает.
Я понимаю, что вы имеете в виду, думаю, мое замешательство возникло из-за вашего комментария Which would return Two (which a capital T) rather than an error or two in lowercase?
. Если я не ошибаюсь, ваш get_Key('one')
не сможет получить ключ One
, как указано в вашем вопросе. Тем не менее, я оставлю свой ответ здесь, так как он может помочь другим, лол.
Использовать другую хеш-таблицу или ту же самую с инвертированными парами ключ-значение?