Как сделать обратный поиск словаря

У меня есть словарь типа <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

вывод: единица времени для "Яр" - это год.