lấy khóa từ điển theo giá trị


361

Làm cách nào để lấy khóa Từ điển theo giá trị trong C #?

Dictionary<string, string> types = new Dictionary<string, string>()
{
            {"1", "one"},
            {"2", "two"},
            {"3", "three"}
};

Tôi muốn một cái gì đó như thế này:

getByValueKey(string value);

getByValueKey("one")phải trở về "1".

Cách tốt nhất để làm điều này là gì? Có lẽ HashTable, SortedLists?


9
Bản sao chính xác: stackoverflow.com/questions/255341
Gabe

Tôi đọc bài viết này trước đây, nhưng trả lời có được.
loviji

5
Có, nhưng ở đó bạn nhận được một câu trả lời được chấp nhận từ Skeet .
ruffin

7
Câu trả lời được chấp nhận ở đây tốt hơn nhiều so với tất cả mọi thứ ở câu hỏi trùng lặp. Nhưng câu hỏi đó cũ hơn; có lẽ biểu cảm lambda không tồn tại khi Jon trả lời.
Seth Battin

5
Mở lại câu hỏi này vì câu hỏi khác đề cập cụ thể đến .Net 2.0, trong khi câu hỏi này không và có câu trả lời tốt hơn cho phiên bản hiện tại của khung .Net.
Rachel

Câu trả lời:


645

Các giá trị không nhất thiết phải là duy nhất nên bạn phải thực hiện tra cứu. Bạn có thể làm một cái gì đó như thế này:

var myKey = types.FirstOrDefault(x => x.Value == "one").Key;

Nếu các giá trị là duy nhất và được chèn ít thường xuyên hơn đọc, thì hãy tạo một từ điển nghịch đảo trong đó các giá trị là khóa và khóa là giá trị.


3
@loviji: Hãy nhớ rằng trong giải pháp lặp, nếu giá trị xảy ra ở cuối từ điển, nó sẽ phải đi qua tất cả các giá trị khác để tìm thấy nó. Nếu bạn có một số mục, điều này sẽ làm chậm chương trình của bạn.
Zach Johnson

2
@Zach Johnson: Cảm ơn. tôi đồng ý với bạn. và câu trả lời của bạn tôi cũng thích nhưng trong từ điển của tôi 8-10 mục. và họ không được thêm động. và tôi nghĩ rằng, sử dụng câu trả lời này không phải là giải pháp tồi.
loviji

4
Am i thiếu cái gì ở đây? Đoạn mã trên trả về giá trị, không phải khóa. Sẽ không gõ.FirstOrDefault (x => x.Value == "one"). Khóa nào phù hợp hơn?
floele

19
Cảnh báo cho tất cả, câu trả lời được chấp nhận khi có các chỉnh sửa sẽ đưa ra một ngoại lệ nếu FirstOrDefault không tìm thấy kết quả trùng khớp và cố gắng truy cập "Khóa" trên đối tượng null.
Jim Yarbro

11
@JimYarbro: vì KeyValuePair<Tkey,Tvalue>là một cấu trúc, vì vậy một loại giá trị, nó không bao giờ có thể null. FirstOrDefaultsẽ trả về một thể hiện trong đó tất cả các trường được khởi tạo với giá trị mặc định của chúng (như nullđối với chuỗi hoặc 0 đối với số nguyên). Vì vậy, bạn sẽ không nhận được một ngoại lệ. Nhưng bạn cũng không biết nếu bạn đã tìm thấy một giá trị, vì vậy câu trả lời này không bao gồm trường hợp giá trị đó không tồn tại.
Tim Schmelter

26

Bạn có thể làm điều đó:

  1. Bằng cách lặp qua tất cả các KeyValuePair<TKey, TValue>từ trong từ điển (đây sẽ là một cú đánh hiệu suất khá lớn nếu bạn có một số mục trong từ điển)
  2. Sử dụng hai từ điển, một cho ánh xạ giá trị-khóa và một cho ánh xạ khóa-giá trị (sẽ chiếm gấp đôi không gian trong bộ nhớ).

Sử dụng Phương pháp 1 nếu hiệu suất không phải là một sự cân nhắc, sử dụng Phương pháp 2 nếu bộ nhớ không phải là một sự cân nhắc.

Ngoài ra, tất cả các khóa phải là duy nhất, nhưng các giá trị không bắt buộc phải là duy nhất. Bạn có thể có nhiều hơn một khóa với giá trị được chỉ định.

Có bất kỳ lý do nào bạn không thể đảo ngược mối quan hệ khóa-giá trị?


1
Để tạo từ điển nghịch đảo theo chương trình, chúng ta vẫn cần sử dụng phương pháp 1, phải không?
Kyle Delaney

Nếu đây là một sự xuất hiện phổ biến thì tôi cũng sẽ đề nghị trao đổi này (liên quan đến câu hỏi cuối cùng của bạn).
Bonez024

3

Tôi đã ở trong một tình huống mà Linq ràng buộc không có sẵn và phải mở rộng lambda một cách rõ ràng. Nó dẫn đến một chức năng đơn giản:

public static T KeyByValue<T, W>(this Dictionary<T, W> dict, W val)
{
    T key = default;
    foreach (KeyValuePair<T, W> pair in dict)
    {
        if (EqualityComparer<W>.Default.Equals(pair.Value, val))
        {
            key = pair.Key;
            break;
        }
    }
    return key;
}

Gọi nó như sau:

public static void Main()
{
    Dictionary<string, string> dict = new Dictionary<string, string>()
    {
        {"1", "one"},
        {"2", "two"},
        {"3", "three"}
    };

    string key = KeyByValue(dict, "two");       
    Console.WriteLine("Key: " + key);
}

Hoạt động trên .NET 2.0 và trong các môi trường hạn chế khác.


Thêm nó làm phương thức mở rộng sẽ tốt hơn :-)
Chayim Friedman

-1

có thể một cái gì đó như thế này:

foreach (var keyvaluepair in dict)
{
    if(Object.ReferenceEquals(keyvaluepair.Value, searchedObject))
    {
        //dict.Remove(keyvaluepair.Key);
        break;
    }
}

-1

Tôi đã tạo một lớp tra cứu kép:

/// <summary>
/// dictionary with double key lookup
/// </summary>
/// <typeparam name="T1">primary key</typeparam>
/// <typeparam name="T2">secondary key</typeparam>
/// <typeparam name="TValue">value type</typeparam>
public class cDoubleKeyDictionary<T1, T2, TValue> {
    private struct Key2ValuePair {
        internal T2 key2;
        internal TValue value;
    }
    private Dictionary<T1, Key2ValuePair> d1 = new Dictionary<T1, Key2ValuePair>();
    private Dictionary<T2, T1> d2 = new Dictionary<T2, T1>();

    /// <summary>
    /// add item
    /// not exacly like add, mote like Dictionary[] = overwriting existing values
    /// </summary>
    /// <param name="key1"></param>
    /// <param name="key2"></param>
    public void Add(T1 key1, T2 key2, TValue value) {
        lock (d1) {
            d1[key1] = new Key2ValuePair {
                key2 = key2,
                value = value,
            };
            d2[key2] = key1;
        }
    }

    /// <summary>
    /// get key2 by key1
    /// </summary>
    /// <param name="key1"></param>
    /// <param name="key2"></param>
    /// <returns></returns>
    public bool TryGetValue(T1 key1, out TValue value) {
        if (d1.TryGetValue(key1, out Key2ValuePair kvp)) {
            value = kvp.value;
            return true;
        } else {
            value = default;
            return false;
        }
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetValue2(T2 key2, out TValue value) {
        if (d2.TryGetValue(key2, out T1 key1)) {
            return TryGetValue(key1, out value);
        } else {
            value = default;
            return false;
        }
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetKey1(T2 key2, out T1 key1) {
        return d2.TryGetValue(key2, out key1);
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetKey2(T1 key1, out T2 key2) {
        if (d1.TryGetValue(key1, out Key2ValuePair kvp1)) {
            key2 = kvp1.key2;
            return true;
        } else {
            key2 = default;
            return false;
        }
    }

    /// <summary>
    /// remove item by key 1
    /// </summary>
    /// <param name="key1"></param>
    public void Remove(T1 key1) {
        lock (d1) {
            if (d1.TryGetValue(key1, out Key2ValuePair kvp)) {
                d1.Remove(key1);
                d2.Remove(kvp.key2);
            }
        }
    }

    /// <summary>
    /// remove item by key 2
    /// </summary>
    /// <param name="key2"></param>
    public void Remove2(T2 key2) {
        lock (d1) {
            if (d2.TryGetValue(key2, out T1 key1)) {
                d1.Remove(key1);
                d2.Remove(key2);
            }
        }
    }

    /// <summary>
    /// clear all items
    /// </summary>
    public void Clear() {
        lock (d1) {
            d1.Clear();
            d2.Clear();
        }
    }

    /// <summary>
    /// enumerator on key1, so we can replace Dictionary by cDoubleKeyDictionary
    /// </summary>
    /// <param name="key1"></param>
    /// <returns></returns>
    public TValue this[T1 key1] {
        get => d1[key1].value;
    }

    /// <summary>
    /// enumerator on key1, so we can replace Dictionary by cDoubleKeyDictionary
    /// </summary>
    /// <param name="key1"></param>
    /// <returns></returns>
    public TValue this[T1 key1, T2 key2] {
        set {
            lock (d1) {
                d1[key1] = new Key2ValuePair {
                    key2 = key2,
                    value = value,
                };
                d2[key2] = key1;
            }
        }
    }

-3
types.Values.ToList().IndexOf("one");

Values.ToList () chuyển đổi giá trị từ điển của bạn thành Danh sách các đối tượng. IndexOf ("một") tìm kiếm Danh sách mới của bạn đang tìm kiếm "một" và trả về Chỉ mục phù hợp với chỉ mục của cặp Khóa / Giá trị trong từ điển.

Phương pháp này không quan tâm đến các khóa từ điển, nó chỉ đơn giản trả về chỉ mục của giá trị mà bạn đang tìm kiếm.

Hãy nhớ rằng có thể có nhiều hơn một giá trị "một" trong từ điển của bạn. Và đó là lý do không có phương pháp "lấy chìa khóa".


-4

Mã bên dưới chỉ hoạt động nếu Nó chứa dữ liệu giá trị duy nhất

public string getKey(string Value)
{
    if (dictionary.ContainsValue(Value))
    {
        var ListValueData=new List<string>();
        var ListKeyData = new List<string>();

        var Values = dictionary.Values;
        var Keys = dictionary.Keys;

        foreach (var item in Values)
        {
            ListValueData.Add(item);
        }

        var ValueIndex = ListValueData.IndexOf(Value);
        foreach (var item in Keys)
        {
            ListKeyData.Add(item);
        }

        return  ListKeyData[ValueIndex];

    }
    return string.Empty;
}

3
-1 Quá nhiều mã cho một hiệu suất sẽ tệ nhất so với câu trả lời hàng đầu từ Kimi (đã được đăng 6 năm trước bạn). Bạn không cần phải cung cấp các thuộc tính Khóa và Giá trị để tạo 2 danh sách đó (Danh sách của Linq sẽ làm điều đó cho bạn). Ngoài ra, nếu bạn định sử dụng IndexOf, bạn có thể tránh cuộc gọi đến ContainsValue (do đó tránh được 2 vòng lặp mặc dù tất cả các yếu tố cho cùng một nhiệm vụ).
Mariano Desanze

2
Hiệu suất của đề nghị này chỉ là khủng khiếp. Bạn cũng có thể tạo một lớp chung với hai Từ điển. Một trong số đó giữ Key1 và Key2, và cái còn lại giữ Key2 và Key1. Bằng cách này, bạn có thể nhận được một trong hai khóa mà không cần ... à ... mọi thứ mà câu trả lời của bạn đề xuất.
Krythic

-11

Tôi có cách rất đơn giản để làm điều này. Nó làm việc hoàn hảo cho tôi.

Dictionary<string, string> types = new Dictionary<string, string>();

types.Add("1", "one");
types.Add("2", "two");
types.Add("3", "three");

Console.WriteLine("Please type a key to show its value: ");
string rLine = Console.ReadLine();

if(types.ContainsKey(rLine))
{
    string value_For_Key = types[rLine];
    Console.WriteLine("Value for " + rLine + " is" + value_For_Key);
}

3
Xin lỗi, nhưng câu trả lời của bạn không đáp ứng được câu hỏi. Câu hỏi là về cách tìm khóa theo giá trị, câu trả lời của bạn cho thấy tiêu chuẩn: tìm giá trị theo khóa
Breeze

1
Đọc nhiệm vụ trước, lần sau
Tommix

4
Và điều này, thưa quý vị và các bạn, chính xác là lý do tại sao chúng ta đọc câu hỏi trước khi đăng câu trả lời.
Krythic
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.