سؤال

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

private void RemoveNotPermittedItems(ActionDictionary menu)
{
    var keysToRemove = new List<string>();
    foreach (var item in menu)
    {
        if (!GetIsPermitted(item.Value.Call))
        {
            keysToRemove.Add(item.Key);
        }
        else if (item.Value is ActionDictionary)
        {
            RemoveNotPermittedItems((ActionDictionary)item.Value);
            if (((ActionDictionary)item.Value).Count == 0)
            {
                keysToRemove.Add(item.Key);
            }
        }
    }
    foreach (var key in (from item in menu where keysToRemove.Contains(item.Key) select item.Key).ToArray())
    {
        menu.Remove(key);
    }
}

قاموس العمل هو مثل هذا:

public class ActionDictionary : Dictionary<string, IActionItem>, IActionItem
هل كانت مفيدة؟

المحلول

وأنت لا تحتاج حقا لجمع المفاتيح وتكرار لهم مرة أخرى إذا كنت تكرار القاموس في الاتجاه المعاكس (من "menu.Count - 1" إلى الصفر). بالتكرار من أجل الأمام وبطبيعة الحال، العائد تحور استثناءات جمع إذا كنت تبدأ إزالة الأشياء.

وأنا لا أعرف ما هو ActionDictionary، لذلك لم أتمكن من اختبار سيناريو بك على وجه الدقة، ولكن هنا على سبيل المثال فقط باستخدام Dictionary<string,object>.

    static int counter = 0;
    private static void RemoveNotPermittedItems(Dictionary<string, object> menu)
    {
        for (int c = menu.Count - 1; c >= 0; c--)
        {
            var key = menu.Keys.ElementAt(c);
            var value = menu[key];
            if (value is Dictionary<string, object>)
            {
                RemoveNotPermittedItems((Dictionary<string, object>)value);
                if (((Dictionary<string, object>)value).Count == 0)
                {
                    menu.Remove(key);
                }
            }
            else if (!GetIsPermitted(value))
            {
                menu.Remove(key);
            }
        }
    }

    // This just added to actually cause some elements to be removed...
    private static bool GetIsPermitted(object value)
    {
        if (counter++ % 2 == 0)
            return false;
        return true;
    }

وأنا أيضا عكس 'إذا' البيان، لكن ذلك كان مجرد افتراض ان كنت تريد أن تفعل نوع التحقق قبل استدعاء أسلوب للعمل على قيمة العنصر ... انها ستعمل في اي من الاتجاهين على افتراض "GetIsPermitted" دائما بإرجاع True للActionDictionary.

وآمل أن يساعد هذا.

نصائح أخرى

وحين فشل foreach وGetEnumerator، ولحلقة أعمال،

و

var table = new Dictionary<string, int>() {{"first", 1}, {"second", 2}};
for (int i = 0; i < table.Keys.Count; i++)//string key in table.Keys)
{
    string key = table.Keys.ElementAt(i);
    if (key.StartsWith("f"))
    {
        table.Remove(key);
    }
}

و

ولكن ElementAt () هي ميزة الصافي 3.5.

لتبدأ، حلقة foreach الخاص بك هو طريقة أكثر تعقيدا مما يجب أن يكون. مجرد القيام به:

foreach (var key in keysToRemove)
{
    menu.Remove(key);
}

وأنا مندهش قليلا أن Dictionary لايوجد طريقة RemoveAll ولكن لا تبدو وكأنها لا ...

الخيار 1:القاموس لا يزال عبارة عن مجموعة.التكرار عبر القائمة. القيم.

يمكنك التكرار عبر القائمة. القيم وإزالتها أثناء التكرار.لن تأتي القيم بأي ترتيب مفروز (وهو ما يجب أن يكون مناسبًا لحالتك).قد تحتاج إلى استخدام حلقة for وضبط الفهرس بدلاً من استخدام foreach - سيطرح العداد استثناءً إذا قمت بتعديل المجموعة أثناء التكرار.

(سأحاول إضافة الكود عندما أكون على جهاز التطوير الخاص بي Mon)

الخيار 2:إنشاء مكرر مخصص.

بعض المجموعات التي يتم إرجاعها من ListBox SelectedItems في Winforms لا تحتوي بالفعل على المجموعة، ولكنها توفر غلافًا حول المجموعة الأساسية.نوع من مثل CollectionViewSource في WPF.يقوم ReadOnlyCollection بفعل شيء مشابه أيضًا.

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

في رأيي، يمكنك تحديد الخاصة بك اشتقاق فئة عامة من KeyValuePair<...> كلا TKey وسيتم List<T> TValue ويمكنك استخدام RemoveAll أو RemoveRange من List<T> في RemoveRange() أو RemoveAll() طريقة جديدة في فئة مشتقة إزالة العناصر التي تريد.

وأنا أعلم أنك ربما وجدت حلا جيدا بالفعل، ولكن فقط لسبب من "النعومة" إذا كنت قد تعديل تواقيع طريقة ل(وأنا أعلم أنه قد لا يكون مناسبا في السيناريو الخاص بك):

private ActionDictionary RemoveNotPermittedItems(ActionDictionary menu)
{
 return new ActionDictionary(from item in menu where GetIsPermitted(item.Value.Call) select item)
.ToDictionary(d=>d.Key, d=>d.Value is ActionDictionary?RemoveNotPermittedItems(d.Value as ActionDictionary) : d.Value));
}

وأستطيع أن أرى طرق زوجين حيث يمكنك استخدام القاموس الخاص بك باستخدام الأدوات التي تم تصفيتها دون تعديل وتجسيد قواميس جديدة.

وانها ليست أقل تعقيدا بكثير، ولكن بعض التغييرات الاصطلاحية جعله قليلا أقصر وأسهل على العيون:

    private static void RemoveNotPermittedItems(IDictionary<string, IActionItem> menu)
    {
        var keysToRemove = new List<string>();

        foreach (var item in menu)
        {
            if (GetIsPermitted(item.Value.Call))
            {
                var value = item.Value as ActionDictionary;

                if (value != null)
                {
                    RemoveNotPermittedItems(value);
                    if (!value.Any())
                    {
                        keysToRemove.Add(item.Key);
                    }
                }
            }
            else
            {
                keysToRemove.Add(item.Key);
            }
        }

        foreach (var key in keysToRemove)
        {
            menu.Remove(key);
        }
    }

    private static bool GetIsPermitted(object call)
    {
        return ...;
    }

وتغيير نوع keysToRemove إلى HashSet<string> وستحصل على O (1) طريقة Contains. مع List<string> انها O (ن)، وهو أبطأ كما قد يتبادر إلى ذهنك.

ولم تختبر حتى أنا في بلدي VS آلة غدا: س

private void RemoveNotPermittedItems(ActionDictionary menu)
{
    foreach(var _checked in (from m in menu
                             select new
                             {
                                 gip = !GetIsPermitted(m.Value.Call),
                                 recur = m.Value is ActionDictionary,
                                 item = m
                             }).ToArray())
    {
        ActionDictionary tmp = _checked.item.Value as ActionDictionary;
        if (_checked.recur)
        {
            RemoveNotPermittedItems(tmp);
        }
        if (_checked.gip || (tmp != null && tmp.Count == 0) {
            menu.Remove(_checked.item.Key);
        }
    }
}

وأعتقد

public class ActionSet : HashSet<IActionItem>, IActionItem

و

bool Clean(ActionSet nodes)
    {
        if (nodes != null)
        {
            var removed = nodes.Where(n => this.IsNullOrNotPermitted(n) || !this.IsNotSetOrNotEmpty(n) || !this.Clean(n as ActionSet));

            removed.ToList().ForEach(n => nodes.Remove(n));

            return nodes.Any();
        }

        return true;
    }

    bool IsNullOrNotPermitted(IActionItem node)
    {
        return node == null || *YourTest*(node.Call);
    }

    bool IsNotSetOrNotEmpty(IActionItem node)
    {
        var hset = node as ActionSet;
        return hset == null || hset.Any();
    }

ويجب العمل بسرعة

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top