Hapus elemen array biasa

135

Saya memiliki berbagai objek Foo. Bagaimana cara menghapus elemen kedua array?

Saya perlu sesuatu yang mirip RemoveAt()tetapi untuk array reguler.

leora
sumber
1
Gunakan System.Collections.ObjectModel.Collection<Foo>.
abatishchev
1
Untuk permainan saya, saya menggunakan datastruktur "null at index". Pada dasarnya, array dalam (buffer) berukuran statis, dan bukannya menghapus indeks dan mengubah ukuran array, saya hanya membuat indeks nol. Ketika saya perlu menambahkan item, saya hanya menemukan indeks non-nol pertama dan letakkan di sana. Bekerja dengan cukup baik, tetapi jelas tidak untuk semuanya.
Krythic

Jawaban:

202

Jika Anda tidak ingin menggunakan Daftar:

var foos = new List<Foo>(array);
foos.RemoveAt(index);
return foos.ToArray();

Anda dapat mencoba metode ekstensi ini yang belum saya uji:

public static T[] RemoveAt<T>(this T[] source, int index)
{
    T[] dest = new T[source.Length - 1];
    if( index > 0 )
        Array.Copy(source, 0, dest, 0, index);

    if( index < source.Length - 1 )
        Array.Copy(source, index + 1, dest, index, source.Length - index - 1);

    return dest;
}

Dan gunakan seperti:

Foo[] bar = GetFoos();
bar = bar.RemoveAt(2);
Andrew Kennan
sumber
8
Contoh pertama yang diberikan dalam jawaban ini jauh lebih efisien daripada yang kedua. Ini membutuhkan dua salinan array dan perubahan segalanya setelah indeks daripada satu salinan array selektif.
Martin Brown
2
+1 tentu saja, tetapi kita juga dapat menggunakan daftar juga ATAU Daftar <Foo> daftar = Daftar baru <Foll> (GetFoos ()); list.Remove (my_foo); list.RemoveAt (2); di mana GetFoos () akan mengembalikan array Foos !!!!
shahjapan
2
Baris pertama di dalam metode harus mengatakan 'source.Length' bukan 'array.Length'.
Nelson
1
Juga, perlu diingat bahwa setiap variabel yang menyimpan referensi ke array asli akan terus berisi data asli dan bahwa setiap perbandingan kesetaraan referensi antara array dalam sumber dan array output akan menghasilkan negatif.
bkqc
1
@ MartinBrown Sebenarnya, mengubah daftar menjadi \ dari dan array jauh lebih lambat daripada salinan array (yang mampu menyalin data pada kecepatan maksimal yang diizinkan oleh CPU dengan hanya beberapa instruksi ASM). Juga, menggeser daftar sangat cepat karena hanya masalah menukar beberapa pointer dan menghapus data node (yang hanya 8 byte [ditambah 16 lainnya untuk head \ tail pointer] dalam kasus ini).
krowe2
66

Sifat array adalah bahwa panjangnya tidak berubah. Anda tidak dapat menambah atau menghapus salah satu item array.

Anda harus membuat array baru yang satu elemen lebih pendek dan menyalin item lama ke array baru, tidak termasuk elemen yang ingin Anda hapus.

Jadi mungkin lebih baik menggunakan Daftar daripada array.

Sebastian Dietz
sumber
4
Ubah array menjadi daftarList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Immortal Blue
1
@ImmortalBlue atau hanya var myList = myArray.ToList();menggunakan Enumerable.ToList()metode dari System.Linqnamespace.
Dyndrilliac
58

Saya menggunakan metode ini untuk menghapus elemen dari array objek. Dalam situasi saya, panjang array saya kecil. Jadi jika Anda memiliki array besar Anda mungkin perlu solusi lain.

private int[] RemoveIndices(int[] IndicesArray, int RemoveAt)
{
    int[] newIndicesArray = new int[IndicesArray.Length - 1];

    int i = 0;
    int j = 0;
    while (i < IndicesArray.Length)
    {
        if (i != RemoveAt)
        {
            newIndicesArray[j] = IndicesArray[i];
            j++;
        }

        i++;
    }

    return newIndicesArray;
}
EdHellyer
sumber
7
Secara pribadi, saya suka jawaban ini lebih baik daripada jawaban yang diterima. Seharusnya sama efisiennya, dan lebih mudah dibaca. Saya bisa melihatnya dan tahu itu benar. Saya harus menguji yang lain untuk memastikan salinan itu ditulis dengan benar.
oillio
1
Sayang sekali jawaban ini sangat rendah, padahal jauh lebih baik daripada dua di atasnya.
Sepulchritude
Aaarhg, itulah jawaban yang saya cari! Ini adalah metode terbaik tanpa Daftar.
Jordi Huertas
47

Solusi satu jalur LINQ:

myArray = myArray.Where((source, index) => index != 1).ToArray();

Dalam 1contoh itu adalah indeks elemen yang akan dihapus - dalam contoh ini, per pertanyaan awal, elemen ke-2 (dengan 1menjadi elemen kedua dalam pengindeksan array berbasis C # nol).

Contoh yang lebih lengkap:

string[] myArray = { "a", "b", "c", "d", "e" };
int indexToRemove = 1;
myArray = myArray.Where((source, index) => index != indexToRemove).ToArray();

Setelah menjalankan cuplikan itu, nilai myArrayakan { "a", "c", "d", "e" }.

Jon Schneider
sumber
1
Untuk area yang membutuhkan akses tinggi / kinerja sering, LINQ tidak direkomendasikan.
Krythic
3
@Krythic Itu komentar yang adil. Jalankan ribuan kali dalam satu lingkaran ketat, kinerja solusi ini tidak sebagus beberapa solusi lain yang sangat banyak dipilih pada halaman ini: dotnetfiddle.net/z9Xkpn
Jon Schneider
9

Ini adalah cara untuk menghapus elemen array, pada .Net 3.5, tanpa menyalin ke array lain - menggunakan contoh array yang sama dengan Array.Resize<T>:

public static void RemoveAt<T>(ref T[] arr, int index)
{
    for (int a = index; a < arr.Length - 1; a++)
    {
        // moving elements downwards, to fill the gap at [index]
        arr[a] = arr[a + 1];
    }
    // finally, let's decrement Array's size by one
    Array.Resize(ref arr, arr.Length - 1);
}
infografnet
sumber
2
"tanpa menyalin ke array lain" - per dokumentasi terkait, Array.Resize sebenarnya tidak mengalokasikan array baru di belakang layar, dan salinan elemen dari array lama ke yang baru. Tetap saja, saya suka keringkasan solusi ini.
Jon Schneider
Sangat bagus dan jelas jika Anda yakin itu array yang relatif kecil.
Darren
1
Melanjutkan komentar JonSchneider, itu bukan "contoh array yang sama". Itulah mengapa Anda perlu menggunakan refsaat Anda memanggil Resizemetode. Panjang instance array adalah tetap dan tidak berubah.
Jeppe Stig Nielsen
2
Jika urutan elemen tidak penting, alih-alih memindahkan semua elemen ke bawah, Anda dapat menukar elemen pada indeks dengan elemen terakhir dan kemudian mengubah ukuran: arr [index] = arr [arr.Length - 1]; Array.Resize (ref arr, arr.Length - 1);
Bartel
5

Berikut ini adalah versi lama yang saya miliki yang berfungsi pada versi 1.0 .NET framework dan tidak perlu tipe generik.

public static Array RemoveAt(Array source, int index)
{
    if (source == null)
        throw new ArgumentNullException("source");

    if (0 > index || index >= source.Length)
        throw new ArgumentOutOfRangeException("index", index, "index is outside the bounds of source array");

    Array dest = Array.CreateInstance(source.GetType().GetElementType(), source.Length - 1);
    Array.Copy(source, 0, dest, 0, index);
    Array.Copy(source, index + 1, dest, index, source.Length - index - 1);

    return dest;
}

Ini digunakan seperti ini:

class Program
{
    static void Main(string[] args)
    {
        string[] x = new string[20];
        for (int i = 0; i < x.Length; i++)
            x[i] = (i+1).ToString();

        string[] y = (string[])MyArrayFunctions.RemoveAt(x, 3);

        for (int i = 0; i < y.Length; i++)
            Console.WriteLine(y[i]);
    }
}
Martin Brown
sumber
3

Bukan cara yang tepat untuk melakukannya, tetapi jika situasinya sepele dan Anda menghargai waktu Anda, Anda dapat mencoba ini untuk jenis yang dapat dibatalkan.

Foos[index] = null

dan kemudian periksa entri nol dalam logika Anda ..

nawfal
sumber
Ini adalah bagaimana saya melakukannya untuk permainan saya. Pergi dengan buffer nullable untuk area yang sangat sering diubah.
Krythic
2

Seperti biasa, aku terlambat ke pesta ...

Saya ingin menambahkan opsi lain ke daftar solusi bagus yang sudah ada. =)
Saya akan melihat ini sebagai peluang bagus untuk Ekstensi.

Referensi: http://msdn.microsoft.com/en-us/library/bb311042.aspx

Jadi, kita mendefinisikan beberapa kelas statis dan di dalamnya, Metode kita.
Setelah itu, kita bisa menggunakan metode extended kita mau tak mau. =)

using System;

namespace FunctionTesting {

    // The class doesn't matter, as long as it's static
    public static class SomeRandomClassWhoseNameDoesntMatter {

        // Here's the actual method that extends arrays
        public static T[] RemoveAt<T>( this T[] oArray, int idx ) {
            T[] nArray = new T[oArray.Length - 1];
            for( int i = 0; i < nArray.Length; ++i ) {
                nArray[i] = ( i < idx ) ? oArray[i] : oArray[i + 1];
            }
            return nArray;
        }
    }

    // Sample usage...
    class Program {
        static void Main( string[] args ) {
            string[] myStrArray = { "Zero", "One", "Two", "Three" };
            Console.WriteLine( String.Join( " ", myStrArray ) );
            myStrArray = myStrArray.RemoveAt( 2 );
            Console.WriteLine( String.Join( " ", myStrArray ) );
            /* Output
             * "Zero One Two Three"
             * "Zero One Three"
             */

            int[] myIntArray = { 0, 1, 2, 3 };
            Console.WriteLine( String.Join( " ", myIntArray ) );
            myIntArray = myIntArray.RemoveAt( 2 );
            Console.WriteLine( String.Join( " ", myIntArray ) );
            /* Output
             * "0 1 2 3"
             * "0 1 3"
             */
        }
    }
}
Duncan
sumber
2

Coba kode di bawah ini:

myArray = myArray.Where(s => (myArray.IndexOf(s) != indexValue)).ToArray();

atau

myArray = myArray.Where(s => (s != "not_this")).ToArray();
NovatechGuy
sumber
1

Begini cara saya melakukannya ...

    public static ElementDefinitionImpl[] RemoveElementDefAt(
        ElementDefinition[] oldList,
        int removeIndex
    )
    {
        ElementDefinitionImpl[] newElementDefList = new ElementDefinitionImpl[ oldList.Length - 1 ];

        int offset = 0;
        for ( int index = 0; index < oldList.Length; index++ )
        {
            ElementDefinitionImpl elementDef = oldList[ index ] as ElementDefinitionImpl;
            if ( index == removeIndex )
            {
                //  This is the one we want to remove, so we won't copy it.  But 
                //  every subsequent elementDef will by shifted down by one.
                offset = -1;
            }
            else
            {
                newElementDefList[ index + offset ] = elementDef;
            }
        }
        return newElementDefList;
    }
Paul Mitchell
sumber
1

Dalam array normal Anda harus mengacak semua entri array di atas 2 dan kemudian mengubah ukurannya menggunakan metode Ubah Ukuran. Anda mungkin lebih baik menggunakan ArrayList.

gkrogers
sumber
1
    private int[] removeFromArray(int[] array, int id)
    {
        int difference = 0, currentValue=0;
        //get new Array length
        for (int i=0; i<array.Length; i++)
        {
            if (array[i]==id)
            {
                difference += 1;
            }
        }
        //create new array
        int[] newArray = new int[array.Length-difference];
        for (int i = 0; i < array.Length; i++ )
        {
            if (array[i] != id)
            {
                newArray[currentValue] = array[i];
                currentValue += 1;
            }
        }

        return newArray;
    }
pengguna2884232
sumber
0

Berikut adalah kumpulan kecil metode pembantu yang saya hasilkan berdasarkan beberapa jawaban yang ada. Ini menggunakan ekstensi dan metode statis dengan parameter referensi untuk idealness maksimum:

public static class Arr
{
    public static int IndexOf<TElement>(this TElement[] Source, TElement Element)
    {
        for (var i = 0; i < Source.Length; i++)
        {
            if (Source[i].Equals(Element))
                return i;
        }

        return -1;
    }

    public static TElement[] Add<TElement>(ref TElement[] Source, params TElement[] Elements)
    {
        var OldLength = Source.Length;
        Array.Resize(ref Source, OldLength + Elements.Length);

        for (int j = 0, Count = Elements.Length; j < Count; j++)
            Source[OldLength + j] = Elements[j];

        return Source;
    }

    public static TElement[] New<TElement>(params TElement[] Elements)
    {
        return Elements ?? new TElement[0];
    }

    public static void Remove<TElement>(ref TElement[] Source, params TElement[] Elements)
    {
        foreach (var i in Elements)
            RemoveAt(ref Source, Source.IndexOf(i));
    }

    public static void RemoveAt<TElement>(ref TElement[] Source, int Index)
    {
        var Result = new TElement[Source.Length - 1];

        if (Index > 0)
            Array.Copy(Source, 0, Result, 0, Index);

        if (Index < Source.Length - 1)
            Array.Copy(Source, Index + 1, Result, Index, Source.Length - Index - 1);

        Source = Result;
    }
}

Dari segi kinerja, memang layak, tetapi mungkin bisa ditingkatkan. Removemengandalkan IndexOfdan array baru dibuat untuk setiap elemen yang ingin Anda hapus dengan menelepon RemoveAt.

IndexOfadalah satu-satunya metode ekstensi karena tidak perlu mengembalikan array asli. Newmenerima beberapa elemen dari beberapa tipe untuk menghasilkan array baru dari tipe tersebut. Semua metode lain harus menerima array asli sebagai referensi sehingga tidak perlu menetapkan hasilnya setelah itu terjadi secara internal.

Saya akan mendefinisikan Mergemetode untuk menggabungkan dua array; Namun, itu sudah dapat diselesaikan dengan Addmetode dengan mengirimkan array aktual versus beberapa elemen individual. Oleh karena itu, Adddapat digunakan dalam dua cara berikut untuk bergabung dengan dua set elemen:

Arr.Add<string>(ref myArray, "A", "B", "C");

Atau

Arr.Add<string>(ref myArray, anotherArray);
James M
sumber
-1

Saya tahu artikel ini berumur sepuluh tahun dan karena itu mungkin mati, tapi inilah yang saya coba lakukan:

Gunakan metode IEnumerable.Skip (), ditemukan di System.Linq . Ini akan melewati elemen yang dipilih dari array, dan mengembalikan salinan array yang hanya berisi semuanya kecuali objek yang dipilih. Kemudian ulangi saja untuk setiap elemen yang ingin Anda hapus dan setelah itu simpan ke variabel.

Misalnya, jika kita memiliki array bernama "Sampel" (dari tipe int []) dengan 5 angka. Kami ingin menghapus yang ke-2, jadi coba "Sample.Skip (2);" harus mengembalikan array yang sama kecuali tanpa angka ke-2.

commandertuna
sumber
Tidakkah metode ini hanya mem-bypass sejumlah elemen tertentu dalam suatu urutan dan kemudian mengembalikan elemen yang tersisa ? Dalam contoh Anda, Anda akan "melewati" dua elemen pertama dari daftar generik dan bukan hanya yang kedua!
xnr_z
-4

Langkah pertama
Anda perlu mengubah array menjadi daftar, Anda bisa menulis metode ekstensi seperti ini

// Convert An array of string  to a list of string
public static List<string> ConnvertArrayToList(this string [] array) {

    // DECLARE a list of string and add all element of the array into it

    List<string> myList = new List<string>();
    foreach( string s in array){
        myList.Add(s);
    }
    return myList;
} 

Langkah kedua
Tulis metode ekstensi untuk mengubah kembali daftar menjadi array

// convert a list of string to an array 
public static string[] ConvertListToArray(this List<string> list) {

    string[] array = new string[list.Capacity];
    array = list.Select(i => i.ToString()).ToArray();
    return array;
}

Langkah-langkah terakhir
Tulis metode terakhir Anda, tetapi ingatlah untuk menghapus elemen pada indeks sebelum mengonversi kembali ke array seperti kode yang ditampilkan

public static string[] removeAt(string[] array, int index) {

    List<string> myList = array.ConnvertArrayToList();
    myList.RemoveAt(index);
    return myList.ConvertListToArray();
} 

contoh kode dapat ditemukan di blog saya , terus melacak.

Bamara Coulibaly
sumber
13
Ini agak gila mengingat keberadaan .ToArray()dan List<T>konstruktor yang mengambil urutan yang ada ...
user7116