문제

누구든지 이 작업을 수행하는 더 매끄러운 방법이 있습니까?이보다 더 쉬울 것 같지만 정신적인 장애가 있습니다.기본적으로 사전에서 항목을 제거하고 사전이기도 한 항목의 값으로 재귀적으로 들어가야 합니다.

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'에서 0에서) 키를 반복 할 필요는 없습니다. 물론 전방 순서로 반복하면 물건을 제거하기 시작하면 돌연변이 수집 예외를 얻을 수 있습니다.

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

나는 또한 'if'진술을 뒤집었지만 항목의 가치에 따라 행동하기 위해 메소드를 호출하기 전에 유형 확인을하고 싶다는 가정 일뿐입니다 ... 'getispermitted'가 항상 true를 반환한다고 가정하면 어느 쪽이든 작동합니다. ActionDictionary.

도움이 되었기를 바랍니다.

다른 팁

Foreach 및 GetEnumerator가 실패하는 동안 For-Loop가 작동합니다.

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 ()는 .NET 3.5 기능입니다.

우선, 당신의 foreach 루프는 필요한 것보다 훨씬 더 복잡합니다. 그냥 해:

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

나는 약간 놀랐다 Dictionary A가 없습니다 RemoveAll 방법이지만 그렇지 않은 것처럼 보이지 않습니다 ...

옵션 1:사전은 여전히 ​​컬렉션입니다.menu.Values를 반복합니다.

menu.Values를 반복하고 반복하면서 제거할 수 있습니다.값은 정렬된 순서로 제공되지 않습니다(귀하의 경우에는 괜찮습니다).foreach를 사용하는 대신 for 루프를 사용하고 인덱스를 조정해야 할 수도 있습니다. 반복하는 동안 컬렉션을 수정하면 열거자가 예외를 발생시킵니다.

(월에 내 개발 컴퓨터에 있을 때 코드를 추가하려고 합니다)

옵션 2:사용자 정의 반복자를 만듭니다.

Winforms의 ListBox SelectedItems에서 반환된 일부 컬렉션은 실제로 컬렉션을 포함하지 않으며 기본 컬렉션 주위에 래퍼를 제공합니다.WPF의 CollectionViewSource와 비슷합니다.ReadOnlyCollection도 비슷한 작업을 수행합니다.

중첩된 사전을 단일 컬렉션인 것처럼 열거할 수 있는 것으로 "평면화"할 수 있는 클래스를 만듭니다.컬렉션에서 항목을 제거하는 것처럼 보이지만 실제로는 현재 사전에서 제거하는 삭제 함수를 구현합니다.

내 생각에, 당신은 당신의 자신의 일반 클래스를 KeyValuePair<...> Tkey와 Tvalue가 모두있을 것입니다 List<T> 그리고 당신은 그것을 사용할 수 있습니다 RemoveAll 아니면 그 RemoveRangeList<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 (N)이며, 추측 할 수있는 것처럼 느립니다.

내일 내 대 기계에있을 때까지 테스트되지 않았다 : 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);
        }
    }
}

제 생각에는

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