Nhận nhiều khóa có giá trị cụ thể của Từ điển chung?


122

Thật dễ dàng để lấy giá trị của khóa từ Từ điển chung .NET:

Dictionary<int, string> greek = new Dictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
string secondGreek = greek[2];  // Beta

Nhưng cố gắng lấy các khóa được cung cấp một giá trị không đơn giản vì có thể có nhiều khóa:

int[] betaKeys = greek.WhatDoIPutHere("Beta");  // expecting single 2

1
Tại sao lại là kiểu trả về int[]khi bạn mong đợi một giá trị?
anar khalilov

3
@Anar, đọc câu trả lời của tôi cho Domenic; “Giá trị trùng lặp khó xảy ra nhưng không phải là không thể”.
Dour High Arch

khóa của một giá trị? Tôi nghĩ rằng bạn có nghĩa là các phím
Max Hodges

Câu trả lời:


144

Được rồi, đây là phiên bản đa hướng:

using System;
using System.Collections.Generic;
using System.Text;

class BiDictionary<TFirst, TSecond>
{
    IDictionary<TFirst, IList<TSecond>> firstToSecond = new Dictionary<TFirst, IList<TSecond>>();
    IDictionary<TSecond, IList<TFirst>> secondToFirst = new Dictionary<TSecond, IList<TFirst>>();

    private static IList<TFirst> EmptyFirstList = new TFirst[0];
    private static IList<TSecond> EmptySecondList = new TSecond[0];

    public void Add(TFirst first, TSecond second)
    {
        IList<TFirst> firsts;
        IList<TSecond> seconds;
        if (!firstToSecond.TryGetValue(first, out seconds))
        {
            seconds = new List<TSecond>();
            firstToSecond[first] = seconds;
        }
        if (!secondToFirst.TryGetValue(second, out firsts))
        {
            firsts = new List<TFirst>();
            secondToFirst[second] = firsts;
        }
        seconds.Add(second);
        firsts.Add(first);
    }

    // Note potential ambiguity using indexers (e.g. mapping from int to int)
    // Hence the methods as well...
    public IList<TSecond> this[TFirst first]
    {
        get { return GetByFirst(first); }
    }

    public IList<TFirst> this[TSecond second]
    {
        get { return GetBySecond(second); }
    }

    public IList<TSecond> GetByFirst(TFirst first)
    {
        IList<TSecond> list;
        if (!firstToSecond.TryGetValue(first, out list))
        {
            return EmptySecondList;
        }
        return new List<TSecond>(list); // Create a copy for sanity
    }

    public IList<TFirst> GetBySecond(TSecond second)
    {
        IList<TFirst> list;
        if (!secondToFirst.TryGetValue(second, out list))
        {
            return EmptyFirstList;
        }
        return new List<TFirst>(list); // Create a copy for sanity
    }
}

class Test
{
    static void Main()
    {
        BiDictionary<int, string> greek = new BiDictionary<int, string>();
        greek.Add(1, "Alpha");
        greek.Add(2, "Beta");
        greek.Add(5, "Beta");
        ShowEntries(greek, "Alpha");
        ShowEntries(greek, "Beta");
        ShowEntries(greek, "Gamma");
    }

    static void ShowEntries(BiDictionary<int, string> dict, string key)
    {
        IList<int> values = dict[key];
        StringBuilder builder = new StringBuilder();
        foreach (int value in values)
        {
            if (builder.Length != 0)
            {
                builder.Append(", ");
            }
            builder.Append(value);
        }
        Console.WriteLine("{0}: [{1}]", key, builder);
    }
}

2
Theo những gì tôi đọc trong msdn, đây không phải là BiLookup thay vì BiDictionary? Không phải là điều quan trọng hoặc bất cứ điều gì, chỉ tò mò muốn nếu tôi hiểu được những điều đúng ở đây là ...
Svish

Ngoài ra, tôi đã sử dụng GetByFirst và lấy lại EmptySecondList, thêm một số thứ vào nó và sau đó gọi lại GetByFirst, tôi sẽ không nhận được danh sách có một số thứ trong đó và không phải là danh sách trống sao?
Svish

@Svish: Không, vì khi bạn cố gắng thêm vào danh sách, nó sẽ tạo ra một ngoại lệ (bạn không thể thêm vào một mảng). Và có, BiLookup có lẽ sẽ là một cái tên tốt hơn.
Jon Skeet

Trong khi tôi thấy điều này trả lời câu hỏi của OP, đây không phải là một triển khai hơi ngây thơ sao? Sẽ không có một triển khai thực tế hơn là Từ điển <> Danh sách <> Từ điển để bạn có thể thực sự tra cứu các đối tượng phong phú bằng 2 phím khác nhau?
Chris Marisic

@ChrisMarisic: Không chắc ý bạn là gì - nhưng những thứ như thế này là thứ tôi đã sử dụng khá nhiều và không cần thêm gì nữa.
Jon Skeet

74

Như mọi người đã nói, không có ánh xạ trong từ điển từ giá trị đến khóa.

Tôi vừa nhận thấy bạn muốn ánh xạ từ giá trị sang nhiều khóa - Tôi đang để giải pháp này ở đây cho phiên bản giá trị duy nhất, nhưng sau đó tôi sẽ thêm một câu trả lời khác cho bản đồ hai chiều nhiều mục nhập.

Cách tiếp cận thông thường để thực hiện ở đây là có hai từ điển - một ánh xạ một chiều và một từ điển kia. Đóng gói chúng trong một lớp riêng biệt và tìm ra những gì bạn muốn làm khi có khóa hoặc giá trị trùng lặp (ví dụ: ném một ngoại lệ, ghi đè mục nhập hiện có hoặc bỏ qua mục nhập mới). Cá nhân tôi có lẽ sẽ đưa ra một ngoại lệ - nó làm cho hành vi thành công dễ xác định hơn. Một cái gì đó như thế này:

using System;
using System.Collections.Generic;

class BiDictionary<TFirst, TSecond>
{
    IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
    IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

    public void Add(TFirst first, TSecond second)
    {
        if (firstToSecond.ContainsKey(first) ||
            secondToFirst.ContainsKey(second))
        {
            throw new ArgumentException("Duplicate first or second");
        }
        firstToSecond.Add(first, second);
        secondToFirst.Add(second, first);
    }

    public bool TryGetByFirst(TFirst first, out TSecond second)
    {
        return firstToSecond.TryGetValue(first, out second);
    }

    public bool TryGetBySecond(TSecond second, out TFirst first)
    {
        return secondToFirst.TryGetValue(second, out first);
    }
}

class Test
{
    static void Main()
    {
        BiDictionary<int, string> greek = new BiDictionary<int, string>();
        greek.Add(1, "Alpha");
        greek.Add(2, "Beta");
        int x;
        greek.TryGetBySecond("Beta", out x);
        Console.WriteLine(x);
    }
}

1
Tôi không nghĩ có bất kỳ lý do gì để làm cho nó bắt nguồn từ một lớp cụ thể - tôi không thích kế thừa mà không cần suy nghĩ cẩn thận - nhưng nó chắc chắn có thể triển khai IEnumerable, v.v. Trên thực tế, nó có thể triển khai IDictionary <TFirst, TSecond> và IDictionary <TSecond, TFirst>.
Jon Skeet

1
(Mặc dù điều đó sẽ khá kỳ lạ nếu TFirst và TSecond giống nhau ...)
Jon Skeet

6
Trên thực tế bạn không thể thực hiện cả hai IDictionary <TFirst, TSecond> và IDictionary <TSecond, TFirst> đồng, .NET 4.0 sẽ không cho phép điều đó
Sebastian

2
@nawfal: Một trong các Addlệnh gọi từ điển sẽ không thành công - nhưng nếu đó là lệnh gọi thứ hai, thì hệ thống đã rơi vào trạng thái nhầm lẫn. Theo cách của tôi, bạn vẫn có một bộ sưu tập nhất quán sau khi ngoại lệ.
Jon Skeet

1
@nawfal: Vâng, tôi không biết liệu đó là lý do tại sao tôi đã làm nó khi lần đầu tiên tôi đã viết câu trả lời ... Tôi đoán;)
Jon Skeet

26

Từ điển không thực sự hoạt động như vậy, bởi vì trong khi tính duy nhất của các khóa được đảm bảo, tính duy nhất của các giá trị thì không. Vì vậy, ví dụ: nếu bạn có

var greek = new Dictionary<int, string> { { 1, "Alpha" }, { 2, "Alpha" } };

Bạn mong đợi nhận được greek.WhatDoIPutHere("Alpha")gì?

Vì vậy, bạn không thể mong đợi một cái gì đó như thế này được cuộn vào khuôn khổ. Bạn cần phương thức của riêng mình cho các mục đích sử dụng độc đáo của riêng bạn --- bạn có muốn trả về một mảng (hoặc IEnumerable<T>) không? Bạn có muốn ném một ngoại lệ nếu có nhiều khóa với giá trị đã cho? Còn nếu không có thì sao?

Cá nhân tôi muốn đi đến một số lượng, như vậy:

IEnumerable<TKey> KeysFromValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TValue val)
{
    if (dict == null)
    {
        throw new ArgumentNullException("dict");
    }
    return dict.Keys.Where(k => dict[k] == val);
}

var keys = greek.KeysFromValue("Beta");
int exceptionIfNotExactlyOne = greek.KeysFromValue("Beta").Single();

Một giải pháp thanh lịch, nhưng điều này phải hoạt động trong 2.0. Các giá trị trùng lặp khó xảy ra nhưng không phải là không thể, trả lại một bộ sưu tập sẽ tốt hơn.
Dour High Arch

23

Có thể cách dễ nhất để làm điều đó, không có Linq, có thể là lặp lại các cặp:

int betaKey; 
foreach (KeyValuePair<int, string> pair in lookup)
{
    if (pair.Value == value)
    {
        betaKey = pair.Key; // Found
        break;
    }
}
betaKey = -1; // Not found

Nếu bạn có Linq, nó có thể thực hiện dễ dàng theo cách này:

int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key;

dour, nhưng bạn có kiểu var ở trên ?! chắc chắn bạn đang ở 3.0? xem bản cập nhật của tôi bên dưới quá.
dove

Xin lỗi, tôi đã sử dụng "var" đơn giản để giảm việc gõ. Tôi không muốn thực hiện tìm kiếm tuyến tính, từ điển có thể lớn.
Dour High Arch

2
varlà một tính năng ngôn ngữ, không phải là một tính năng khung. Bạn có thể sử dụng null-thanescing từ C # -6.0 và vẫn nhắm mục tiêu CF-2.0 nếu bạn thực sự muốn.
binki

3

Từ điển không giữ một hàm băm của các giá trị mà chỉ giữ các khóa, vì vậy bất kỳ tìm kiếm nào trên nó bằng cách sử dụng một giá trị sẽ mất ít nhất thời gian tuyến tính. Đặt cược tốt nhất của bạn là chỉ cần lặp lại các phần tử trong từ điển và theo dõi các khóa phù hợp hoặc chuyển sang cấu trúc dữ liệu khác, có thể duy trì hai từ điển ánh xạ key-> value và value-> List_of_keys. Nếu bạn làm điều này, bạn sẽ đánh đổi dung lượng lưu trữ để lấy tốc độ tra cứu. Sẽ không mất nhiều thời gian để biến ví dụ @Cybis thành một cấu trúc dữ liệu như vậy.


3

Vì tôi muốn có một Từ điển hai chiều chính thức (và không chỉ Bản đồ), tôi đã thêm các chức năng còn thiếu để biến nó thành một lớp tương thích với IDictionary. Điều này dựa trên phiên bản có các Cặp Khóa-Giá trị duy nhất. Đây là tệp nếu muốn (Hầu hết công việc là XMLDoc thông qua):

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Common
{
    /// <summary>Represents a bidirectional collection of keys and values.</summary>
    /// <typeparam name="TFirst">The type of the keys in the dictionary</typeparam>
    /// <typeparam name="TSecond">The type of the values in the dictionary</typeparam>
    [System.Runtime.InteropServices.ComVisible(false)]
    [System.Diagnostics.DebuggerDisplay("Count = {Count}")]
    //[System.Diagnostics.DebuggerTypeProxy(typeof(System.Collections.Generic.Mscorlib_DictionaryDebugView<,>))]
    //[System.Reflection.DefaultMember("Item")]
    public class BiDictionary<TFirst, TSecond> : Dictionary<TFirst, TSecond>
    {
        IDictionary<TSecond, TFirst> _ValueKey = new Dictionary<TSecond, TFirst>();
        /// <summary> PropertyAccessor for Iterator over KeyValue-Relation </summary>
        public IDictionary<TFirst, TSecond> KeyValue => this;
        /// <summary> PropertyAccessor for Iterator over ValueKey-Relation </summary>
        public IDictionary<TSecond, TFirst> ValueKey => _ValueKey;

        #region Implemented members

        /// <Summary>Gets or sets the value associated with the specified key.</Summary>
        /// <param name="key">The key of the value to get or set.</param>
        /// <Returns>The value associated with the specified key. If the specified key is not found,
        ///      a get operation throws a <see cref="KeyNotFoundException"/>, and
        ///      a set operation creates a new element with the specified key.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
        /// The property is retrieved and <paramref name="key"/> does not exist in the collection.</exception>
        /// <exception cref="T:System.ArgumentException"> An element with the same key already
        /// exists in the <see cref="ValueKey"/> <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public new TSecond this[TFirst key]
        {
            get { return base[key]; }
            set { _ValueKey.Remove(base[key]); base[key] = value; _ValueKey.Add(value, key); }
        }

        /// <Summary>Gets or sets the key associated with the specified value.</Summary>
        /// <param name="val">The value of the key to get or set.</param>
        /// <Returns>The key associated with the specified value. If the specified value is not found,
        ///      a get operation throws a <see cref="KeyNotFoundException"/>, and
        ///      a set operation creates a new element with the specified value.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="val"/> is null.</exception>
        /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
        /// The property is retrieved and <paramref name="val"/> does not exist in the collection.</exception>
        /// <exception cref="T:System.ArgumentException"> An element with the same value already
        /// exists in the <see cref="KeyValue"/> <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public TFirst this[TSecond val]
        {
            get { return _ValueKey[val]; }
            set { base.Remove(_ValueKey[val]); _ValueKey[val] = value; base.Add(value, val); }
        }

        /// <Summary>Adds the specified key and value to the dictionary.</Summary>
        /// <param name="key">The key of the element to add.</param>
        /// <param name="value">The value of the element to add.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> is null.</exception>
        /// <exception cref="T:System.ArgumentException">An element with the same key or value already exists in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public new void Add(TFirst key, TSecond value) {
            base.Add(key, value);
            _ValueKey.Add(value, key);
        }

        /// <Summary>Removes all keys and values from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        public new void Clear() { base.Clear(); _ValueKey.Clear(); }

        /// <Summary>Determines whether the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/> contains the specified
        ///      KeyValuePair.</Summary>
        /// <param name="item">The KeyValuePair to locate in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</param>
        /// <Returns>true if the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/> contains an element with
        ///      the specified key which links to the specified value; otherwise, false.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
        public bool Contains(KeyValuePair<TFirst, TSecond> item) => base.ContainsKey(item.Key) & _ValueKey.ContainsKey(item.Value);

        /// <Summary>Removes the specified KeyValuePair from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        /// <param name="item">The KeyValuePair to remove.</param>
        /// <Returns>true if the KeyValuePair is successfully found and removed; otherwise, false. This
        ///      method returns false if <paramref name="item"/> is not found in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
        public bool Remove(KeyValuePair<TFirst, TSecond> item) => base.Remove(item.Key) & _ValueKey.Remove(item.Value);

        /// <Summary>Removes the value with the specified key from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <Returns>true if the element is successfully found and removed; otherwise, false. This
        ///      method returns false if <paramref name="key"/> is not found in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        public new bool Remove(TFirst key) => _ValueKey.Remove(base[key]) & base.Remove(key);

        /// <Summary>Gets the key associated with the specified value.</Summary>
        /// <param name="value">The value of the key to get.</param>
        /// <param name="key">When this method returns, contains the key associated with the specified value,
        ///      if the value is found; otherwise, the default value for the type of the key parameter.
        ///      This parameter is passed uninitialized.</param>
        /// <Returns>true if <see cref="ValueKey"/> contains an element with the specified value; 
        ///      otherwise, false.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="value"/> is null.</exception>
        public bool TryGetValue(TSecond value, out TFirst key) => _ValueKey.TryGetValue(value, out key);
        #endregion
    }
}

2

sửa lại: không sao để có một số loại tìm thấy bạn sẽ cần một cái gì đó khác ngoài từ điển, vì nếu bạn nghĩ về nó, từ điển là một chìa khóa một chiều. nghĩa là, các giá trị có thể không phải là duy nhất

điều đó cho biết có vẻ như bạn đang sử dụng c # 3.0, vì vậy bạn có thể không phải dùng đến vòng lặp và có thể sử dụng một cái gì đó như:

var key = (from k in yourDictionary where string.Compare(k.Value, "yourValue", true)  == 0 select k.Key).FirstOrDefault();

Từ điển không có .FindByValue. Tôi muốn chuyển sang một cấu trúc dữ liệu khác hơn là lặp qua các giá trị.
Dour High Arch

2

Lớp từ điển không được tối ưu hóa cho trường hợp này, nhưng nếu bạn thực sự muốn làm điều đó (trong C # 2.0), bạn có thể làm:

public List<TKey> GetKeysFromValue<TKey, TVal>(Dictionary<TKey, TVal> dict, TVal val)
{
   List<TKey> ks = new List<TKey>();
   foreach(TKey k in dict.Keys)
   {
      if (dict[k] == val) { ks.Add(k); }
   }
   return ks;
}

Tôi thích giải pháp LINQ hơn cho sự thanh lịch, nhưng đây là cách 2.0.


1

Bạn không thể tạo một lớp con của Từ điển có chức năng đó?


    public class MyDict < TKey, TValue > : Dictionary < TKey, TValue >
    {
        private Dictionary < TValue, TKey > _keys;

        public TValue this[TKey key]
        {
            get
            {
                return base[key];
            }
            set 
            { 
                base[key] = value;
                _keys[value] = key;
            }
        }

        public MyDict()
        {
            _keys = new Dictionary < TValue, TKey >();
        }

        public TKey GetKeyFromValue(TValue value)
        {
            return _keys[value];
        }
    }

CHỈNH SỬA: Xin lỗi, không nhận được mã ngay lần đầu tiên.


Điều đó sẽ chỉ chuyển đổi những gì tôi đang sử dụng cho một khóa và chỉ trả về giá trị int của khóa chuỗi, tôi cần phải thực hiện cả hai cách. Và, như Domenic chỉ ra, tôi có thể có các giá trị chuỗi trùng lặp.
Dour High Arch

Nếu bạn có thể có các giá trị chuỗi trùng lặp cho các khóa int của mình, bạn mong đợi nhận được gì khi tra cứu theo chuỗi? Một đối tượng danh sách của int tương ứng?
Cybis

1

Giải pháp từ điển hai chiều "đơn giản" được đề xuất ở đây là phức tạp và có thể khó hiểu, khó duy trì hoặc mở rộng. Cũng là câu hỏi ban đầu yêu cầu "khóa cho một giá trị", nhưng rõ ràng có thể có nhiều khóa (kể từ khi tôi chỉnh sửa câu hỏi). Toàn bộ cách tiếp cận là khá đáng ngờ.

Thay đổi phần mềm. Viết mã dễ bảo trì nên được ưu tiên các cách giải quyết phức tạp "thông minh" khác. Cách lấy lại khóa từ các giá trị trong từ điển là lặp lại. Từ điển không được thiết kế để sử dụng hai chiều.


Hoặc có thể là một từ điển thứ hai ánh xạ từng giá trị với (các) khóa của nó.
DavidRR

Các khóa chỉ @DavidRR phải là duy nhất, vì vậy cách tiếp cận từ điển thứ hai sẽ không thực sự hoạt động. Nhưng bạn có thể chỉ cần lặp lại từ điển để lấy các khóa cho một giá trị.
Max Hodges

Nếu các cuộc gọi vấn đề đối với một cuốn từ điển để hỗ trợ nhiều intgiá trị mỗi stringchìa khóa, sau đó vào từ điển có thể được định nghĩa như thế này: Dictionary<string, List<int>>.
DavidRR

bây giờ làm thế nào để làm cho nó hai chiều mà không lặp lại?
Max Hodges

Đối với câu hỏi của OP với, một tiêu chuẩn Dictionarynào không cung cấp một khả năng hai chiều. Vì vậy, nếu tất cả những gì bạn có là tiêu chuẩn Dictionaryvà bạn muốn tìm (các) khóa được liên kết với một giá trị cụ thể, bạn thực sự phải lặp lại! Tuy nhiên, đối với các từ điển "lớn", việc lặp lại có thể dẫn đến hiệu suất kém. Lưu ý rằng câu trả lời mà bản thân tôi đưa ra dựa trên sự lặp lại (thông qua LINQ). Nếu ban đầu của bạn Dictionarykhông thể thay đổi thêm, bạn có thể tạo bản đảo ngược Dictionarymột lần để tăng tốc độ tra cứu ngược.
DavidRR

1

Sử dụng LINQ để thực hiện Dictionary<K, V>tra cứu ngược lại . Nhưng hãy nhớ rằng các giá trị trong Dictionary<K, V>giá trị của bạn có thể không khác biệt.

Trình diễn:

using System;
using System.Collections.Generic;
using System.Linq;

class ReverseDictionaryLookupDemo
{
    static void Main()
    {
        var dict = new Dictionary<int, string>();
        dict.Add(4, "Four");
        dict.Add(5, "Five");
        dict.Add(1, "One");
        dict.Add(11, "One"); // duplicate!
        dict.Add(3, "Three");
        dict.Add(2, "Two");
        dict.Add(44, "Four"); // duplicate!

        Console.WriteLine("\n== Enumerating Distinct Values ==");
        foreach (string value in dict.Values.Distinct())
        {
            string valueString =
                String.Join(", ", GetKeysFromValue(dict, value));

            Console.WriteLine("{0} => [{1}]", value, valueString);
        }
    }

    static List<int> GetKeysFromValue(Dictionary<int, string> dict, string value)
    {
        // Use LINQ to do a reverse dictionary lookup.
        // Returns a 'List<T>' to account for the possibility
        // of duplicate values.
        return
            (from item in dict
             where item.Value.Equals(value)
             select item.Key).ToList();
    }
}

Đầu ra mong đợi:

== Enumerating Distinct Values ==
Four => [4, 44]
Five => [5]
One => [1, 11]
Three => [3]
Two => [2]

1
Vấn đề tôi thấy với điều này là bạn đang kiểm tra mọi thành phần trong từ điển để có được hướng ngược lại. Thời gian tìm kiếm O (n) đánh bại mục đích của việc sử dụng từ điển; nó phải là O (1).
stephen

@stephen - Đồng ý. Như những người khác đã chỉ ra, nếu hiệu suất là tối quan trọng, thì một từ điển riêng cho các giá trị hoặc một từ điển hai chiều sẽ thích hợp. Tuy nhiên, nếu nhu cầu thực hiện tra cứu giá trị là không thường xuyên và hiệu suất làm như vậy có thể chấp nhận được, thì cách tiếp cận mà tôi phác thảo ở đây có thể đáng được xem xét. Điều đó nói rằng, việc sử dụng LINQ trong câu trả lời của tôi không tương thích với mong muốn của OP về một giải pháp phù hợp để sử dụng với .NET 2.0. (Mặc dù hạn chế .NET 2.0 được cho là ít có khả năng xảy ra hơn trong năm 2014.)
DavidRR

1
Dictionary<string, string> dic = new Dictionary<string, string>();
dic["A"] = "Ahmed";
dic["B"] = "Boys";

foreach (string mk in dic.Keys)
{
    if(dic[mk] == "Ahmed")
    {
        Console.WriteLine("The key that contains \"Ahmed\" is " + mk);
    }
}

1
Cảm ơn đã đăng một câu trả lời! Trong khi một đoạn mã có thể trả lời câu hỏi nó vẫn còn tuyệt vời để thêm một số thông tin bổ sung xung quanh, như giải thích, vv ..
j0k

0

Như một bước ngoặt của câu trả lời được chấp nhận ( https://stackoverflow.com/a/255638/986160 ) giả định rằng các khóa sẽ được liên kết với các giá trị dấu hiệu trong từ điển. Tương tự như ( https://stackoverflow.com/a/255630/986160 ) nhưng thanh lịch hơn một chút. Tính mới là ở chỗ lớp tiêu thụ có thể được sử dụng như một sự thay thế kiểu liệt kê (nhưng đối với chuỗi cũng vậy) và từ điển triển khai IEnumerable.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MyApp.Dictionaries
{

    class BiDictionary<TFirst, TSecond> : IEnumerable
    {
        IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
        IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

        public void Add(TFirst first, TSecond second)
        {
            firstToSecond.Add(first, second);
            secondToFirst.Add(second, first);
        }

        public TSecond this[TFirst first]
        {
            get { return GetByFirst(first); }
        }

        public TFirst this[TSecond second]
        {
            get { return GetBySecond(second); }
        }

        public TSecond GetByFirst(TFirst first)
        {
            return firstToSecond[first];
        }

        public TFirst GetBySecond(TSecond second)
        {
            return secondToFirst[second];
        }

        public IEnumerator GetEnumerator()
        {
            return GetFirstEnumerator();
        }

        public IEnumerator GetFirstEnumerator()
        {
            return firstToSecond.GetEnumerator();
        }

        public IEnumerator GetSecondEnumerator()
        {
            return secondToFirst.GetEnumerator();
        }
    }
}

Và với tư cách là một tầng lớp tiêu thụ, bạn có thể có

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyApp.Dictionaries
{
    class Greek
    {

        public static readonly string Alpha = "Alpha";
        public static readonly string Beta = "Beta";
        public static readonly string Gamma = "Gamma";
        public static readonly string Delta = "Delta";


        private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();


        static Greek() {
            Dictionary.Add(1, Alpha);
            Dictionary.Add(2, Beta);
            Dictionary.Add(3, Gamma);
            Dictionary.Add(4, Delta);
        }

        public static string getById(int id){
            return Dictionary.GetByFirst(id);
        }

        public static int getByValue(string value)
        {
            return Dictionary.GetBySecond(value);
        }

    }
}

1
Về cơ bản, điều này giống với một câu trả lời được đăng cách đây sáu năm và như đã lưu ý, các khóa không được liên kết với các giá trị đơn lẻ. Mỗi khóa có thể có nhiều giá trị.
Dour High Arch

Tôi biết nhưng phiên bản của tôi triển khai IEnumerable và thanh lịch hơn .. Thêm vào đó, ví dụ về lớp tiêu thụ đặt lớp BiDictionary lên một cấp độ khả dụng khác - nó giải quyết vấn đề về các chuỗi và id tĩnh không được cung cấp bởi C #. Tôi cũng tham khảo nó nếu bạn đọc câu trả lời của tôi!
Michail Michailidis

0

Sau đó, giải pháp của giáo dân

Một hàm tương tự như bên dưới có thể được viết để tạo một từ điển như vậy:

    public Dictionary<TValue, TKey> Invert(Dictionary<TKey, TValue> dict) {
    Dictionary<TValue, TKey> ret = new Dictionary<TValue, TKey>();
    foreach (var kvp in dict) {ret[kvp.value] = kvp.key;} return ret; }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.