Quelle est la meilleure façon de supprimer des éléments d'une collection en C #, une fois que l'élément est connu, mais pas son index. C'est une façon de le faire, mais cela semble au mieux inélégant.
//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);
Ce que j'aimerais vraiment faire, c'est trouver l'élément à supprimer par propriété (dans ce cas, nom) sans parcourir en boucle toute la collection et utiliser 2 variables supplémentaires.
Si vous souhaitez accéder aux membres de la collection par l’une de leurs propriétés, vous pouvez utiliser plutôt un Dictionary<T>
ou un KeyedCollection<T>
. De cette façon, vous n'avez pas à rechercher l'élément que vous recherchez.
Sinon, vous pourriez au moins faire ceci:
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
if (spAssignment.Member.Name == shortName)
{
workspace.RoleAssignments.Remove(spAssignment);
break;
}
}
Si RoleAssignments est un List<T>
, vous pouvez utiliser le code suivant.
workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
@smaclell a demandé pourquoi l'itération inverse était plus efficace dans un commentaire à @ sambo99.
Parfois c'est plus efficace. Considérez que vous avez une liste de personnes et que vous souhaitez supprimer ou filtrer tous les clients dont la cote de crédit est inférieure à 1 000;
Nous avons les données suivantes
"Bob" 999
"Mary" 999
"Ted" 1000
Si nous devions avancer, nous aurions bientôt des ennuis
for( int idx = 0; idx < list.Count ; idx++ )
{
if( list[idx].Rating < 1000 )
{
list.RemoveAt(idx); // whoops!
}
}
À idx = 0, nous supprimons Bob
, qui déplace alors tous les éléments restants. La prochaine fois dans la boucle idx = 1, mais list [1] est maintenant Ted
au lieu de Mary
. Nous finissons par sauter Mary
par erreur. Nous pourrions utiliser une boucle while et introduire davantage de variables.
Ou bien, nous inversons simplement l'itération:
for (int idx = list.Count-1; idx >= 0; idx--)
{
if (list[idx].Rating < 1000)
{
list.RemoveAt(idx);
}
}
Tous les index à gauche de l'élément supprimé restent les mêmes, vous ne sautez donc aucun élément.
Le même principe s'applique si vous recevez une liste d'index à supprimer d'un tableau. Pour que tout soit clair, vous devez trier la liste, puis supprimer les éléments du plus haut index au plus bas.
Maintenant, vous pouvez simplement utiliser Linq et déclarer ce que vous faites de manière simple.
list.RemoveAll(o => o.Rating < 1000);
Dans ce cas de suppression d'un seul élément, il n'est pas plus efficace d'effectuer une itération en avant ou en arrière. Vous pouvez également utiliser Linq pour cela.
int removeIndex = list.FindIndex(o => o.Name == "Ted");
if( removeIndex != -1 )
{
list.RemoveAt(removeIndex);
}
Pour une structure de liste simple, le moyen le plus efficace semble utiliser l'implémentation Predicate RemoveAll.
Par exemple.
workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
Les raisons sont:
Si vous êtes coincé à mettre cela en œuvre dans une ère pré-c # 3.0. Vous avez 2 options.
Par exemple.
List<int> list2 = new List<int>() ;
foreach (int i in GetList())
{
if (!(i % 2 == 0))
{
list2.Add(i);
}
}
list2 = list2;
Ou
Si vous supprimez fréquemment des éléments d'une liste, peut-être une autre structure telle qu'un HashTable (.net 1.1) ou un Dictionary (.net 2.0) ou un HashSet (.net 3.5) sont mieux adaptés à cette fin.
S'il s'agit d'une ICollection
, vous n'aurez pas de méthode RemoveAll
. Voici une méthode d'extension qui le fera:
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));
}
Basé sur: http://phejndorf.wordpress.com/2011/03/09/a-removeall-extension-for-the-collection-class/
De quel type est la collection? Si c'est la liste, vous pouvez utiliser l'utile "RemoveAll":
int cnt = workspace.RoleAssignments
.RemoveAll(spa => spa.Member.Name == shortName)
(Cela fonctionne dans .NET 2.0. Bien sûr, si vous ne possédez pas le nouveau compilateur, vous devrez utiliser "delegate (SPRoleAssignment spa) {return spa.Member.Name == shortName;}" à la place de Nice syntaxe lambda.)
Une autre approche si ce n'est pas une liste, mais toujours une ICollection:
var toRemove = workspace.RoleAssignments
.FirstOrDefault(spa => spa.Member.Name == shortName)
if (toRemove != null) workspace.RoleAssignments.Remove(toRemove);
Cela nécessite les méthodes d'extension Enumerable. (Vous pouvez copier les fichiers Mono dans, si vous êtes bloqué sur .NET 2.0). S'il s'agit d'une collection personnalisée qui ne peut pas prendre un élément, mais DOIT prendre un index, certaines des autres méthodes Enumerable, telles que Select, transmettent l'index entier à votre place.
Voici un très bon moyen de le faire
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();
Ceci est ma solution générique
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();
}
Cela semblerait beaucoup plus simple si les méthodes d’extension prennent en charge le passage par référence! Usage:
var result = string[]{"mike", "john", "ALi"}
result = result.Remove(x => x.Username == "mike").ToArray();
Assert.IsTrue(result.Length == 2);
EDIT: assure que la boucle de liste reste valide même lors de la suppression d'éléments en décrémentant l'index (idx).
Pour ce faire tout en parcourant la collection et ne pas obtenir l’exception de modification d’une collection, c’est l’approche que j’ai adoptée par le passé (notez .ToList () à la fin de la collection originale, cela crée une autre collection en mémoire , alors vous pouvez modifier la collection existante)
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments.ToList())
{
if (spAssignment.Member.Name == shortName)
{
workspace.RoleAssignments.Remove(spAssignment);
}
}
La meilleure façon de le faire est d'utiliser linq.
Exemple de classe:
public class Product
{
public string Name { get; set; }
public string Price { get; set; }
}
Requête Linq:
var subCollection = collection1.RemoveAll(w => collection2.Any(q => q.Name == w.Name));
Cette requête supprimera tous les éléments de collection1
si Name
correspond à un élément Name
de collection2
N'oubliez pas d'utiliser: using System.Linq;
Beaucoup de bonnes réponses ici; J'aime particulièrement les expressions lambda ... très propres. Cependant, j'ai été négligent de ne pas spécifier le type de collection. Il s'agit d'une SPRoleAssignmentCollection (à partir de MOSS) qui ne comporte que Remove (int) et Remove (SPPrincipal), mais pas le très pratique RemoveAll (). Je me suis donc décidé, à moins que la suggestion ne soit meilleure.
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
if (spAssignment.Member.Name != shortName) continue;
workspace.RoleAssignments.Remove((SPPrincipal)spAssignment.Member);
break;
}
Semblable au point de vue Collection de dictionnaires, je l'ai fait.
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);
}
Remarque: Le code ci-dessus échouera dans .Net Client Profile (3.5 et 4.5). Certains téléspectateurs ont mentionné qu'il était Ils ont échoué dans .Net4.0 et ne savent pas exactement quels paramètres sont à l'origine du problème. .
Remplacez donc par le code ci-dessous (.ToList ()) pour l'instruction Where, afin d'éviter cette erreur. «La collection a été modifiée. l'opération d'énumération peut ne pas s'exécuter. ”
var itemsToDelete = sourceDict.Where(DictItem => DictItem.Value == false).ToList();
Par MSDN à partir de .Net4.5, les profils de clients sont abandonnés. http://msdn.Microsoft.com/en-us/library/cc656912(v=vs.110).aspx
Il existe une autre approche que vous pouvez adopter en fonction de la manière dont vous utilisez votre collection. Si vous téléchargez les assignations une fois (par exemple, lorsque l'application est exécutée), vous pouvez traduire la collection à la volée en une table de hachage où:
shortname => SPRoleAssignment
Si vous procédez ainsi, lorsque vous souhaitez supprimer un élément par son nom abrégé, il vous suffit de supprimer l’élément de la table de hachage par clé.
Malheureusement, si vous chargez souvent ces SPRoleAssignments, cela ne sera évidemment pas plus rentable en termes de temps. Les suggestions que d'autres personnes ont faites sur l'utilisation de Linq seraient utiles si vous utilisez une nouvelle version du .NET Framework, mais sinon, vous devrez vous en tenir à la méthode que vous utilisez.
Enregistrez d'abord vos éléments, puis supprimez-les.
var itemsToDelete = Items.Where(x => !!!your condition!!!).ToArray();
for (int i = 0; i < itemsToDelete.Length; ++i)
Items.Remove(itemsToDelete[i]);
Vous devez remplacer GetHashCode()
dans votre classe Item.