รับคีย์พจนานุกรมตามค่า


361

ฉันจะรับรหัสพจนานุกรมตามค่าใน C # ได้อย่างไร

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

ฉันต้องการสิ่งนี้:

getByValueKey(string value);

getByValueKey("one")"1"จะต้องกลับมา

วิธีที่ดีที่สุดในการทำเช่นนี้คืออะไร? อาจจะ HashTable, SortedLists?


9
สำเนาซ้ำกันทั้งหมด: stackoverflow.com/questions/255341
Gabe

ฉันอ่านบทความนี้มาก่อน แต่ตอบให้ได้
loviji

5
ใช่ แต่คุณได้รับคำตอบที่ยอมรับจาก Skeetแล้ว
ruffin

7
คำตอบที่ยอมรับได้ที่นี่ดีกว่าทุกอย่างอย่างซ้ำซ้อนในคำถามที่ซ้ำกัน แต่คำถามนั้นเก่ากว่า บางทีการแสดงออกแลมบ์ดาอาจไม่มีอยู่เมื่อจอนตอบ
เซทแบ

5
การเปิดคำถามนี้อีกครั้งเนื่องจากอีกคำถามหนึ่งระบุที่อยู่เฉพาะเป็น. NET 2.0 ในขณะที่คำถามนี้ไม่ได้และมีคำตอบที่ดีกว่าสำหรับรุ่นปัจจุบันของกรอบงาน. Net
Rachel

คำตอบ:


645

ค่าไม่จำเป็นต้องไม่ซ้ำกันดังนั้นคุณต้องทำการค้นหา คุณสามารถทำสิ่งนี้:

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

หากค่าไม่ซ้ำกันและมีการแทรกน้อยกว่าการอ่านให้สร้างพจนานุกรมผกผันที่ค่าเป็นคีย์และคีย์เป็นค่า


3
@loviji: โปรดจำไว้ว่าในการวนลูปหากค่าเกิดขึ้นเมื่อสิ้นสุดพจนานุกรมมันจะต้องข้ามค่าอื่น ๆ ทั้งหมดเพื่อค้นหามัน หากคุณมีรายการจำนวนมากสิ่งนี้จะทำให้โปรแกรมของคุณช้าลง
Zach Johnson

2
@ Zach Johnson: ขอบคุณ ฉันเห็นด้วยกับคุณ. และคำตอบของคุณก็เช่นกัน แต่ในพจนานุกรม 8-10 รายการของฉัน และจะไม่ถูกเพิ่มแบบไดนามิก และฉันคิดว่าการใช้คำตอบนี้ไม่ใช่วิธีแก้ปัญหาที่ไม่ดี
loviji

4
ฉันทำอะไรบางอย่างหายไปหรือเปล่า โค้ดด้านบนส่งคืนค่าไม่ใช่รหัส จะไม่พิมพ์FirstOrDefault (x => x.Value == "one") รหัสจะเหมาะสมกว่าหรือไม่
floele

19
คำเตือนสำหรับทุกคนคำตอบที่ยอมรับเนื่องจากมีการแก้ไขจะทำให้เกิดข้อยกเว้นหาก FirstOrDefault ไม่พบการแข่งขันและพยายามเข้าถึง "คีย์" บนวัตถุว่าง
Jim Yarbro

11
@JimYarbro: เนื่องจากKeyValuePair<Tkey,Tvalue>เป็นโครงสร้างดังนั้นประเภทของค่าจึงไม่มีทางเป็นnullไปได้ FirstOrDefaultจะส่งคืนอินสแตนซ์ที่ฟิลด์ทั้งหมดถูกเริ่มต้นด้วยค่าเริ่มต้น (เช่นnullสำหรับสตริงหรือ 0 สำหรับ int) ดังนั้นคุณจะไม่ได้รับข้อยกเว้น แต่คุณไม่ทราบด้วยว่าคุณพบคุณค่าหรือไม่ดังนั้นคำตอบนี้ไม่ครอบคลุมกรณีที่ไม่มีคุณค่า
Tim Schmelter

26

คุณสามารถทำได้:

  1. โดยการวนลูปทั้งหมดKeyValuePair<TKey, TValue>ในพจนานุกรม (ซึ่งจะเป็นผลการดำเนินงานที่มีขนาดใหญ่มากหากคุณมีจำนวนรายการในพจนานุกรม)
  2. ใช้พจนานุกรมสองพจนานุกรมฉบับหนึ่งสำหรับการทำแผนที่แบบมูลค่าต่อคีย์และอีกหนึ่งพจนานุกรมสำหรับการทำแผนที่แบบคีย์ต่อมูลค่า

ใช้วิธีที่ 1 หากประสิทธิภาพไม่ใช่ข้อควรพิจารณาให้ใช้วิธีที่ 2 หากหน่วยความจำไม่ใช่ข้อควรพิจารณา

นอกจากนี้ปุ่มทั้งหมดจะต้องไม่ซ้ำกัน แต่ค่าไม่จำเป็นต้องเป็นเอกลักษณ์ คุณอาจมีคีย์มากกว่าหนึ่งคีย์พร้อมค่าที่ระบุ

มีเหตุผลใดที่คุณไม่สามารถย้อนกลับความสัมพันธ์ของคีย์ - ค่าได้หรือไม่


1
ในการสร้างพจนานุกรมผกผันทางโปรแกรมเรายังต้องใช้วิธีที่ 1 ใช่ไหม
Kyle Delaney

หากนี่เป็นเหตุการณ์ที่เกิดขึ้นทั่วไปฉันจะแนะนำการแลกเปลี่ยนนี้เช่นกัน (เกี่ยวกับคำถามสุดท้ายของคุณ)
Bonez024

3

ฉันอยู่ในสถานการณ์ที่การผูก Linq ไม่พร้อมใช้งานและต้องขยายแลมบ์ดาอย่างชัดเจน มันส่งผลให้ฟังก์ชั่นที่เรียบง่าย:

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;
}

เรียกว่าชอบดังนี้:

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);
}

ทำงานบน. NET 2.0 และในสภาพแวดล้อมที่ จำกัด อื่น ๆ


การเพิ่มเป็นวิธีการขยายเป็นสิ่งที่ดีกว่า :-)
Chayim Friedman

-1

อาจจะเป็นสิ่งนี้:

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

-1

ฉันได้สร้างคลาสค้นหาสองครั้ง:

/// <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 () แปลงค่าพจนานุกรมของคุณเป็นรายการของวัตถุ IndexOf ("หนึ่ง") ค้นหารายการใหม่ของคุณค้นหา "หนึ่ง" และส่งกลับดัชนีซึ่งจะตรงกับดัชนีของคู่คีย์ / ค่าในพจนานุกรม

วิธีนี้ไม่สนใจเกี่ยวกับปุ่มพจนานุกรมมันเพียงแค่ส่งกลับดัชนีของค่าที่คุณกำลังมองหา

โปรดทราบว่าอาจมีค่ามากกว่าหนึ่งค่าในพจนานุกรมของคุณ และนั่นคือเหตุผลที่ไม่มีวิธี "รับกุญแจ"


-4

รหัสด้านล่างใช้งานได้เฉพาะหากมีข้อมูลค่าที่ไม่ซ้ำ

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 โค้ดมากเกินไปสำหรับการแสดงที่จะแย่ที่สุดกว่าคำตอบยอดนิยมจาก Kimi (ซึ่งโพสต์เมื่อ 6 ปีก่อนหน้าคุณ) คุณไม่จำเป็นต้องละคุณสมบัติคีย์และค่าเพื่อสร้าง 2 รายการเหล่านั้น (ToList ของ Linq จะทำเพื่อคุณ) นอกจากนี้หากคุณกำลังจะใช้ IndexOf คุณสามารถหลีกเลี่ยงการเรียกไปยัง containValue (ดังนั้นการหลีกเลี่ยง 2 ลูปแม้ว่าองค์ประกอบทั้งหมดสำหรับงานเดียวกัน)
Mariano Desanze

2
ประสิทธิภาพของคำแนะนำนี้แย่มาก คุณอาจสร้างคลาสทั่วไปด้วยพจนานุกรมสองรายการ หนึ่งในนั้นมี Key1 และ Key2 และอีกอันหนึ่งที่มี Key2 และ Key1 วิธีนี้คุณสามารถรับรหัสได้โดยไม่ต้อง ... ดี ... ทุกอย่างที่คำตอบของคุณแนะนำ
Krythic

-12

ฉันมีวิธีง่าย ๆ ในการทำเช่นนี้ มันสมบูรณ์แบบสำหรับฉัน

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
ขออภัยคำตอบของคุณไม่ตรงกับคำถาม คำถามคือเกี่ยวกับวิธีค้นหาคีย์ด้วยค่าคำตอบของคุณแสดงมาตรฐาน: ค้นหาค่าด้วยคีย์
Breeze

1
อ่านภารกิจก่อนในครั้งต่อไป
Tommix

4
และนี่คือสุภาพสตรีและสุภาพบุรุษเป็นเหตุผลที่เราอ่านคำถามก่อนโพสต์คำตอบ
Krythic
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.