通常の配列の要素を削除する


135

Fooオブジェクトの配列があります。配列の2番目の要素を削除するにはどうすればよいですか?

RemoveAt()通常の配列に似たものが必要です。


1
を使用しSystem.Collections.ObjectModel.Collection<Foo>ます。
abatishchev 2009年

1
私のゲームでは、 "null at index"データ構造を使用しました。基本的に、内部の配列(バッファー)は静的サイズであり、インデックスを削除して配列のサイズを変更する代わりに、インデックスをnullにします。アイテムを追加する必要があるときは、最初のnull以外のインデックスを見つけてそこに配置します。かなりうまく機能しますが、明らかにすべてに適しているわけではありません。
Krythic 2016

回答:


202

リストを使用したくない場合:

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

私が実際にテストしていないこの拡張メソッドを試すことができます:

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

そしてそれを次のように使用します:

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

8
この回答の最初の例は、2番目の例よりも効率がはるかに低くなっています。1つの選択的な配列コピーではなく、2つの配列コピーとインデックス後のすべてのシフトが必要です。
マーティンブラウン

2
もちろん+1ですが、リストも使用できますOR List <Foo> list = new List <Foll>(GetFoos()); list.Remove(my_foo); list.RemoveAt(2); GetFoos()はFoosの配列を返します!!!!
shahjapan 2009

2
メソッド内の最初の行は、「array.Length」ではなく「source.Length」となるはずです。
ネルソン

1
また、元の配列への参照を格納する変数には引き続き元のデータが含まれ、ソース内の配列と出力配列間の参照の等価比較は負の値を返すことに注意してください。
bkqc

1
@MartinBrown実際には、リストを\ fromから配列に変換することは、配列のコピーよりもかなり低速です(ASM命令をいくつか使用するだけで、CPUが許可する最大速度でデータをコピーできます)。また、いくつかのポインタを交換してノードデータを削除するだけなので、リストのシフトは非常に高速です(この場合は8バイト(この場合、ヘッドポインタとテールポインタ用にさらに16バイト)だけです)。
krowe2

66

配列の性質は、その長さが不変であることです。配列項目を追加または削除することはできません。

要素を1つ短くした新しい配列を作成し、削除する要素を除いて、古い項目を新しい配列にコピーする必要があります。

したがって、配列の代わりにリストを使用する方が良いでしょう。


4
配列をリストに変換しますList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
イモータルブルー

1
@ImmortalBlueまたは名前空間var myList = myArray.ToList();Enumerable.ToList()メソッドを使用するSystem.Linq
Dyndrilliac

58

この方法を使用して、オブジェクト配列から要素を削除します。私の状況では、配列の長さが短いです。したがって、大きな配列がある場合は、別のソリューションが必要になることがあります。

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
個人的に、私はこの答えが受け入れられた答えよりも好きです。それも同じくらい効率的である必要があり、読みやすくなります。私はそれを見て、それが正しいことを知ることができます。他のコピーをテストして、それらのコピーが正しく書き込まれたことを確認する必要があります。
オイリオ2018年

1
それは上の2つよりもはるかに優れているときに、この回答が非常に低いのは本当に残念です。
Sepulchritude

Aaarhg、それが私が探していた答えです!これはリストなしの最良の方法です。
ジョルディフエルタス

47

LINQ単一行ソリューション:

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

1この例では、元の質問ごとに、第2の要素を(で-この例では、要素のインデックス削除する1C#ゼロベースの配列インデックスの2番目の要素です)。

より完全な例:

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

そのスニペットを実行すると、の値はにmyArrayなります{ "a", "c", "d", "e" }


1
高性能で頻繁なアクセスが必要な領域では、LINQは推奨されません。
Krythic 2016

3
@Krythicそれは公平なコメントです。緊密なループで何千回も実行します。このソリューションのパフォーマンスは、このページの他の投票数の多いソリューションほど良くありません。dotnetfiddle.net
Jon Schneider

9

これは、.Net 3.5以降、別の配列にコピーせずに配列要素を削除する方法です。同じ配列インスタンスを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
「別の配列にコピーせずに」-リンクされたドキュメントに従って、Array.Resizeは実際に新しい配列を背後で割り当て、古い配列から新しい配列に要素をコピーします。それでも、私はこのソリューションの簡潔さが好きです。
Jon Schneider

比較的小さい配列であることが確かな場合は、とてもわかりやすく明確です。
Darren

1
@JonSchneiderのコメントを続けると、「同じ配列インスタンス」ではありません。これがrefResizeメソッドを呼び出すときに使用する必要がある理由です。配列インスタンスの長さは固定されており、不変です。
Jeppe Stig Nielsen

2
要素の順序が重要でない場合は、すべての要素を下に移動する代わりに、indexの要素を最後の要素と入れ替えてサイズを変更できます。arr [index] = arr [arr.Length-1]; Array.Resize(ref arr、arr.Length-1);
Bartel、

5

これは、.NETフレームワークのバージョン1.0で動作し、ジェネリック型を必要としない古いバージョンです。

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

これは次のように使用されます:

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

正確にこれを行う方法ではありませんが、状況が些細で時間を重視する場合は、null許容型に対してこれを試すことができます。

Foos[index] = null

その後、ロジックのnullエントリを確認します。


これは私が自分のゲームでそれをした方法です。頻繁に変更される領域には、null許容バッファを使用してください。
Krythic 2016

2

いつものように、私はパーティーに遅れます...

すでに存在する素晴らしいソリューションリストに別のオプションを追加したいと思います。=)
私はこれを拡張機能にとって良い機会だと思います。

リファレンス:http : //msdn.microsoft.com/en-us/library/bb311042.aspx

そこで、静的クラスを定義し、その中にメソッドを定義します。
その後、拡張メソッドwilly-nillyを使用できます。=)

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

以下のコードを試してください:

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

または

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

1

これが私がやった方法です...

    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

通常の配列では、2より上のすべての配列エントリをシャッフルしてから、Resizeメソッドを使用してサイズを変更する必要があります。ArrayListを使用した方がよい場合があります。


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

これは、既存の回答のいくつかに基づいて私が作成したヘルパーメソッドの小さなコレクションです。拡張と静的メソッドの両方を使用して、参照パラメーターを最大の理想性にします。

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

パフォーマンスの点では、それはまともですが、おそらく改善することができます。Removeに依存しIndexOf、を呼び出すことで、削除する要素ごとに新しい配列が作成されますRemoveAt

IndexOf元の配列を返す必要がないため、唯一の拡張メソッドです。Newある型の複数の要素を受け入れて、その型の新しい配列を生成します。他のすべてのメソッドは元の配列を参照として受け入れる必要があるため、後で内部的に行われるため、後で結果を割り当てる必要はありません。

Merge2つの配列をマージする方法を定義しました。ただし、Add実際の配列と複数の個別の要素を渡すことで、メソッドですでにそれを実現できます。したがって、Add次の2つの方法で使用して、要素の2つのセットを結合できます。

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

または

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

-1

この記事は10年前のものなので、おそらく死んでいると思いますが、ここで私が試してみたいことを説明します。

System.LinqにあるIEnumerable.Skip()メソッドを使用します。選択した要素を配列からスキップし、選択したオブジェクト以外のすべてを含む配列の別のコピーを返します。次に、削除するすべての要素についてこれを繰り返し、その後、要素を変数に保存します。

たとえば、「Sample」という名前の(int []型の)5つの数値を持つ配列があるとします。2番目のものを削除したいので、「Sample.Skip(2);」を試します。2番目の数値を除いて同じ配列を返す必要があります。


このメソッドは、シーケンス内の指定された数の要素をバイパスし、残りの要素を返すだけではありませんか?あなたの例では、2番目のものだけでなく、総称リストの最初の2つの要素を「スキップ」します!
xnr_z

-4

最初のステップ
配列をリストに変換する必要があります。このような拡張メソッドを書くことができます

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

2番目のステップ
リストを配列に変換する拡張メソッドを記述します。

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

最後のステップ
最後のメソッドを記述しますが、コードショーのように配列に戻す前に、インデックスの要素を削除することを忘れないでください。

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

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

例のコードは私のブログで見つけることができ、追跡を続けます。


13
これは、存在.ToArray()List<T>既存のシーケンスをとるコンストラクタの存在を考えると、
少し気が狂ってい
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.