재귀를 사용하여 IDictionary에서 항목 제거
-
04-07-2019 - |
문제
누구든지 이 작업을 수행하는 더 매끄러운 방법이 있습니까?이보다 더 쉬울 것 같지만 정신적인 장애가 있습니다.기본적으로 사전에서 항목을 제거하고 사전이기도 한 항목의 값으로 재귀적으로 들어가야 합니다.
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
아니면 그 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 (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();
}
빨리 작동해야합니다