سؤال

لقد رأيت عدة طرق مختلفة للتكرار عبر قاموس في C#.هل هناك طريقة قياسية؟

هل كانت مفيدة؟

المحلول

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

نصائح أخرى

إذا كنت تحاول استخدام قاموس عام في لغة 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 وما فوق، ويمكنك أيضًا استخدام النوع الدقيق لمفاتيحك/قيمك هنا)

في بعض الحالات، قد تحتاج إلى عداد يمكن توفيره من خلال تطبيق 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;
}

يعتمد على ما إذا كنت تبحث عن المفاتيح أو القيم...

من MSDN Dictionary(TKey, TValue) وصف الفئة:

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

بشكل عام، فإن السؤال عن "أفضل طريقة" بدون سياق محدد يشبه السؤالما هو أفضل لون?

من ناحية، هناك العديد من الألوان وليس هناك لون أفضل.يعتمد ذلك على الحاجة وفي كثير من الأحيان على الذوق أيضًا.

من ناحية أخرى، هناك العديد من الطرق للتكرار عبر قاموس في 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)
}

هناك العديد من حالات الاستخدام الواقعية التي يمكنك القيام بها من هذه الأمثلة.إذا لم تكن بحاجة إلى أمر محدد، فما عليك سوى الالتزام بـ "الطريقة الأكثر مباشرة" (انظر أعلاه)!

أود أن أقول إن foreach هي الطريقة القياسية، على الرغم من أنها تعتمد بشكل واضح على ما تبحث عنه

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

هل هذا ما تبحث عنه؟

يمكنك أيضًا تجربة ذلك على القواميس الكبيرة للمعالجة متعددة الخيوط.

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

هناك الكثير من الخيارات.مفضلتي الشخصية هي KeyValuePair

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

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

يمكنك أيضًا استخدام مجموعات المفاتيح والقيم

أقدر أن هذا السؤال قد حصل بالفعل على الكثير من الردود ولكني أردت إجراء القليل من البحث.

يمكن أن يكون التكرار عبر القاموس بطيئًا إلى حد ما عند مقارنته بالتكرار على شيء مثل المصفوفة.في اختباراتي، استغرق التكرار على مصفوفة 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);
}

هنا استغرق تشغيل foreach العادي 0.2820564 ثانية (حوالي عشر مرات أطول مما استغرقه تكرار واحد - كما كنت تتوقع).استغرق التكرار على المفاتيح 2.2249449 ثانية.

تم التعديل للإضافة:قراءة بعض الإجابات الأخرى جعلتني أتساءل عما سيحدث إذا استخدمت القاموس بدلاً من القاموس.في هذا المثال، استغرق المصفوفة 0.0120024 ثانية، والقائمة 0.0185037 ثانية، والقاموس 0.0465093 ثانية.من المعقول أن نتوقع أن يُحدث نوع البيانات فرقًا في مدى بطئ القاموس.

ما هي استنتاجاتي?

  • تجنب التكرار على القاموس إذا استطعت، فهو أبطأ بكثير من التكرار على مصفوفة تحتوي على نفس البيانات.
  • إذا اخترت التكرار على القاموس، فلا تحاول أن تكون ذكيًا جدًا، على الرغم من أنه أبطأ فقد تفعل ما هو أسوأ بكثير من استخدام طريقة foreach القياسية.

ج#7.0 قدَّم المفككون وإذا كنت تستخدم نت كور 2.0+ التطبيق، الهيكل KeyValuePair<> تشمل بالفعل أ 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}");
}

enter image description here

لقد اقترحت أدناه للتكرار

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

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

لعِلمِكَ، foreach لا يعمل إذا كانت القيمة من نوع الكائن.

مع .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;
    }
}

أبسط نموذج لتكرار القاموس:

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

استخدام ج#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

في بعض الأحيان، إذا كنت تحتاج فقط إلى تعداد القيم، فاستخدم مجموعة قيم القاموس:

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

لقد وجدت هذه الطريقة في وثائق فئة DictionaryBase على MSDN:

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

كان هذا هو الشيء الوحيد الذي تمكنت من تشغيله بشكل صحيح في الفصل الموروث من DictionaryBase.

سأستفيد من .NET 4.0+ وسأقدم إجابة محدثة للإجابة المقبولة في الأصل:

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

الطريقة القياسية للتكرار عبر القاموس، وفقًا للوثائق الرسمية على MSDN هي:

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

اعتبارًا من الإصدار C#7، يمكنك تفكيك الكائنات إلى متغيرات.أعتقد أن هذه هي أفضل طريقة للتكرار عبر القاموس.

مثال:

قم بإنشاء طريقة تمديد على KeyValuePair<TKey, TVal> الذي يفككها:

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey, out TVal val)
{
   key = pair.Key;
   val = 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}");
}

إذا كنت تريد التكرار عبر مجموعة القيم افتراضيًا، فأعتقد أنه يمكنك تنفيذ IEnumerable<>، حيث T هو نوع كائن القيم في القاموس، و"هذا" هو قاموس.

public new IEnumerator<T> GetEnumerator()
{
   return this.Values.GetEnumerator();
}
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

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

أردت فقط إضافة سنتي 2، حيث أن معظم الإجابات تتعلق بحلقة foreach.من فضلك قم بإلقاء نظرة على الكود التالي:

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.من ناحية أخرى، سوف تكون قادرًا على التعامل مع "المفتاح" أيضًا، مما قد يسبب لك مشاكل في وقت التشغيل.

عندما تريد فقط "قراءة" المفاتيح والقيم، يمكنك أيضًا استخدام IEnumerable.Select().

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

لقد كتبت امتدادًا للتكرار فوق القاموس.

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

أعلم أن هذا سؤال قديم جدًا، لكنني قمت بإنشاء بعض طرق التوسيع التي قد تكون مفيدة:

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

قاموس<TKey, TValue> إنها فئة تجميع عامة في c# وتقوم بتخزين البيانات بتنسيق قيمة المفتاح. يجب أن يكون المفتاح فريدًا ولا يمكن أن يكون فارغًا بينما يمكن أن تكون القيمة مكررة وخالية. حيث يتم التعامل مع كل عنصر في القاموس على أنه 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);
}

بالإضافة إلى المشاركات الأعلى تصنيفًا والتي يوجد بها نقاش بين الاستخدام

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
}

القواميس هي قوائم خاصة ، في حين أن كل قيمة في القائمة لديها مفتاح وهو أيضًا متغير.ومن الأمثلة الجيدة على القاموس هو دليل الهاتف.

   Dictionary<string, long> phonebook = new Dictionary<string, long>();
    phonebook.Add("Alex", 4154346543);
    phonebook["Jessica"] = 4159484588;

لاحظ أنه عند تحديد القاموس ، نحتاج إلى توفير تعريف عام بنوعين - نوع المفتاح ونوع القيمة.في هذه الحالة، المفتاح عبارة عن سلسلة بينما القيمة عبارة عن عدد صحيح.

هناك أيضًا طريقتان لإضافة قيمة واحدة إلى القاموس، إما باستخدام عامل الأقواس أو باستخدام طريقة الإضافة.

للتحقق مما إذا كان القاموس يحتوي على مفتاح معين، يمكننا استخدام طريقة يحتوي على مفتاح:

Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

if (phonebook.ContainsKey("Alex"))
{
    Console.WriteLine("Alex's number is " + phonebook["Alex"]);
}

لإزالة عنصر من القاموس، يمكننا استخدام طريقة الإزالة.تعد إزالة عنصر من القاموس بواسطة مفتاحه أمرًا سريعًا وفعالًا للغاية.عند إزالة عنصر من قائمة باستخدام قيمته، تكون العملية بطيئة وغير فعالة، على عكس وظيفة إزالة القاموس.

Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

phonebook.Remove("Jessica");
Console.WriteLine(phonebook.Count);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top