c# معنى - ما أفضل طريقة للتكرار عبر القاموس؟




كلمة بالعربية (21)

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

مثال:

إنشاء طريقة ملحق على KeyValuePair<TKey, TVal> التي 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}");
}

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


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

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

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

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

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


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

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

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

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


أبسط شكل لتكرار القاموس:

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

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

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

أردت فقط إضافة سنتي 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 مقابل بعض List.Foreach () {} ) ، espacially عند العمل مع قواميس كبيرة وتشغيل في موازاة لا الخيار / لن يكون لها تأثير على الإطلاق.

كذلك ، يرجى ملاحظة أنك لن تتمكن من تعيين قيم لقيمة "القيمة" داخل حلقة foreach. من ناحية أخرى ، سوف تكون قادرًا على التعامل مع "المفتاح" أيضًا ، مما قد يجعلك تواجه مشكلة في وقت التشغيل.

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

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

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

من وصف 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 # 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> الخاص بك IDictionary<TKey, TValue> إلى مجموعة IDictionary<TKey, TValue> مكتوبة بقوة ، مما يسمح لك باستخدام هذا التركيب الجديد المريح.

إنه يحول - فقط- إدخالات القاموس المطلوبة إلى tuples ، لذلك لا يحول القاموس بأكمله إلى مجموعات ، لذلك لا توجد مخاوف متعلقة بالأداء مرتبطة بذلك.

هناك تكلفة ثانوية بسيطة تسمى طريقة التمديد لإنشاء فئة مقارنة مع استخدام KeyValuePair مباشرة ، والتي لا ينبغي أن تكون مشكلة إذا كنت تقوم بتعيين خصائص KeyValuePair 's Key and Value للمتغيرات حلقة جديدة على أي حال.

من الناحية العملية ، فإن هذه البنية الجديدة تناسب بشكل جيد للغاية في معظم الحالات ، باستثناء سيناريوهات الأداء المنخفض للغاية ذات المستوى المنخفض ، حيث لا يزال أمامك خيار عدم استخدامه ببساطة على تلك النقطة المحددة.

تحقق من ذلك: مدونة MSDN - ميزات جديدة في C # 7


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

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
}

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

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

في بعض الحالات ، قد تحتاج إلى عداد يمكن توفيره من خلال تنفيذ الحل المتكرر. لهذا ، يوفر LINQ ElementAt مما يتيح ما يلي:

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

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

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

كان هذا هو الوحيد الذي تمكنت من الحصول على وظائف بشكل صحيح في فئة ورثت من DictionaryBase.


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

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

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

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

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

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

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

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

phonebook.Remove("Jessica");
Console.WriteLine(phonebook.Count);

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

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

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

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


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


لقد كتبت امتدادًا لحلقة عبر قاموس.

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

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

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

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

الطريق الأكثر مباشرة

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

إذا كنت بحاجة إلى القيمة فقط (يسمح لك بالاتصال به ، أكثر قابلية للقراءة من 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)
}

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


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

تأتي متأخرة إلى الحفلة: أستخدم أنواع CLR 100٪ من الوقت (جيد ، إلا إذا أجبرت على استخدام نوع C # ، لكنني لا أتذكر متى كانت المرة الأخيرة).

بدأت أصلا في القيام بذلك منذ سنوات ، وفقا لكتب CLR التي كتبها ريتشي. كان من المنطقي بالنسبة لي أن جميع لغات CLR في نهاية المطاف يجب أن تكون قادرة على دعم مجموعة من أنواع CLR ، لذلك فإن استخدام أنواع CLR قد وفرت بنفسك أوضح ، وربما أكثر من "reusable" code.

الآن بعد أن كنت أفعل ذلك لسنوات ، إنها عادة وأحب التلوين الذي تعرضه VS لأنواع CLR.

الموفر الحقيقي الوحيد هو أن الإكمال التلقائي يستخدم نوع C # ، لذلك انتهى بي الأمر بإعادة كتابة الأنواع التي تم إنشاؤها تلقائيًا لتحديد نوع CLR بدلاً من ذلك.

أيضا ، الآن ، عندما أرى كلمة "int" أو "string" ، يبدو الأمر خاطئًا بالنسبة لي تمامًا ، كأنني أبحث عن رمز C عام 1970.





c# dictionary loops