Cara terbaik untuk menghapus item dari koleksi

Apa cara terbaik untuk pendekatan menghapus item dari koleksi di C#, setelah item dikenal, tapi bukan itu's indeks. Ini adalah salah satu cara untuk melakukan hal itu, tetapi tampaknya janggal di terbaik.

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

Apa yang saya benar-benar ingin lakukan adalah menemukan item untuk menghapus dengan properti (dalam hal ini, nama) tanpa perulangan melalui seluruh koleksi dan menggunakan 2 variabel tambahan.

Mengomentari pertanyaan (4)

Jika RoleAssignments adalah Daftar<T> anda dapat menggunakan kode berikut.

workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
Komentar (5)
Larutan

Jika anda ingin mengakses anggota dari koleksi oleh salah satu sifat mereka, anda mungkin mempertimbangkan menggunakan Kamus<T> atau KeyedCollection<T> sebagai gantinya. Dengan cara ini anda don't harus mencari item yang anda're looking for.

Jika tidak, anda setidaknya bisa melakukan hal ini:

foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
    if (spAssignment.Member.Name == shortName)
    {
        workspace.RoleAssignments.Remove(spAssignment);
        break;
    }
}
Komentar (3)

@smaclell bertanya mengapa reverse iterasi lebih efisien di dalam komentar untuk @sambo99.

Sometimes itu's lebih efisien. Anggap anda memiliki daftar orang-orang, dan anda ingin menghilangkan atau menyaring semua pelanggan dengan peringkat kredit < 1000;

Kami memiliki data sebagai berikut

"Bob" 999
"Mary" 999
"Ted" 1000

Jika kita beralih ke depan, kita'd segera mendapatkan masalah

for( int idx = 0; idx < list.Count ; idx++ )
{
    if( list[idx].Rating < 1000 )
    {
        list.RemoveAt(idx); // whoops!
    }
}

Di idx = 0 kami menghapus Bob, yang kemudian menggeser semua elemen yang tersisa kiri. Waktu berikutnya melalui loop idx = 1, tapi daftar[1] sekarang Ted bukan Maria. Kami akhirnya melewati Maria oleh kesalahan. Kita bisa menggunakan while loop, dan kita bisa memperkenalkan lebih variabel.

Atau, kita hanya membalikkan iterate:

for (int idx = list.Count-1; idx >= 0; idx--)
{
    if (list[idx].Rating < 1000)
    {
        list.RemoveAt(idx);
    }
}

Semua indeks ke kiri dari item yang dihapus tetap sama, sehingga anda don't lewatkan setiap item.

Prinsip yang sama berlaku jika anda're diberikan daftar indeks untuk menghapus dari array. Dalam rangka untuk menjaga hal-hal lurus yang anda butuhkan untuk mengurutkan daftar dan kemudian menghapus item dari index tertinggi sampai yang terendah.

Sekarang anda hanya dapat menggunakan Linq dan menyatakan apa yang anda're lakukan dengan cara yang sederhana.

list.RemoveAll(o => o.Rating < 1000);

Untuk kasus ini menghapus satu item,'s tidak ada yang lebih efisien iterasi ke depan atau ke belakang. Anda juga bisa menggunakan Linq untuk ini.

int removeIndex = list.FindIndex(o => o.Name == "Ted");
if( removeIndex != -1 )
{
    list.RemoveAt(removeIndex);
}
Komentar (7)

Untuk Daftar sederhana struktur cara yang paling efisien tampaknya akan menggunakan Predikat RemoveAll implementasi.

Misalnya.

 workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);

Alasan:

  1. Predikat/Linq RemoveAll metode ini dilaksanakan di Daftar dan memiliki akses ke internal array menyimpan data yang sebenarnya. Hal ini akan menggeser data dan mengubah ukuran array internal.
  2. Yang RemoveAt metode pelaksanaannya sangat lambat, dan akan menyalin seluruh mendasari data array ke array baru. Ini berarti reverse iterasi ini berguna untuk Daftar

Jika anda terjebak menerapkan ini dalam pra c# 3.0 era. Anda memiliki 2 pilihan.

  • Mudah dipelihara pilihan. Copy semua item yang sesuai dalam daftar baru dan dan swap yang mendasari daftar.

Misalnya.

List list2 = new List() ; 
foreach (int i in GetList())
{
    if (!(i % 2 == 0))
    {
        list2.Add(i);
    }
}
list2 = list2;

Atau

  • Rumit sedikit lebih cepat pilihan, yang melibatkan pergeseran semua data dalam daftar itu tidak sesuai dan kemudian mengubah ukuran array.

Jika anda menghapus hal-hal yang benar-benar sering dari daftar, mungkin struktur lain seperti HashTable (.net 1.1) atau Kamus (.net 2.0) atau HashSet (.net 3.5) yang lebih cocok untuk tujuan ini.

Komentar (0)

Jika itu's ICollection maka anda tidak't memiliki RemoveAll metode. Berikut ini's sebuah metode penyuluhan yang akan melakukannya:

    public static void RemoveAll(this ICollection source, 
                                    Func 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));
    }

Berdasarkan pada: http://phejndorf.wordpress.com/2011/03/09/a-removeall-extension-for-the-collection-class/

Komentar (1)

Apa jenis koleksi? Jika itu's Daftar, anda bisa gunakan membantu "RemoveAll":

int cnt = workspace.RoleAssignments
                      .RemoveAll(spa => spa.Member.Name == shortName)

(Ini bekerja di .NET 2.0. Tentu saja, jika anda don't memiliki baru compiler, anda'll harus menggunakan "mendelegasikan (SPRoleAssignment spa) { return spa.Anggota.Nama == shortName; " bukannya bagus lambda sintaks.)

Pendekatan lain jika itu's tidak Daftar, tapi masih ICollection:

   var toRemove = workspace.RoleAssignments
                              .FirstOrDefault(spa => spa.Member.Name == shortName)
   if (toRemove != null) workspace.RoleAssignments.Remove(toRemove);

Ini membutuhkan Enumerable metode ekstensi. (Anda dapat menyalin Mono yang dalam, jika anda terjebak di .NET 2.0). Jika itu's beberapa koleksi kustom yang tidak bisa mengambil item, tapi HARUS mengambil sebuah indeks, beberapa lainnya Enumerable metode, seperti Select, lulus dalam indeks integer untuk anda.

Komentar (0)

Berikut ini adalah cara yang cukup baik untuk melakukan itu

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();
Komentar (0)

Ini adalah solusi generik

public static IEnumerable Remove(this IEnumerable items, Func 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();
    }

Itu akan terlihat jauh lebih sederhana jika metode ekstensi dukungan lewat referensi ! penggunaan:

var result = string[]{"mike", "john", "ali"}
result = result.Remove(x => x.Username == "mike").ToArray();
Assert.IsTrue(result.Length == 2);

EDIT: memastikan bahwa daftar perulangan tetap berlaku bahkan ketika menghapus item dengan decrementing index (bei).

Komentar (4)

Cara terbaik untuk melakukannya adalah dengan menggunakan linq.

Contoh kelas:

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

Query ini akan menghapus semua elemen dari collection1 jika Nama pertandingan setiap elemen Nama dari collection2

Ingat untuk menggunakan: menggunakan Sistem.Linq;

Komentar (0)

Untuk melakukan hal ini sementara perulangan melalui pengumpulan dan tidak mendapatkan memodifikasi koleksi pengecualian, ini adalah pendekatan yang saya'telah diambil di masa lalu (perhatikan .Kedaftar() pada akhir dari koleksi asli, hal ini menciptakan koleksi lain dalam memori, maka anda dapat memodifikasi koleksi yang ada)

foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments.ToList())
{
    if (spAssignment.Member.Name == shortName)
    {
        workspace.RoleAssignments.Remove(spAssignment);
    }
}
Komentar (0)

Ada pendekatan lain yang dapat anda ambil tergantung pada bagaimana anda're menggunakan koleksi anda. Jika anda're men-download tugas satu waktu (misalnya, ketika aplikasi berjalan), anda bisa menerjemahkan koleksi terbang ke hashtable dimana:

shortname => SPRoleAssignment

Jika anda melakukan ini, maka ketika anda ingin menghapus item dengan nama pendek, semua yang perlu anda lakukan adalah menghapus item dari hashtable oleh kunci.

Sayangnya, jika anda're loading ini SPRoleAssignments banyak, yang jelas isn't akan ada biaya yang lebih efisien dalam hal waktu. Saran-saran orang lain tentang menggunakan Linq akan lebih baik jika anda'kembali menggunakan versi baru .NET Framework, tapi jika tidak, anda'll harus tetap berpegang pada metode anda're menggunakan.

Komentar (0)

Serupa dengan Kamus Pengumpulan point of view, yang telah saya lakukan ini.

Dictionary sourceDict = new Dictionary();
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);
}

Catatan: Kode di atas akan gagal dalam .Net Profil Klien (3.5 dan 4.5) juga beberapa penonton yang disebutkan itu adalah Gagal untuk mereka .Net4.0 juga tidak yakin setelan mana yang menyebabkan masalah.

Jadi ganti dengan kode di bawah ini (.Kedaftar()) Dimana pernyataan, untuk menghindari kesalahan itu. "Koleksi dimodifikasi; pencacahan operasi mungkin tidak dapat mengeksekusi."

var itemsToDelete = sourceDict.Where(DictItem => DictItem.Value == false).ToList();

Per MSDN Dari .Net4.5 dan seterusnya Profil Klien dihentikan. http://msdn.microsoft.com/en-us/library/cc656912(v=vs. 110).aspx

Komentar (0)

Menyimpan barang-barang anda terlebih dahulu, dari menghapusnya.

var itemsToDelete = Items.Where(x => !!!your condition!!!).ToArray();
for (int i = 0; i < itemsToDelete.Length; ++i)
    Items.Remove(itemsToDelete[i]);

Anda perlu mengganti GetHashCode() di kelas Item.

Komentar (0)

Banyak respon yang baik di sini, saya sangat suka ekspresi lambda...sangat bersih. Aku lalai, bagaimanapun, tidak menentukan jenis Koleksi. Ini adalah SPRoleAssignmentCollection (dari LUMUT) yang hanya telah Menghapus(int) dan Menghapus(SPPrincipal), tidak berguna RemoveAll(). Jadi, saya telah menetap ini, kecuali ada saran yang lebih baik.

foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
                        {
                            if (spAssignment.Member.Name != shortName) continue;
                            workspace.RoleAssignments.Remove((SPPrincipal)spAssignment.Member);
                            break;
                        }
Komentar (1)