วิธีที่ดีที่สุดในการวนซ้ำในพจนานุกรมคืออะไร


2625

ฉันเห็นวิธีต่าง ๆ ในการทำซ้ำพจนานุกรมใน C # มีวิธีมาตรฐานหรือไม่


108
ฉันประหลาดใจที่มีคำตอบสำหรับคำถามนี้มากมายพร้อมกับ upvoted หนึ่งครั้งที่ 923 .. (ใช้ foreach ofcourse) .. ฉันจะเถียงหรืออย่างน้อยก็เพิ่มว่าถ้าคุณต้องทำซ้ำมากกว่าพจนานุกรมโอกาสที่คุณจะเป็น การใช้มันอย่างไม่ถูกต้อง / ไม่เหมาะสม .. ฉันต้องแสดงความคิดเห็นนี้เพราะฉันเคยเห็นพจนานุกรมที่ถูกทารุณกรรมในลักษณะที่ IMHO ไม่เหมาะสม ... ใช่อาจมีสถานการณ์ที่หายากเมื่อคุณย้ำพจนานุกรมแทนที่จะค้นหาซึ่งเป็นสิ่งที่ มันถูกออกแบบมาสำหรับ .. โปรดจำไว้ก่อนที่จะสงสัยว่าจะทำซ้ำมากกว่าพจนานุกรม
Vikas Gupta

74
@VikasGupta คุณอยากแนะนำอะไรให้ทำบางอย่างกับคอลเลกชันของคู่คีย์ - ค่าเมื่อคุณไม่รู้ว่าคีย์จะเป็นอย่างไร
nasch

26
@displayName หากคุณต้องการทำบางสิ่งบางอย่างกับคู่คีย์ - ค่าแต่ละอัน แต่ไม่มีการอ้างอิงถึงคีย์ที่ใช้ในการค้นหาค่าคุณจะทำซ้ำในพจนานุกรมใช่ไหม ฉันแค่ชี้ให้เห็นว่าอาจมีหลายครั้งที่คุณต้องการทำสิ่งนั้นแม้ว่า Vikas จะอ้างว่านี่เป็นการใช้งานที่ไม่ถูกต้อง
nasch

34
หากต้องการบอกว่าการใช้งานที่ไม่ถูกต้องหมายถึงมีทางเลือกอื่น ทางเลือกนั้นคืออะไร?
Kyle Delaney

40
VikasGupta ผิดฉันสามารถยืนยันได้ว่าหลังจากหลายปีของการเขียนโปรแกรม C # และ C ++ ประสิทธิภาพสูงในสถานการณ์ที่ไม่ใช่เชิงทฤษฎี มีหลายกรณีที่คนหนึ่งจะสร้างพจนานุกรมเก็บคู่คีย์ - ค่าที่ไม่ซ้ำกันแล้ววนซ้ำค่าเหล่านี้ซึ่งพิสูจน์แล้วว่ามีคีย์ที่ไม่ซ้ำกันภายในคอลเลกชัน การสร้างคอลเลกชันเพิ่มเติมใด ๆ เป็นวิธีที่ไม่มีประสิทธิภาพและมีค่าใช้จ่ายสูงในการหลีกเลี่ยงการวนซ้ำของพจนานุกรม โปรดให้ทางเลือกที่ดีในการตอบคำถามเพื่อชี้แจงมุมมองของคุณมิฉะนั้นความคิดเห็นของคุณนั้นไร้สาระ
sɐunıɔןɐqɐp

คำตอบ:


3711
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

29
ถ้าฉันไม่รู้ประเภทของคีย์ / ค่าในพจนานุกรม การใช้var entryดีกว่าในกรณีนั้นและดังนั้นฉันจึงโหวตคำตอบนี้ในการค้นหาครั้งที่สองแทนที่จะใช้คำตอบข้างต้น
Ozair Kafray

93
@OzairKafray ใช้varเมื่อคุณไม่ทราบว่าประเภทนี้เป็นแนวปฏิบัติที่ไม่ดี
เนท

52
คำตอบนี้ดีกว่าเพราะ Pablo ไม่ได้ใช้ค่าเริ่มต้นสำหรับการใช้งาน "var" แบบสันหลังยาวที่ทำให้การกลับมาประเภทนั้นงงงวย
MonkeyWrench

119
@MonkeyWrench: Meh Visual Studio รู้ประเภทของสิ่งนั้น สิ่งที่คุณต้องทำคือเลื่อนเมาส์ไปเหนือตัวแปรเพื่อค้นหา
Robert Harvey

31
ตามที่ฉันเข้าใจมันจะvarทำงานเฉพาะในกรณีที่รู้ว่ารวบรวมในเวลารวบรวม ถ้า Visual Studio รู้ประเภทแล้วก็พร้อมให้คุณค้นหาเช่นกัน
Kyle Delaney

866

หากคุณกำลังพยายามใช้พจนานุกรมทั่วไปใน C # เช่นคุณจะต้องใช้อาเรย์แบบเชื่อมโยงในภาษาอื่น:

foreach(var item in myDictionary)
{
  foo(item.Key);
  bar(item.Value);
}

หรือถ้าคุณจำเป็นต้องทำซ้ำมากกว่าชุดของคีย์ให้ใช้

foreach(var item in myDictionary.Keys)
{
  foo(item);
}

และสุดท้ายหากคุณสนใจเฉพาะค่า:

foreach(var item in myDictionary.Values)
{
  foo(item);
}

(โปรดทราบว่าvarคำหลักนั้นเป็นคุณสมบัติเสริม C # 3.0 และสูงกว่าคุณยังสามารถใช้ประเภท / คีย์ที่แน่นอนได้ที่นี่)


11
คุณลักษณะ var เป็นสิ่งที่จำเป็นที่สุดสำหรับการบล็อกโค้ดครั้งแรกของคุณ :)
nawfal

27
ฉันขอขอบคุณที่คำตอบนี้ชี้ให้เห็นว่าคุณสามารถทำซ้ำปุ่มหรือค่าอย่างชัดเจน
Rotsiser Mho

11
ฉันไม่ชอบการใช้ var ที่นี่ ระบุว่ามันเป็นเพียงน้ำตาลประโยคทำไมใช้ที่นี่? เมื่อมีคนพยายามอ่านรหัสพวกเขาจะต้องข้ามไปรอบ ๆ รหัสเพื่อกำหนดประเภทของmyDictionary(ยกเว้นว่าเป็นชื่อจริงของหลักสูตร) ฉันคิดว่าการใช้ var เป็นสิ่งที่ดีเมื่อประเภทชัดเจนเช่นvar x = "some string"แต่เมื่อไม่ชัดเจนทันทีฉันคิดว่ามันเป็นโค้ดที่ขี้เกียจที่ทำร้ายผู้อ่าน / ผู้ตรวจสอบรหัส
James Wierzba

8
varควรใช้เท่าที่จำเป็นในความคิดของฉัน โดยเฉพาะอย่างยิ่งที่นี่ไม่สร้างสรรค์: ประเภทKeyValuePairนี้มีแนวโน้มที่เกี่ยวข้องกับคำถาม
สินใจ

3
varมีจุดประสงค์ที่ไม่เหมือนใครและฉันไม่เชื่อว่ามันจะเป็น 'น้ำตาล' syntactic การใช้อย่างตั้งใจเป็นวิธีการที่เหมาะสม
Joshua K

159

ในบางกรณีคุณอาจต้องมีตัวนับที่อาจได้รับจากการใช้งาน for-loop สำหรับการที่ LINQ ให้ElementAtซึ่งช่วยให้ต่อไปนี้:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}

21
ในการใช้วิธีการ '.ElementAt' โปรดจำไว้ว่า: ใช้ System.Linq; นี่ไม่รวมอยู่ใน fx คลาสทดสอบที่สร้างขึ้นโดยอัตโนมัติ
Tinia

14
นี่คือวิธีที่จะไปหากคุณกำลังแก้ไขค่าที่เกี่ยวข้องกับกุญแจ มิฉะนั้นจะมีข้อผิดพลาดเกิดขึ้นเมื่อแก้ไขและใช้ foreach ()
Mike de Klerk

27
ไม่ElementAtO (n) การดำเนินงาน?
Arturo Torres Sánchez

162
คำตอบนี้ไม่สมควรอย่างยิ่งสำหรับผู้อัปโหลดจำนวนมาก พจนานุกรมไม่มีลำดับโดยนัยดังนั้นการใช้.ElementAtในบริบทนี้อาจนำไปสู่ข้อบกพร่องเล็กน้อย ร้ายแรงยิ่งกว่านั้นคือประเด็นของอาร์ตูโรข้างต้น คุณจะต้องทำซ้ำdictionary.Count + 1เวลาในพจนานุกรมที่นำไปสู่ความซับซ้อน O (n ^ 2) สำหรับการดำเนินการที่ควรเป็น O (n) เท่านั้น หากคุณต้องการดัชนีจริงๆ (ถ้าคุณทำคุณอาจใช้ประเภทการรวบรวมที่ไม่ถูกต้องในตอนแรก) คุณควรทำซ้ำdictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})แทนและไม่ใช้.ElementAtภายในลูป
อะไรต่อมิอะไร

5
การดำเนินการ ElementAt - o (n)! อย่างจริงจัง? นี่คือตัวอย่างของวิธีที่คุณไม่ควรทำ upvotes มากมายเหล่านี้?
Mukesh Adhvaryu

96

ขึ้นอยู่กับว่าคุณอยู่หลังคีย์หรือค่า ...

จากDictionary(TKey, TValue)คำอธิบายคลาสMSDN :

// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach( KeyValuePair<string, string> kvp in openWith )
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

// To get the values alone, use the Values property.
Dictionary<string, string>.ValueCollection valueColl =
    openWith.Values;

// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach( string s in valueColl )
{
    Console.WriteLine("Value = {0}", s);
}

// To get the keys alone, use the Keys property.
Dictionary<string, string>.KeyCollection keyColl =
    openWith.Keys;

// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach( string s in keyColl )
{
    Console.WriteLine("Key = {0}", s);
}

82

โดยทั่วไปแล้วการถามหา "วิธีที่ดีที่สุด" โดยไม่มีบริบทที่เฉพาะเจาะจงก็เหมือนกับการถาม ว่าสีใดดีที่สุด ?

มือข้างเดียวมีหลายสีและไม่มีสีที่ดีที่สุด ขึ้นอยู่กับความต้องการและบ่อยครั้งที่รสนิยมเช่นกัน

ในทางกลับกันมีหลายวิธีในการทำซ้ำพจนานุกรมใน C # และไม่มีวิธีที่ดีที่สุด ขึ้นอยู่กับความต้องการและบ่อยครั้งที่รสนิยมเช่นกัน

วิธีที่ตรงไปตรงมามากที่สุด

foreach (var kvp in items)
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

หากคุณต้องการเฉพาะค่า (อนุญาตให้เรียกว่าitemสามารถอ่านได้มากกว่าkvp.Value)

foreach (var item in items.Values)
{
    doStuff(item)
}

หากคุณต้องการเรียงลำดับเฉพาะ

โดยทั่วไปผู้เริ่มต้นจะประหลาดใจเกี่ยวกับลำดับการนับของพจนานุกรม

LINQ มีไวยากรณ์ที่กระชับที่อนุญาตให้ระบุคำสั่งซื้อ (และสิ่งอื่น ๆ อีกมากมาย) เช่น:

foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

คุณอาจต้องการเพียงค่าอีกครั้ง LINQ ยังให้บริการโซลูชั่นที่กระชับเพื่อ:

  • วนซ้ำโดยตรงกับค่า (อนุญาตให้เรียกitemได้อ่านได้ง่ายกว่าkvp.Value)
  • แต่จัดเรียงโดยกุญแจ

นี่มันคือ:

foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
    doStuff(item)
}

มีกรณีการใช้งานจริงอีกมากมายที่คุณสามารถทำได้จากตัวอย่างเหล่านี้ หากคุณไม่ต้องการคำสั่งซื้อที่เฉพาะเจาะจงเพียงทำตาม "วิธีที่ตรงไปตรงมาที่สุด" (ดูด้านบน)!


อันสุดท้ายควรเป็น.Valuesและไม่ใช่ประโยคที่เลือก
Mafii

@Mafii คุณแน่ใจเหรอ? ค่าที่ส่งคืนโดย OrderBy ไม่ใช่ประเภท KeyValuePair แต่ไม่มีValueฟิลด์ IOrderedEnumerable<KeyValuePair<TKey, TValue>>ชนิดที่แน่นอนผมเห็นที่นี่เป็น บางทีคุณอาจหมายถึงอย่างอื่น? คุณช่วยเขียนบรรทัดที่สมบูรณ์ที่แสดงความหมายของคุณ (และทดสอบ) ได้ไหม?
Stéphane Gourichon

ฉันคิดว่าคำตอบนี้มีสิ่งที่ฉันหมายถึง: stackoverflow.com/a/141105/5962841แต่แก้ไขให้ฉันถ้าฉันสับสนบางสิ่งบางอย่าง
Mafii

1
@Mafii อ่านคำตอบทั้งหมดของฉันอีกครั้งคำอธิบายระหว่างส่วนของโค้ดบอกบริบท คำตอบที่คุณพูดถึงเป็นเหมือนส่วนของรหัสที่สองในคำตอบของฉัน (ไม่จำเป็นต้องมีคำสั่งซื้อ) ที่นั่นฉันเพิ่งเขียนitems.Valueเหมือนที่คุณแนะนำ ในกรณีของส่วนที่สี่ที่คุณแสดงความคิดเห็นSelect()เป็นวิธีที่ทำให้เกิดforeachการระบุโดยตรงในค่าในพจนานุกรมแทนคู่ของคีย์ - ค่า ถ้าอย่างใดที่คุณไม่ชอบSelect()ในกรณีนี้คุณอาจต้องการส่วนรหัสที่สาม จุดของส่วนที่สี่คือการแสดงว่าสามารถประมวลผลคอลเลกชันล่วงหน้าด้วย LINQ
Stéphane Gourichon

1
ถ้าคุณทำ.Keys.Orderby()คุณจะทำซ้ำในรายการของคีย์ ถ้านั่นคือทั้งหมดที่คุณต้องการ หากคุณต้องการค่าจากนั้นในลูปคุณจะต้องค้นหาพจนานุกรมในแต่ละคีย์เพื่อรับค่า ในหลาย ๆ สถานการณ์มันจะไม่สร้างความแตกต่างในทางปฏิบัติ ในสถานการณ์ที่มีประสิทธิภาพสูงมันจะ เช่นเดียวกับที่ฉันเขียนในตอนต้นของคำตอบ: "มีหลายวิธี (... ) และไม่มีวิธีที่ดีที่สุดมันขึ้นอยู่กับความต้องการและบ่อยครั้งเกี่ยวกับรสนิยมด้วย"
Stéphane Gourichon

53

ฉันจะบอกว่า foreach เป็นวิธีมาตรฐานถึงแม้ว่ามันจะขึ้นอยู่กับสิ่งที่คุณกำลังมองหา

foreach(var kvp in my_dictionary) {
  ...
}

นั่นคือสิ่งที่คุณกำลังมองหา?


42
อืมการตั้งชื่อรายการ "มูลค่า" ค่อนข้างสับสนใช่ไหม โดยทั่วไปคุณจะใช้ไวยากรณ์เช่น "value.Key" และ "value.Value" ซึ่งไม่ง่ายมากสำหรับผู้ที่จะอ่านรหัสโดยเฉพาะอย่างยิ่งหากพวกเขาไม่คุ้นเคยกับวิธีการใช้พจนานุกรม. Net .
RenniePet

3
@RenniePet kvpเป็นที่นิยมใช้ในการตั้งชื่ออินสแตนซ์ KeyValuePair เมื่อ iterating foreach(var kvp in myDictionary){...กว่าพจนานุกรมและโครงสร้างข้อมูลที่เกี่ยวข้อง:
mbx

37

คุณสามารถลองใช้พจนานุกรมนี้เพื่อการประมวลผลแบบมัลติเธรด

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});

8
@WiiMaxx และมีความสำคัญมากขึ้นถ้ารายการเหล่านี้ไม่ได้ขึ้นอยู่กับแต่ละอื่น ๆ
Mafii

36

C # 7.0แนะนำDeconstructorsและถ้าคุณใช้ . NET Core 2.0 ขึ้นไปแอพ structKeyValuePair<>จะมีDeconstruct()ให้คุณแล้ว ดังนั้นคุณสามารถทำได้:

var dic = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
foreach (var (key, value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}
//Or
foreach (var (_, value) in dic) {
    Console.WriteLine($"Item [NO_ID] = {value}");
}
//Or
foreach ((int key, string value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}

ป้อนคำอธิบายรูปภาพที่นี่


5
หากคุณใช้. NET Framework ซึ่งอย่างน้อยไม่เกิน 4.7.2 ไม่มี Deconstruct บน KeyValuePair ให้ลองสิ่งนี้:foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
nwsmith

29

ฉันขอขอบคุณคำถามนี้มีคำตอบจำนวนมากอยู่แล้ว แต่ฉันต้องการที่จะทำวิจัยเล็กน้อย

การวนซ้ำพจนานุกรมอาจช้ากว่าเมื่อเปรียบเทียบกับการวนซ้ำบางอย่างเช่นอาร์เรย์ ในการทดสอบของฉันการวนซ้ำในอาร์เรย์ใช้เวลา 0.015003 วินาทีในขณะที่การวนซ้ำในพจนานุกรม (ด้วยจำนวนองค์ประกอบเท่ากัน) ใช้เวลา 0.0365073 วินาทีนั่นคือ 2.4 เท่า! แม้ว่าฉันจะเห็นความแตกต่างที่ใหญ่กว่ามาก สำหรับการเปรียบเทียบรายการอยู่ในระหว่างเวลา 0.00215043 วินาที

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

พจนานุกรมได้รับการปรับให้เหมาะกับการค้นหาดังนั้นเมื่อคำนึงถึงสิ่งที่ฉันได้สร้างไว้สองวิธี คนหนึ่งทำหน้าที่คนอื่น ๆ ทำซ้ำปุ่มอื่น ๆ ก็จะค้นหาขึ้นมา

public static string Normal(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var kvp in dictionary)
    {
        value = kvp.Value;
        count++;
    }

    return "Normal";
}

อันนี้โหลดกุญแจแล้ววนซ้ำมันแทน (ฉันลองดึงกุญแจเข้าไปในสายอักขระ [] แต่ความแตกต่างนั้นเล็กน้อยมาก

public static string Keys(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var key in dictionary.Keys)
    {
        value = dictionary[key];
        count++;
    }

    return "Keys";
}

ด้วยตัวอย่างนี้การทดสอบ foreach ปกติใช้เวลา 0.0310062 และรุ่นคีย์ใช้เวลา 0.2205441 การโหลดกุญแจทั้งหมดและวนซ้ำในการค้นหาทั้งหมดนั้นช้ากว่ามากอย่างเห็นได้ชัด!

สำหรับการทดสอบครั้งสุดท้ายฉันทำซ้ำอีกสิบครั้งเพื่อดูว่ามีประโยชน์กับการใช้ปุ่มที่นี่ (โดยจุดนี้ฉันแค่อยากรู้อยากเห็น):

นี่คือวิธี RunTest หากช่วยให้คุณเห็นภาพว่าเกิดอะไรขึ้น

private static string RunTest<T>(T dictionary, Func<T, string> function)
{            
    DateTime start = DateTime.Now;
    string name = null;
    for (int i = 0; i < 10; i++)
    {
        name = function(dictionary);
    }
    DateTime end = DateTime.Now;
    var duration = end.Subtract(start);
    return string.Format("{0} took {1} seconds", name, duration.TotalSeconds);
}

ที่นี่การวิ่งปกติแต่ละครั้งใช้เวลา 0.2820564 วินาที (นานกว่าการทำซ้ำครั้งเดียวประมาณสิบเท่า - ตามที่คุณคาดหวัง) การวนซ้ำของคีย์ใช้เวลา 2.2249449 วินาที

แก้ไขเพื่อเพิ่ม: การ อ่านคำตอบอื่น ๆ ทำให้ฉันถามว่าจะเกิดอะไรขึ้นถ้าฉันใช้พจนานุกรมแทนพจนานุกรม ในตัวอย่างนี้อาร์เรย์ใช้เวลา 0.0120024 วินาทีรายการ 0.0185037 วินาทีและพจนานุกรม 0.0465093 วินาที มีความสมเหตุสมผลที่จะคาดหวังว่าชนิดข้อมูลจะสร้างความแตกต่างว่าพจนานุกรมจะทำงานช้าลงเท่าใด

บทสรุปของฉันคืออะไร

  • หลีกเลี่ยงการวนซ้ำในพจนานุกรมหากคุณทำได้ แต่จะช้ากว่าการทำซ้ำอาร์เรย์อย่างมากด้วยข้อมูลเดียวกัน
  • หากคุณเลือกที่จะทำซ้ำมากกว่าพจนานุกรมอย่าพยายามฉลาดเกินไป แต่ช้ากว่าคุณสามารถทำได้แย่กว่าการใช้วิธีมาตรฐาน foreach

11
คุณควรวัดด้วยบางอย่างเช่น StopWatch แทน DateTime: hanselman.com/blog/…
แม้ Mien

2
คุณช่วยอธิบายสถานการณ์การทดสอบของคุณได้บ้างมีรายการอยู่ในพจนานุกรมของคุณบ่อยแค่ไหนที่คุณเรียกใช้สถานการณ์จำลองเพื่อคำนวณเวลาเฉลี่ย ...
WiiMaxx

3
น่าสนใจคุณจะได้รับผลลัพธ์ที่แตกต่างกันขึ้นอยู่กับข้อมูลที่คุณมีในพจนานุกรม ในขณะที่วนซ้ำในฟังก์ชันพจนานุกรมตัวระบุต้องข้ามช่องว่างจำนวนมากในพจนานุกรมซึ่งเป็นสาเหตุที่ทำให้ช้ากว่าการวนซ้ำในอาร์เรย์ หากพจนานุกรมเต็มจะมีสล็อตว่างน้อยกว่าที่จะข้ามไปหากว่าครึ่งนั้นว่างเปล่า
Martin Brown

26

มีตัวเลือกมากมาย รายการโปรดส่วนตัวของฉันคือ KeyValuePair

Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}

คุณยังสามารถใช้ชุดกุญแจและค่าได้


14

ด้วย.NET Framework 4.7หนึ่งสามารถใช้การสลายตัว

var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
    Console.WriteLine(fruit + ": " + number);
}

ในการทำให้โค้ดนี้ใช้งานในเวอร์ชัน C # ที่ต่ำกว่าให้เพิ่มSystem.ValueTuple NuGet packageและเขียนที่ใดที่หนึ่ง

public static class MyExtensions
{
    public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple,
        out T1 key, out T2 value)
    {
        key = tuple.Key;
        value = tuple.Value;
    }
}

9
สิ่งนี้ไม่ถูกต้อง .NET 4.7 นั้นมีมาให้ValueTupleในตัวมันมีให้ในรูปแบบแพ็คเกจ nuget สำหรับเวอร์ชั่นก่อนหน้านี้ ที่สำคัญกว่า, C # 7.0 ขึ้นไปเป็นสิ่งจำเป็นสำหรับDeconstructวิธีการในการทำงานเป็น Deconstructor var (fruit, number) in fruitsสำหรับ
David Arno

13

ตั้งแต่ C # 7 คุณสามารถแยกโครงสร้างวัตถุเป็นตัวแปรได้ ฉันเชื่อว่านี่เป็นวิธีที่ดีที่สุดในการทำซ้ำพจนานุกรม

ตัวอย่าง:

สร้างวิธีการขยายKeyValuePair<TKey, TVal>ที่ deconstructs มัน:

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value)
{
   key = pair.Key;
   value = pair.Value;
}

วนซ้ำDictionary<TKey, TVal>ในลักษณะต่อไปนี้

// Dictionary can be of any types, just using 'int' and 'string' as examples.
Dictionary<int, string> dict = new Dictionary<int, string>();

// Deconstructor gets called here.
foreach (var (key, value) in dict)
{
   Console.WriteLine($"{key} : {value}");
}

10

คุณแนะนำด้านล่างเพื่อย้ำ

Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary) {
    //Do some interesting things;
}

FYI foreachไม่ทำงานหากค่าเป็นประเภทวัตถุ


4
กรุณาอธิบายรายละเอียด: foreachจะไม่ทำงานหากที่คุ้มค่าเป็นประเภทobject? มิฉะนั้นสิ่งนี้ไม่สมเหตุสมผล
Marc L.

9

รูปแบบที่ง่ายที่สุดในการย้ำพจนานุกรม:

foreach(var item in myDictionary)
{ 
    Console.WriteLine(item.Key);
    Console.WriteLine(item.Value);
}

9

ใช้C # 7เพิ่มวิธีการขยายนี้ไปยังโครงการใด ๆ ของการแก้ปัญหาของคุณ:

public static class IDictionaryExtensions
{
    public static IEnumerable<(TKey, TValue)> Tuples<TKey, TValue>(
        this IDictionary<TKey, TValue> dict)
    {
        foreach (KeyValuePair<TKey, TValue> kvp in dict)
            yield return (kvp.Key, kvp.Value);
    }
}


และใช้ไวยากรณ์ง่าย ๆ นี้

foreach (var(id, value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


หรืออันนี้ถ้าคุณต้องการ

foreach ((string id, object value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


ในสถานที่ดั้งเดิม

foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;

    // your code using 'id' and 'value'
}


วิธีการขยายเปลี่ยนส่วนที่KeyValuePairคุณIDictionary<TKey, TValue>พิมพ์เป็นอย่างมากtupleช่วยให้คุณใช้ไวยากรณ์ที่สะดวกสบายใหม่นี้

มันแปลง - เพียง - รายการพจนานุกรมที่ต้องการเป็นtuplesดังนั้นจึงไม่แปลงทั้งพจนานุกรมเป็นtuplesดังนั้นจึงไม่มีความกังวลเกี่ยวกับประสิทธิภาพที่เกี่ยวข้อง

มีค่าใช้จ่ายเพียงเล็กน้อยเท่านั้นที่เรียกวิธีการขยายสำหรับการสร้างโดยtupleเปรียบเทียบกับการใช้KeyValuePairโดยตรงซึ่งไม่ควรเป็นปัญหาหากคุณกำหนดKeyValuePairคุณสมบัติของKeyและValueตัวแปรลูปใหม่อย่างไรก็ตาม

ในทางปฏิบัติไวยากรณ์ใหม่นี้เหมาะมากสำหรับกรณีส่วนใหญ่ยกเว้นสถานการณ์จำลองประสิทธิภาพสูงพิเศษระดับต่ำซึ่งคุณยังคงมีตัวเลือกที่จะไม่ใช้ในจุดเฉพาะนั้น

ลองดูสิ: บล็อก MSDN - คุณสมบัติใหม่ใน C # 7


1
มีเหตุผลอะไรที่จะชอบสิ่งอันดับ "สบาย" กับคู่ของคีย์ - ค่า - คู่ ฉันไม่เห็นกำไรที่นี่ tuple ของคุณมีคีย์และค่าดังนั้น key-value-pair
Maarten

4
สวัสดี Maarten ขอขอบคุณสำหรับคำถามของคุณ ประโยชน์หลักคือการอ่านรหัสได้โดยไม่ต้องเขียนโปรแกรมเพิ่มเติม ด้วย KeyValuePair เราต้องใช้แบบฟอร์มเสมอkvp.Keyและkvp.Valueใช้คีย์และค่าตามลำดับ ด้วย tuples คุณจะได้รับความยืดหยุ่นในการตั้งชื่อคีย์และค่าตามที่คุณต้องการโดยไม่ต้องใช้การประกาศตัวแปรเพิ่มเติมภายในบล็อก foreach เช่นคุณอาจตั้งชื่อคีย์ของคุณเป็นfactoryNameและค่าตามmodelsที่เป็นประโยชน์โดยเฉพาะอย่างยิ่งเมื่อคุณได้รับลูปซ้อน (พจนานุกรมของพจนานุกรม): การบำรุงรักษาโค้ดง่ายขึ้นมาก ลองดูสิ! ;-)
sɐunıɔןɐqɐp

7

ฉันรู้ว่านี่เป็นคำถามที่เก่ามาก แต่ฉันสร้างวิธีการขยายที่อาจมีประโยชน์:

    public static void ForEach<T, U>(this Dictionary<T, U> d, Action<KeyValuePair<T, U>> a)
    {
        foreach (KeyValuePair<T, U> p in d) { a(p); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.KeyCollection k, Action<T> a)
    {
        foreach (T t in k) { a(t); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.ValueCollection v, Action<U> a)
    {
        foreach (U u in v) { a(u); }
    }

ด้วยวิธีนี้ฉันสามารถเขียนรหัสเช่นนี้:

myDictionary.ForEach(pair => Console.Write($"key: {pair.Key}, value: {pair.Value}"));
myDictionary.Keys.ForEach(key => Console.Write(key););
myDictionary.Values.ForEach(value => Console.Write(value););

6

บางครั้งหากคุณต้องการระบุค่าให้ใช้การรวบรวมค่าของพจนานุกรม:

foreach(var value in dictionary.Values)
{
    // do something with entry.Value only
}

รายงานโดยโพสต์นี้ซึ่งระบุว่าเป็นวิธีที่เร็วที่สุด: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html


+1 สำหรับการนำประสิทธิภาพมาใช้จริง ๆ แล้วการวนซ้ำในพจนานุกรมเองรวมถึงค่าใช้จ่ายบางอย่างหากคุณต้องการเพียงค่า ส่วนใหญ่เกิดจากการคัดลอกค่าหรือการอ้างอิงไปยังโครงสร้างของ KeyValuePair
Jaanus Varus

1
FYI การทดสอบครั้งสุดท้ายในลิงค์นั้นเป็นการทดสอบสิ่งที่ผิด: มันวัดการวนซ้ำของคีย์โดยไม่สนใจค่าในขณะที่การทดสอบสองครั้งก่อนหน้านี้ใช้ค่านั้น
Crescent Fresh

5

ฉันพบวิธีนี้ในเอกสารสำหรับคลาส DictionaryBase บน MSDN:

foreach (DictionaryEntry de in myDictionary)
{
     //Do some stuff with de.Value or de.Key
}

นี่เป็นเพียงคนเดียวที่ฉันสามารถทำงานได้อย่างถูกต้องในชั้นเรียนที่สืบทอดมาจาก DictionaryBase


3
ดูเหมือนว่าเมื่อใช้ Dictionary รุ่นปลอด ... เช่นก่อน. NET Framework 2.0
joedotnot

@ joed0tnot: มันเป็นรุ่นที่ไม่ใช่ชื่อสามัญที่ใช้สำหรับHashtableวัตถุ
sɐunıɔןɐqɐp

5

foreachเร็วที่สุดและถ้าคุณทำซ้ำ___.Valuesมันก็เร็วขึ้นด้วย

ป้อนคำอธิบายรูปภาพที่นี่


ทำไมคุณถึงโทรContainsKey()ในforรุ่น? ซึ่งเพิ่มค่าใช้จ่ายเพิ่มเติมซึ่งไม่ปรากฏในรหัสที่คุณเปรียบเทียบ TryGetValue()มีอยู่เพื่อแทนที่รูปแบบ "ถ้ามีคีย์รับรายการด้วยคีย์" นอกจากนี้หากdictมีช่วงจำนวนเต็มต่อเนื่องตั้งแต่0ถึงถึงdictCount - 1คุณจะรู้ว่าตัวทำดัชนีไม่สามารถล้มเหลวได้ มิฉะนั้นdict.Keysเป็นสิ่งที่คุณควรทำซ้ำ ไม่ว่าจะด้วยวิธีใดContainsKey()/ ไม่TryGetValue()จำเป็น สุดท้ายโปรดอย่าโพสต์ภาพหน้าจอของรหัส
BACON

3

ฉันจะใช้ประโยชน์จาก. NET 4.0 ขึ้นไปและให้คำตอบที่ปรับปรุงใหม่สำหรับคำตอบที่ได้รับการยอมรับเดิม:

foreach(var entry in MyDic)
{
    // do something with entry.Value or entry.Key
}

3

วิธีมาตรฐานในการทำซ้ำในพจนานุกรมตามเอกสารอย่างเป็นทางการของ MSDN คือ:

foreach (DictionaryEntry entry in myDictionary)
{
     //Read entry.Key and entry.Value here
}

2

ฉันเขียนส่วนขยายเพื่อวนลูปมากกว่าพจนานุกรม

public static class DictionaryExtension
{
    public static void ForEach<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2> action) {
        foreach(KeyValuePair<T1, T2> keyValue in dictionary) {
            action(keyValue.Key, keyValue.Value);
        }
    }
}

จากนั้นคุณสามารถโทร

myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));

คุณกำหนดForEachวิธีการที่คุณมีforeach (...) { }... ดูเหมือนว่าไม่จำเป็น
Maarten

1

ถ้าพูดว่าคุณต้องการวนซ้ำคอลเลกชันค่าโดยค่าเริ่มต้นฉันเชื่อว่าคุณสามารถใช้ IEnumerable <>, โดยที่ T คือประเภทของวัตถุค่าในพจนานุกรมและ "this" เป็นพจนานุกรม

public new IEnumerator<T> GetEnumerator()
{
   return this.Values.GetEnumerator();
}

1

แค่ต้องการเพิ่ม 2 เซ็นต์ของฉันเพราะคำตอบส่วนใหญ่เกี่ยวข้องกับ foreach-loop โปรดดูที่รหัสต่อไปนี้:

Dictionary<String, Double> myProductPrices = new Dictionary<String, Double>();

//Add some entries to the dictionary

myProductPrices.ToList().ForEach(kvP => 
{
    kvP.Value *= 1.15;
    Console.Writeline(String.Format("Product '{0}' has a new price: {1} $", kvp.Key, kvP.Value));
});

สิ่งนี้เป็นการเพิ่มการเรียกเพิ่มเติมของ 'ToList ()' อาจมีการปรับปรุงประสิทธิภาพเล็กน้อย (ดังที่กล่าวไว้ที่นี่foreach กับ someList.Foreach () {} ) โดยเฉพาะเมื่อทำงานกับพจนานุกรมขนาดใหญ่และทำงานในแบบคู่ขนานไม่มี ตัวเลือก / จะไม่มีผลเลย

นอกจากนี้โปรดทราบว่าคุณจะไม่สามารถกำหนดค่าให้กับคุณสมบัติ 'ค่า' ภายใน foreach-loop ในทางกลับกันคุณจะสามารถจัดการ 'คีย์' ได้เช่นกันซึ่งอาจทำให้คุณมีปัญหาในขณะใช้งานจริง

เมื่อคุณเพียงต้องการ "อ่าน" คีย์และค่าคุณอาจใช้ IEnumerable.Select ()

var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );

5
การคัดลอกคอลเล็กชันทั้งหมดโดยไม่มีเหตุผลจะไม่ช่วยเพิ่มประสิทธิภาพ มันจะช้าลงอย่างมากรหัสเช่นเดียวกับการเพิ่มรอยเท้าหน่วยความจำของรหัสสองเท่าที่ควรกินแทบไม่มีหน่วยความจำเพิ่มเติม
Servy

ฉันหลีกเลี่ยงวิธีการ 'List.ForEach' ของforeachผลข้างเคียง: บังคับให้มองเห็นผลข้างเคียงที่เกิดขึ้น
2864740

0
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));

2
จะต้องมีเหตุผล / คำอธิบายเพิ่มเติมในคำตอบนี้ อะไรAggregateObjectเพิ่มKeyValuePair? "การทำซ้ำ" ตามที่ร้องขอในคำถามอยู่ที่ไหน
Marc L.

เลือกทำซ้ำมากกว่าพจนานุกรมและช่วยให้เราสามารถทำงานกับแต่ละวัตถุ มันไม่ได้เป็นทั่วไปforeachแต่ฉันใช้มันมาก คำตอบของฉันสมควรได้รับการลงคะแนนอย่างแท้จริงหรือไม่?
Pixar

ไม่Select ใช้การวนซ้ำเพื่อให้เกิดผลลัพธ์ แต่ไม่ใช่ตัววนซ้ำ ประเภทของสิ่งที่ย้ำ ( foreach) ใช้สำหรับ - โดยเฉพาะอย่างยิ่งการดำเนินงานที่มีผลข้างเคียง - อยู่นอกขอบเขตของ Linq Selectรวมทั้ง แลมบ์ดาจะไม่วิ่งจนกว่าaggregateObjectCollectionจะมีการแจกแจงจริง หากคำตอบนี้ถูกใช้เป็น "เส้นทางแรก" (เช่นใช้ก่อนหน้าตรงforeach) มันส่งเสริมการปฏิบัติที่ไม่ดี สถานการณ์อาจมีการดำเนินการ Linq ที่เป็นประโยชน์ก่อนทำซ้ำพจนานุกรม แต่ไม่ได้ตอบคำถามตามที่ถาม
Marc L.

0

พจนานุกรม <TKey, TValue>มันเป็นคลาสคอลเลกชันทั่วไปใน c # และเก็บข้อมูลในรูปแบบค่าคีย์คีย์ต้องไม่ซ้ำกันและไม่สามารถเป็น null ได้ในขณะที่ค่าสามารถซ้ำกันและเป็น null แต่ละรายการในพจนานุกรมคือ ถือว่าเป็นโครงสร้าง KeyValuePair <TKey, TValue> ซึ่งแทนค่าคีย์และค่า และด้วยเหตุนี้เราควรใช้ประเภทองค์ประกอบ KeyValuePair <TKey, TValue> ในระหว่างการวนซ้ำขององค์ประกอบ ด้านล่างเป็นตัวอย่าง

Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"One");
dict.Add(2,"Two");
dict.Add(3,"Three");

foreach (KeyValuePair<int, string> item in dict)
{
    Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}

0

ถ้าคุณต้องการใช้ลูปคุณสามารถทำได้:

var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
   var key= keyList[i];
   var value = dictionary[key];
 }

สิ่งนี้มีประโยชน์อย่างไร มันเป็นรหัสที่ยาวกว่าการforeachวนซ้ำ และประสิทธิภาพที่แย่ลงเพราะnew List<string>(dictionary.Keys)จะวนซ้ำdictionary.Countก่อนที่คุณจะมีโอกาสวนซ้ำด้วยตัวคุณเอง นอกเหนือจากที่ขอให้ "วิธีที่ดีที่สุด" เป็นเรื่องส่วนตัวฉันไม่เห็นว่าสิ่งนี้จะมีคุณสมบัติเป็น "วิธีที่ดีที่สุด" หรือ "วิธีมาตรฐาน" ที่คำถามนั้นถาม ถึง "ถ้าคุณต้องการใช้สำหรับลูป ... " ฉันจะตอบโต้ด้วย " ไม่ต้องใช้forลูป"
เบคอน

หากคุณมีคอลเลกชันขนาดใหญ่และการดำเนินการช้าใน foreach และหากคอลเลกชันของคุณสามารถเปลี่ยนแปลงได้เมื่อคุณทำซ้ำมันปกป้องคุณจากข้อผิดพลาด "คอลเลกชันที่มีการเปลี่ยนแปลง" และวิธีนี้มีประสิทธิภาพที่ดีกว่า
Seçkin Durgay

ฉันยอมรับว่าการหลีกเลี่ยงข้อยกเว้น "การรวบรวมถูกแก้ไข" เป็นเหตุผลหนึ่งในการทำเช่นนี้แม้ว่ากรณีพิเศษนั้นไม่ได้ระบุไว้ในคำถามและสามารถทำได้foreach (var pair in dictionary.ToArray()) { }ทุกครั้ง ถึงกระนั้นฉันคิดว่ามันเป็นการดีที่จะให้ความกระจ่างในคำตอบของสถานการณ์เฉพาะที่เราต้องการใช้รหัสนี้และความหมายของการทำเช่นนั้น
BACON

ใช่คุณพูดถูก แต่มีคำตอบที่นี่กับ ElementAt และมันมีชื่อเสียงสูงมากและฉันป้อนคำตอบนี้ :)
Seçkin Durgay

-1

ง่ายด้วย linq

 Dictionary<int, string> dict = new Dictionary<int, string>();
 dict.Add(1, "American Ipa");
 dict.Add(2, "Weiss");
 dict.ToList().ForEach(x => Console.WriteLine($"key: {x.Key} | value: {x.Value}") );

ฉันเห็นว่าคุณกำลังโทรToList()เพราะForEach()มีการกำหนดไว้ในList<>ชั้นเรียนเท่านั้น แต่ทำไมถึงทำแบบนั้นทั้งหมดแทนที่จะเป็นแค่foreach (var pair in dict) { }? ฉันจะบอกว่ามันง่ายกว่าและไม่มีนัยยะของหน่วยความจำ / ประสิทธิภาพที่เหมือนกัน คำตอบที่ถูกต้องนี้ได้เสนอไว้แล้วในคำตอบนี้จาก 3.5 ปีก่อนอย่างไรก็ตาม
BACON

เพียงเพื่อการสร้างภาพรหัสมันสามารถทำได้จริง ๆ ตามที่คุณระบุฉันแค่อยากจะนำเสนอมันในวิธีที่ต่างกันและในมุมมองของฉันฉันขอโทษที่ไม่เห็นคำตอบที่ระบุกอด
Luiz Fernando Corrêa Leite

-3

นอกจากโพสต์อันดับสูงสุดที่มีการสนทนาระหว่างการใช้

foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

หรือ

foreach(var entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

สมบูรณ์ที่สุดคือต่อไปนี้เพราะคุณสามารถดูประเภทพจนานุกรมจากการเริ่มต้น kvp คือ KeyValuePair

var myDictionary = new Dictionary<string, string>(x);//fill dictionary with x

foreach(var kvp in myDictionary)//iterate over dictionary
{
    // do something with kvp.Value or kvp.Key
}

5
การสร้างและการคัดลอกพจนานุกรมที่สองไม่ใช่วิธีที่ถูกต้องในการอ่านโค้ด ในความเป็นจริงฉันจะเถียงว่ามันจะทำให้โค้ดยากขึ้นที่จะเข้าใจเพราะตอนนี้คุณต้องถามตัวเองว่า: "ทำไมคนสุดท้ายที่สร้างพจนานุกรมที่สอง?" หากคุณต้องการที่จะละเอียดมากขึ้นเพียงใช้ตัวเลือกที่หนึ่ง
Matthew Goulart

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