У меня есть словарь типа <string, string>
, и для конкретного случая мне нужно выполнить обратный поиск. Так, например, предположим, что у меня есть эта запись <"SomeString", "ab">
, и что я передаю в "ab"
, тогда я хотел бы вернуть "SomeString"
.
Прежде чем я начну цикл foreach
по каждой записи в словаре, мне было интересно, что будет самым эффективным способом сделать этот обратный поиск?
Как сделать обратный поиск словаря
Ответ 1
В принципе, вы можете использовать LINQ
и получить Key
, как это, без изменения чего-либо:
var key = dictionary.FirstOrDefault(x => x.Value == "ab").Key;
Если вы действительно хотите отменить свой словарь, вы можете использовать метод расширения следующим образом:
public static Dictionary<TValue, TKey> Reverse<TKey, TValue>(this IDictionary<TKey, TValue> source)
{
var dictionary = new Dictionary<TValue, TKey>();
foreach (var entry in source)
{
if(!dictionary.ContainsKey(entry.Value))
dictionary.Add(entry.Value, entry.Key);
}
return dictionary;
}
Затем вы можете использовать его следующим образом:
var reversedDictionary = dictionary.Reverse();
var key = reversedDictionary["ab"];
Примечание: если у вас есть повторяющиеся значения, этот метод добавит первый Value
и игнорирует остальные.
Ответ 2
Используйте функцию Linq ToDictionary
:
var reversed = d.ToDictionary(x => x.Value, x => x.Key);
Ниже вы можете видеть, что он работает, как проверено в Linqpad:
var d = new Dictionary<int, string>();
d.Add(1,"one");
d.Add(2,"two");
d.Dump(); //prints it out in linq-pad
var reversed = d.ToDictionary(x => x.Value, x => x.Key);
reversed.Dump(); //prints it out in linq-pad
Ответ 3
Как насчет использования функции linq ToDictionary:
var reversedDictionary = dictionary.ToDictionary(x => x.Value, x => x.Key);
Ответ 4
1) Ключи уникальны, значения нет. Для данного значения у вас есть набор ключей.
2) Поиск по ключу - O(log n)
. Итерация с помощью foreach
или LINQ O(n)
.
Итак,
Вариант A: Итерация с помощью LINQ, потратьте O(n)
на запрос, нет дополнительной памяти.
Вариант B: Поддержание Dictionary<ValueType, HashSet<KeyType>>
, потратьте O(log n)
за запрос, используйте O(n)
дополнительную память. (Есть два субоптима: постройте этот словарь перед серией поисков, поддерживайте его все время)
Ответ 5
Когда фактическое направление поиска словаря (ключ> значение) не представляет интереса, вы можете построить его в другом направлении с нуля. Кроме того, полезен метод расширения для IDictionary (Of TKey, TValue), который облегчает сбор нескольких ключей для одного значения.
Пример
Проблема: в наборе единиц времени каждой из них могут быть присвоены разные имена, например, "s", "sec", "second" или "seconds" для единицы времени "second", и я должен найти примерная единица времени для имени или сокращения.
Мое решение: (это VB.net, но это должно быть легко конвертировать)
Imports System
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices
Public Class TimeUnit
Private ReadOnly _name As String
Public Sub New(ByVal name As String, ByVal avgSpan As TimeSpan)
_name = name
_AvgSpan = avgSpan
End Sub
Public ReadOnly Property AvgSpan() As TimeSpan
Public Overrides Function ToString() As String
Return _name
End Function
End Class
Public Class TimeUnits
Private ReadOnly _items As New Dictionary(Of String, TimeUnit)(37, StringComparer.OrdinalIgnoreCase) _
From {{{"n", "min", "minute", "minutes", "minuten"},
New TimeUnit("Minute", TimeSpan.FromMinutes(1))},
{{"h", "std", "stunde", "hour", "stunden", "hours"},
New TimeUnit("Hour", TimeSpan.FromHours(1))},
{{"d", "t", "day", "tag", "days", "tage"},
New TimeUnit("Day", TimeSpan.FromDays(1))},
{{"m", "mo", "month", "monat", "months", "monate"},
New TimeUnit("Month", TimeSpan.FromDays(30.4375))},
{{"q", "quarter", "quartal", "quarters", "quartale"},
New TimeUnit("Quarter", TimeSpan.FromDays(91.3125))},
{{"y", "yy", "yyy", "yyyy", "j", "year", "jahr", "years", "jahre"},
New TimeUnit("Year", TimeSpan.FromDays(365.25))}}
Public Function GetByAbbreviation(ByVal abbreviation As String) As TimeUnit
Return _items(abbreviation)
End Function
End Class
Public Module ExtensionMethods
<Extension>
Public Sub Add(Of TKey, TValue)(ByVal valItems As IDictionary(Of TKey, TValue), ByVal valKeys As IEnumerable(Of TKey), ByVal valValue As TValue)
For Each tempKey As TKey In valKeys
valItems.Add(tempKey, valValue)
Next
End Sub
End Module
Public Module Main
Public Sub Main()
Dim name As String = "Jahr"
Console.WriteLine(String.Format("The time unit for '{0}' is {1}.", name, (New TimeUnits).GetByAbbreviation(name)))
End Sub
End Module
вывод: единица времени для "Яр" - это год.