컬렉션에서 항목을 제거하는 가장 좋은 방법
항목이 알려져 있지만 인덱스가 아닌 C #의 컬렉션에서 항목을 제거하는 가장 좋은 방법은 무엇입니까? 이것은 그것을 수행하는 한 가지 방법이지만 기껏해야 우아하지 않은 것 같습니다.
//Remove the existing role assignment for the user.
int cnt = 0;
int assToDelete = 0;
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
if (spAssignment.Member.Name == shortName)
{
assToDelete = cnt;
}
cnt++;
}
workspace.RoleAssignments.Remove(assToDelete);
내가 정말로하고 싶은 것은 전체 컬렉션을 반복하고 2 개의 추가 변수를 사용하지 않고 속성 (이 경우 이름)별로 제거 할 항목을 찾는 것입니다.
당신은 그 속성 중 하나를 사용하여 컬렉션의 멤버에의 액세스하려는 경우 사용하는 것이 좋습니다 Dictionary<T>
또는 KeyedCollection<T>
대신. 이렇게하면 찾고있는 항목을 검색 할 필요가 없습니다.
그렇지 않으면 적어도 다음과 같이 할 수 있습니다.
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
if (spAssignment.Member.Name == shortName)
{
workspace.RoleAssignments.Remove(spAssignment);
break;
}
}
RoleAssignments가 a List<T>
이면 다음 코드를 사용할 수 있습니다.
workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
@smaclell은 @ sambo99에 대한 의견에서 역 반복이 더 효율적인 이유를 물었습니다.
때로는 더 효율적입니다. 사람 목록이 있고 신용 등급이 1000 미만인 모든 고객을 제거하거나 필터링하려고한다고 가정합니다.
우리는 다음과 같은 데이터를 가지고 있습니다
"Bob" 999
"Mary" 999
"Ted" 1000
우리가 앞으로 반복한다면 곧 문제가 될 것입니다.
for( int idx = 0; idx < list.Count ; idx++ )
{
if( list[idx].Rating < 1000 )
{
list.RemoveAt(idx); // whoops!
}
}
idx = 0에서를 제거한 Bob
다음 나머지 모든 요소를 왼쪽으로 이동합니다. 루프 IDX = 1이지만 목록 [1] 내지 다음번 지금 Ted
대신 Mary
. Mary
실수로 건너 뛰게 됩니다. while 루프를 사용할 수 있고 더 많은 변수를 도입 할 수 있습니다.
또는 우리는 단지 역 반복합니다.
for (int idx = list.Count-1; idx >= 0; idx--)
{
if (list[idx].Rating < 1000)
{
list.RemoveAt(idx);
}
}
제거 된 항목의 왼쪽에있는 모든 인덱스는 동일하게 유지되므로 항목을 건너 뛰지 않습니다.
배열에서 제거 할 인덱스 목록이 제공된 경우에도 동일한 원칙이 적용됩니다. 일을 똑바로 유지하려면 목록을 정렬 한 다음 가장 높은 인덱스에서 가장 낮은 항목으로 항목을 제거해야합니다.
이제 Linq를 사용하고 수행중인 작업을 간단하게 선언 할 수 있습니다.
list.RemoveAll(o => o.Rating < 1000);
단일 항목을 제거하는이 경우 앞뒤로 반복하는 것이 더 효율적이지 않습니다. 이를 위해 Linq를 사용할 수도 있습니다.
int removeIndex = list.FindIndex(o => o.Name == "Ted");
if( removeIndex != -1 )
{
list.RemoveAt(removeIndex);
}
간단한 List 구조의 경우 가장 효율적인 방법은 Predicate RemoveAll 구현을 사용하는 것 같습니다.
예 :
workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
그 이유는 다음과 같습니다.
- Predicate / Linq RemoveAll 메서드는 List에서 구현되며 실제 데이터를 저장하는 내부 배열에 액세스 할 수 있습니다. 데이터를 이동하고 내부 배열의 크기를 조정합니다.
- RemoveAt 메서드 구현은 매우 느리며 전체 기본 데이터 배열을 새 배열로 복사합니다. 이것은 역방향 반복이 List에 쓸모가 없음을 의미합니다.
이전 C # 3.0 시대에 이것을 구현하는 데 어려움을 겪고 있다면. 두 가지 옵션이 있습니다.
- 쉽게 관리 할 수있는 옵션입니다. 일치하는 모든 항목을 새 목록에 복사하고 기본 목록을 바꿉니다.
예 :
List<int> list2 = new List<int>() ;
foreach (int i in GetList())
{
if (!(i % 2 == 0))
{
list2.Add(i);
}
}
list2 = list2;
또는
- 약간 더 빠른 옵션으로, 일치하지 않을 때 목록의 모든 데이터를 아래로 이동 한 다음 배열 크기를 조정합니다.
목록에서 항목을 정말 자주 제거하는 경우 HashTable (.net 1.1) 또는 Dictionary (.net 2.0) 또는 HashSet (.net 3.5) 과 같은 다른 구조 가이 목적에 더 적합합니다.
그렇다면 방법 ICollection
이 없습니다 RemoveAll
. 이를 수행하는 확장 메서드는 다음과 같습니다.
public static void RemoveAll<T>(this ICollection<T> source,
Func<T, bool> predicate)
{
if (source == null)
throw new ArgumentNullException("source", "source is null.");
if (predicate == null)
throw new ArgumentNullException("predicate", "predicate is null.");
source.Where(predicate).ToList().ForEach(e => source.Remove(e));
}
기반 : http://phejndorf.wordpress.com/2011/03/09/a-removeall-extension-for-the-collection-class/
컬렉션은 어떤 유형입니까? 목록 인 경우 유용한 "RemoveAll"을 사용할 수 있습니다.
int cnt = workspace.RoleAssignments
.RemoveAll(spa => spa.Member.Name == shortName)
(이는 .NET 2.0에서 작동합니다. 물론 최신 컴파일러가없는 경우에는 nice 대신 "delegate (SPRoleAssignment spa) {return spa.Member.Name == shortName;}"을 사용해야합니다. 람다 구문.)
목록이 아니지만 여전히 ICollection 인 경우 다른 방법 :
var toRemove = workspace.RoleAssignments
.FirstOrDefault(spa => spa.Member.Name == shortName)
if (toRemove != null) workspace.RoleAssignments.Remove(toRemove);
여기에는 Enumerable 확장 메서드가 필요합니다. (.NET 2.0에서 멈춘 경우 Mono를 복사 할 수 있습니다.) 항목을 가져올 수 없지만 인덱스를 가져와야하는 사용자 지정 컬렉션 인 경우 Select와 같은 다른 Enumerable 메서드 중 일부가 정수 인덱스를 전달합니다.
이것은 내 일반적인 솔루션입니다.
public static IEnumerable<T> Remove<T>(this IEnumerable<T> items, Func<T, bool> match)
{
var list = items.ToList();
for (int idx = 0; idx < list.Count(); idx++)
{
if (match(list[idx]))
{
list.RemoveAt(idx);
idx--; // the list is 1 item shorter
}
}
return list.AsEnumerable();
}
확장 메서드가 참조에 의한 전달을 지원한다면 훨씬 더 간단 해 보일 것입니다! 용법:
var result = string[]{"mike", "john", "ali"}
result = result.Remove(x => x.Username == "mike").ToArray();
Assert.IsTrue(result.Length == 2);
편집 : 인덱스 (idx)를 줄여 항목을 삭제할 때에도 목록 루핑이 유효한 상태로 유지되도록합니다.
여기에 아주 좋은 방법이 있습니다.
http://support.microsoft.com/kb/555972
System.Collections.ArrayList arr = new System.Collections.ArrayList();
arr.Add("1");
arr.Add("2");
arr.Add("3");
/*This throws an exception
foreach (string s in arr)
{
arr.Remove(s);
}
*/
//where as this works correctly
Console.WriteLine(arr.Count);
foreach (string s in new System.Collections.ArrayList(arr))
{
arr.Remove(s);
}
Console.WriteLine(arr.Count);
Console.ReadKey();
컬렉션을 사용하는 방법에 따라 취할 수있는 또 다른 접근 방식이 있습니다. 할당을 한 번 다운로드하는 경우 (예 : 앱이 실행될 때) 즉시 컬렉션을 해시 테이블로 변환 할 수 있습니다.
짧은 이름 => SPRoleAssignment
If you do this, then when you want to remove an item by short name, all you need to do is remove the item from the hashtable by key.
Unfortunately, if you're loading these SPRoleAssignments a lot, that obviously isn't going to be any more cost efficient in terms of time. The suggestions other people made about using Linq would be good if you're using a new version of the .NET Framework, but otherwise, you'll have to stick to the method you're using.
A lot of good responses here; I especially like the lambda expressions...very clean. I was remiss, however, in not specifying the type of Collection. This is a SPRoleAssignmentCollection (from MOSS) that only has Remove(int) and Remove(SPPrincipal), not the handy RemoveAll(). So, I have settled on this, unless there is a better suggestion.
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
if (spAssignment.Member.Name != shortName) continue;
workspace.RoleAssignments.Remove((SPPrincipal)spAssignment.Member);
break;
}
Similar to Dictionary Collection point of view, I have done this.
Dictionary<string, bool> sourceDict = new Dictionary<string, bool>();
sourceDict.Add("Sai", true);
sourceDict.Add("Sri", false);
sourceDict.Add("SaiSri", true);
sourceDict.Add("SaiSriMahi", true);
var itemsToDelete = sourceDict.Where(DictItem => DictItem.Value == false);
foreach (var item in itemsToDelete)
{
sourceDict.Remove(item.Key);
}
Note: Above code will fail in .Net Client Profile (3.5 and 4.5) also some viewers mentioned it is Failing for them in .Net4.0 as well not sure which settings are causing the problem.
So replace with below code (.ToList()) for Where statement, to avoid that error. “Collection was modified; enumeration operation may not execute.”
var itemsToDelete = sourceDict.Where(DictItem => DictItem.Value == false).ToList();
Per MSDN From .Net4.5 onwards Client Profile are discontinued. http://msdn.microsoft.com/en-us/library/cc656912(v=vs.110).aspx
Save your items first, than delete them.
var itemsToDelete = Items.Where(x => !!!your condition!!!).ToArray();
for (int i = 0; i < itemsToDelete.Length; ++i)
Items.Remove(itemsToDelete[i]);
You need to override GetHashCode()
in your Item class.
The best way to do it is by using linq.
Example class:
public class Product
{
public string Name { get; set; }
public string Price { get; set; }
}
Linq query:
var subCollection = collection1.RemoveAll(w => collection2.Any(q => q.Name == w.Name));
This query will remove all elements from collection1
if Name
match any element Name
from collection2
Remember to use: using System.Linq;
To do this while looping through the collection and not to get the modifying a collection exception, this is the approach I've taken in the past (note the .ToList() at the end of the original collection, this creates another collection in memory, then you can modify the existing collection)
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments.ToList())
{
if (spAssignment.Member.Name == shortName)
{
workspace.RoleAssignments.Remove(spAssignment);
}
}
참고URL : https://stackoverflow.com/questions/207038/best-way-to-remove-items-from-a-collection
'Nice programing' 카테고리의 다른 글
앱이 전체 화면을 요구하지 않는 한 출시 스토리 보드 또는 xib를 제공해야합니다. (0) | 2020.11.09 |
---|---|
Angular 2-이미지 URL이 유효하거나 손상되었는지 확인 (0) | 2020.11.09 |
코드에서 div를 숨기는 방법 (C #) (0) | 2020.11.09 |
Jackson을 사용하여 JS 날짜를 역 직렬화하는 방법은 무엇입니까? (0) | 2020.11.09 |
스케일이 다른 matplotlib의 다중 축 (0) | 2020.11.09 |