Pergunta

Alguém tem uma maneira mais lisa para fazer isso? Parece que ele deve ser mais fácil do que isso, mas eu estou tendo um bloqueio mental. Basicamente eu preciso para remover itens de um dicionário e recurse nos valores dos itens que também são dicionários.

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

dicionário Ação é assim:

public class ActionDictionary : Dictionary<string, IActionItem>, IActionItem
Foi útil?

Solução

Você não precisa realmente de recolher as chaves e reitere-los novamente se você iterar o dicionário em sentido inverso (de 'menu.Count - 1' para zero). Iteração em ordem para a frente, é claro, rendimento mutante exceções de cobrança, se você começar a remover as coisas.

Eu não sei o que um ActionDictionary é, então eu não poderia testar seu cenário exato, mas aqui está um exemplo usando apenas 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;
    }

Eu também reverteu a declaração 'se', mas isso era apenas uma suposição que você gostaria de fazer a verificação de tipo antes de chamar um método de agir sobre o valor do item ... ele vai trabalhar de qualquer forma assumindo 'GetIsPermitted' sempre retorna true para ActionDictionary.

Espero que isso ajude.

Outras dicas

Enquanto foreach e GetEnumerator falhar, um para-loop de obras,

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

Mas ElementAt () é um recurso .NET 3.5.

Para começar, seu loop foreach é muito mais complicado do que precisa ser. Basta fazer:

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

Estou um pouco surpreso que Dictionary não tem um método RemoveAll mas não parece que ele faz ...

Opção 1: Um dicionário ainda é uma Collection. Iterar sobre menu.Values.

Você pode iterar sobre menu.Values ??e removê-los como você iterate. Os valores não virá em qualquer ordem de classificação (que deve ser bom para o seu caso). Você pode precisar usar um loop for e ajustar o índice em vez de usar foreach -. O recenseador irá lançar uma exceção se você modificar a coleção durante a iteração

(Vou tentar adicionar o código quando estou na minha máquina dev Mon)

Opção 2:. Criar um iterador personalizado

Algumas coleções devolvidos a partir de caixa de listagem SelectedItems em WinForms realmente não contêm a coleção, eles fornecem um invólucro em torno da coleção subjacente. Tipo como CollectionViewSource em WPF. ReadOnlyCollection faz algo semelhante também.

Crie uma classe que pode "achatar" seus dicionários aninhados em algo que pode enumerar sobre eles como se fossem uma única coleção. Implementar uma função de exclusão que parece que remove um item da coleção, mas realmente remove a partir do dicionário atual.

Na minha opinião, você pode definir sua própria classe genérica decorrente da KeyValuePair<...> tanto TKey e TValue será List<T> e você pode usar o RemoveAll ou o RemoveRange do List<T> em um novo método RemoveRange() ou RemoveAll() em sua classe derivada para remover os itens que deseja.

Eu sei que você provavelmente encontrou boa solução já, mas apenas pela razão de 'esperteza' se você pode modificar suas assinaturas de método para (eu sei que pode não ser apropriado em seu cenário):

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

E eu posso ver maneiras casal onde você pode usar seu dicionário com itens filtrados sem modificação e materializando novos dicionários.

Não é muito menos complicado, mas algumas mudanças idiomáticas torná-lo um pouco mais curto e mais fácil sobre os olhos:

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

Alterar o tipo de keysToRemove para HashSet<string> e você terá um método Contains O (1). Com List<string>-lo de O (n), que é mais lento como você pode imaginar.

não testado até que eu estou em minha máquina VS amanhã: o

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

Eu acho

public class ActionSet : HashSet<IActionItem>, IActionItem

E

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

devem trabalhar rápido

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top