Normal bir dizinin elemanını kaldır


135

Bir dizi Foo nesnem var. Dizinin ikinci elemanını nasıl kaldırırım?

Benzer bir şeye ihtiyacım var RemoveAt()ama normal bir dizi için.


1
Kullanın System.Collections.ObjectModel.Collection<Foo>.
abatishchev

1
Oyunum için "indekste boş" veri yapısı kullandım. Temel olarak, iç dizi (arabellek) statik boyuttadır ve dizini kaldırmak ve diziyi yeniden boyutlandırmak yerine, dizini boş yapıyorum. Bir öğe eklemem gerektiğinde, boş olmayan ilk dizini bulup oraya yerleştiriyorum. Oldukça iyi çalışıyor, ama belli ki her şey için değil.
Krythic

Yanıtlar:


202

Listeyi kullanmak istemiyorsanız:

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

Aslında test etmediğim bu uzantı yöntemini deneyebilirsiniz:

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

Ve şu şekilde kullanın:

Foo[] bar = GetFoos();
bar = bar.RemoveAt(2);

8
Bu cevapta verilen ilk örnek, ikinciden çok daha az etkilidir. Bir seçici dizi kopyası yerine iki dizi kopyası ve indeksten sonra her şeyin kaydırılmasını gerektirir.
Martin Brown

2
Elbette +1, ancak listeyi de kullanabiliriz VEYA List <Foo> list = new List <Foll> (GetFoos ()); list.Remove (my_foo); list.RemoveAt (2); GetFoos (), Foos dizisini döndürecektir !!!!
shahjapan

2
Metodun içindeki ilk satırda 'array.Length' yerine 'source.Length' demelidir.
Nelson

1
Ayrıca, orijinal diziye bir başvuru depolayan herhangi bir değişkenin orijinal verileri içermeye devam edeceğini ve kaynaktaki dizi ile çıktı dizisi arasındaki herhangi bir başvuru eşitliği karşılaştırmasının bir negatif döndüreceğini unutmayın.
bkqc

1
@MartinBrown Aslında, bir listeyi \ from ve diziye dönüştürmek, bir dizi kopyasından çok daha yavaştır (bu, verileri yalnızca birkaç ASM talimatıyla CPU'nun izin verdiği maksimum hızda kopyalayabilir). Ayrıca, bir listeyi değiştirmek çok hızlıdır çünkü bu sadece birkaç işaretleyiciyi değiştirip düğüm verilerini kaldırmakla ilgilidir (bu durumda bu sadece 8 bayttır [artı head \ tail işaretçileri için 16 tane daha]).
krowe2

67

Dizilerin doğası, uzunluklarının değişmez olmasıdır. Dizi öğelerinin hiçbirini ekleyemez veya silemezsiniz.

Bir öğe daha kısa olan yeni bir dizi oluşturmanız ve silmek istediğiniz öğeyi hariç tutarak eski öğeleri yeni diziye kopyalamanız gerekecektir.

Bu yüzden bir dizi yerine Liste kullanmak muhtemelen daha iyidir.


4
Diziyi listeye dönüştürList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Immortal Blue

1
@ImmortalBlue veya sadece ad alanındaki yöntemi var myList = myArray.ToList();kullanarak . Enumerable.ToList()System.Linq
Dyndrilliac

58

Nesne dizisinden bir öğeyi kaldırmak için bu yöntemi kullanıyorum. Benim durumumda dizilerimin uzunluğu küçük. Yani büyük dizileriniz varsa başka bir çözüme ihtiyacınız olabilir.

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

7
Şahsen, bu cevabı kabul edilen cevaptan daha çok seviyorum. Aynı derecede verimli olmalı ve okunması çok daha kolay. Ona bakabilirim ve doğru olduğunu bilirim. Bu kopyaların doğru yazıldığından emin olmak için diğerini test etmem gerekecekti.
oillio

1
Yukarıdaki ikisinden çok daha iyi olduğu zaman, bu cevabın bu kadar düşük olması gerçekten utanç verici.
Sepulchritude

Aaarhg, aradığım cevap buydu! Listeler olmadan en iyi yöntem budur.
Jordi Huertas

47

LINQ tek satırlık çözüm:

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

Bu 1örnekteki, kaldırılacak öğenin dizinidir - bu örnekte, orijinal soruya göre, 2. öğe ( 1C # sıfır tabanlı dizi indekslemede ikinci öğedir).

Daha eksiksiz bir örnek:

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

Bu parçacığı çalıştırdıktan sonra değeri myArrayolacaktır { "a", "c", "d", "e" }.


1
Yüksek performans / sık erişim gerektiren alanlar için LINQ önerilmez.
Krythic

3
@Krythic Bu adil bir yorum. Sıkı bir döngüde binlerce kez çalıştırın, bu çözümün performansı bu sayfadaki diğer yüksek oy alan çözümlerden bazıları kadar iyi değil: dotnetfiddle.net/z9Xkpn
Jon Schneider

9

Bu, .Net 3.5'ten itibaren bir dizi öğesini başka bir diziye kopyalamadan - aynı dizi örneğini kullanarak silmenin bir yoludur 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);
}

2
"Başka diziye kopyalamadan" - bağlantılı dokümantasyon başına, Array.Resize aslında yapar perde arkasında yeni bir dizi tahsis ve kopyalar yenisine eski diziden unsurlar. Yine de, bu çözümün özünü seviyorum.
Jon Schneider

Nispeten küçük bir dizi olduğundan eminseniz çok güzel ve net.
Darren

1
@ JonSchneider'ın yorumuna devam edersek, bu "aynı dizi örneği" değildir. Yöntemi refçağırdığınızda bu yüzden kullanmanız gerekir Resize. Bir dizi örneğinin uzunluğu sabittir ve değişmez.
Jeppe Stig Nielsen

2
Elemanların sırası önemli değilse, tüm elemanları aşağı doğru hareket ettirmek yerine, dizindeki elemanı son elemanla değiştirebilir ve sonra yeniden boyutlandırabilirsiniz: arr [index] = arr [arr.Length - 1]; Array.Resize (ref dizi, dizi uzunluğu - 1);
Bartel

5

İşte .NET çerçevesinin 1.0 sürümünde çalışan ve genel türlere ihtiyaç duymayan eski bir sürümüm var.

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

Bu şu şekilde kullanılır:

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

3

Tam olarak bunu yapmanın yolu değil, ancak durum önemsizse ve zamanınıza değer veriyorsanız, bunu null yapılabilir türler için deneyebilirsiniz.

Foos[index] = null

ve daha sonra mantığınızda boş girdileri kontrol edin ..


Oyunum için bunu böyle yaptım. Çok sık değişen alanlar için boş değer atanabilir arabellekleri kullanın.
Krythic

2

Her zamanki gibi partiye geç kaldım ...

Zaten mevcut olan güzel çözümler listesine başka bir seçenek eklemek istiyorum. =)
Bunu Uzantılar için iyi bir fırsat olarak görürdüm.

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

Bu nedenle, bazı statik sınıfları ve içinde Metodumuzu tanımlıyoruz.
Bundan sonra, genişletilmiş yöntemimizi isteyerek kullanabiliriz. =)

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"
             */
        }
    }
}

2

Aşağıdaki kodu deneyin:

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

veya

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

1

İşte böyle yaptım ...

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

1

Normal bir dizide, 2'nin üzerindeki tüm dizi girdilerini karıştırmanız ve ardından Yeniden Boyutlandırma yöntemini kullanarak yeniden boyutlandırmanız gerekir. Bir ArrayList kullanmanız daha iyi olabilir.


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

0

İşte mevcut yanıtlardan bazılarına dayanarak ürettiğim küçük bir yardımcı yöntem koleksiyonu. Maksimum ideallik için referans parametreli hem uzantıları hem de statik yöntemleri kullanır:

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

Performans açısından iyi, ancak muhtemelen geliştirilebilir. Removedayanır IndexOfve çağırarak kaldırmak istediğiniz her öğe için yeni bir dizi oluşturulur RemoveAt.

IndexOforijinal diziyi döndürmesi gerekmediği için tek uzantı yöntemidir. Newsöz konusu tipte yeni bir dizi üretmek için bir türden birden fazla elemanı kabul eder. Diğer tüm yöntemler, orijinal diziyi bir referans olarak kabul etmelidir, böylece daha sonra dahili olarak gerçekleştiği için sonucu daha sonra atamaya gerek yoktur.

Mergeİki diziyi birleştirmek için bir yöntem tanımlardım ; ancak, bu, Addgerçek bir diziye karşı birden çok tekil öğeye geçerek yöntemle zaten başarılabilir . Bu nedenle, Addiki öğe kümesini birleştirmek için aşağıdaki iki şekilde kullanılabilir:

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

Veya

Arr.Add<string>(ref myArray, anotherArray);

-1

Bu makalenin on yaşında olduğunu ve bu nedenle muhtemelen öldüğünü biliyorum, ama işte yapmaya çalıştığım şey:

System.Linq'de bulunan IEnumerable.Skip () yöntemini kullanın . Diziden seçilen öğeyi atlar ve dizinin yalnızca seçilen nesne dışındaki her şeyi içeren başka bir kopyasını döndürür. Ardından, kaldırılmasını istediğiniz her öğe için bunu tekrarlayın ve ardından bir değişkene kaydedin.

Örneğin, "Sample" adında (int [] türünde) 5 sayı içeren bir dizimiz varsa. İkincisini kaldırmak istiyoruz, bu yüzden "Sample.Skip (2);" 2. sayı olmadan aynı diziyi döndürmelidir.


Bu yöntem , bir dizideki belirli sayıda öğeyi atlayıp sonra kalan öğeleri döndürmez mi? Örneğinizde, yalnızca 2. olanı değil, genel listenin ilk iki öğesini "atlayacaksınız"!
xnr_z

-4

İlk adım
Diziyi bir listeye dönüştürmeniz gerekiyor, bunun gibi bir genişletme yöntemi yazabilirsiniz.

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

İkinci adım
Listeyi bir diziye dönüştürmek için bir genişletme yöntemi yazın

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

Son adımlar
Son yönteminizi yazın, ancak kod gösterisi gibi bir diziye geri dönüştürmeden önce dizindeki öğeyi kaldırmayı unutmayın.

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

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

örnekler kodları üzerinde bulabilmesi olabilir bloguma , izleme tutun.


13
Bu varlığını dikkate hafif delilik .ToArray()ve List<T>... Varolan diziyi alan kurucuya
user7116
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.