일반 배열의 요소 제거


135

Foo 객체 배열이 있습니다. 배열의 두 번째 요소를 어떻게 제거합니까?

RemoveAt()일반 배열 과 비슷한 것이 필요합니다 .


1
사용하십시오 System.Collections.ObjectModel.Collection<Foo>.
abatishchev 12

1
내 게임에서는 "null at index"데이터 구조를 사용했습니다. 기본적으로 내부 배열 (버퍼)은 정적 크기이며 인덱스를 제거하고 배열 크기를 조정하는 대신 인덱스를 null로 만듭니다. 항목을 추가해야 할 때 null이 아닌 첫 번째 색인을 찾아서 배치하십시오. 꽤 잘 작동하지만 모든 것이 아닙니다.
Krythic

답변:


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
이 답변에 주어진 첫 번째 예는 두 번째 예보다 훨씬 덜 효율적입니다. 하나의 선택적 배열 복사본이 아니라 두 개의 배열 복사본과 인덱스 이후의 모든 항목 이동이 필요합니다.
Martin Brown

2
물론 +1이지만 list도 사용할 수 있습니다. OR List <Foo> list = new List <Foll> (GetFoos ()); list.Remove (my_foo); list.RemoveAt (2); 여기서 GetFoos ()는 Foos 배열을 반환합니다 !!!!
shahjapan

2
메서드 내부의 첫 번째 줄에는 'array.Length'대신 'source.Length'가 표시되어야합니다.
Nelson

1
또한 원래 배열에 대한 참조를 저장하는 모든 변수에는 원본 데이터가 계속 포함되며 소스의 배열과 출력 배열 사이의 참조 동등 비교는 음수를 반환합니다.
bkqc

1
@MartinBrown 실제로, 목록을 \ from 및 array로 변환하는 것은 어레이 사본보다 훨씬 느립니다 (이는 ASM 명령어 몇 개만으로 CPU에서 허용하는 최대 속도로 데이터를 복사 할 수 있음). 또한,리스트를 옮기는 것은 단지 몇 개의 포인터를 교환하고 노드 데이터 (이 경우 머리 / 꼬리 포인터의 경우 16 바이트)와 8 바이트 만 제거하기 때문에 매우 빠릅니다.
krowe2

66

배열의 특성상 길이는 변경할 수 없습니다. 배열 항목을 추가하거나 삭제할 수 없습니다.

한 요소보다 짧은 새 배열을 작성하고 삭제하려는 요소를 제외하고 이전 항목을 새 배열로 복사해야합니다.

따라서 배열 대신 List를 사용하는 것이 좋습니다.


4
배열을 목록으로 변환List<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Immortal Blue

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
개인적으로, 나는이 대답이 받아 들여진 대답보다 낫습니다. 그것은 효율적이고 읽기 쉬워야합니다. 나는 그것을보고 그것이 올바른지 알 수 있습니다. 다른 사본을 테스트하여 사본이 올바르게 작성되었는지 확인해야합니다.
oillio

1
위의 두 가지보다 훨씬 좋을 때이 답변이 너무 낮아서 부끄러운 일입니다.
Sepulchritude

Aaarhg, 그것이 내가 찾던 해답입니다! 이것은 목록이없는 가장 좋은 방법입니다.
Jordi Huertas 1

47

LINQ 단선 솔루션 :

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

1이 예에서, 원래의 질문에 따라, 제 2 요소 (와 -이 예에서 요소의 인덱스를 제거하는 1C # 제로 어레이 인덱스에서 두 번째 요소이다).

보다 완전한 예 :

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

3
@Krythic 이건 공정한 의견입니다. : 꽉 루프에서 수천 번을 실행,이 솔루션의 성능은 다른 어떤이 페이지에서 솔루션을 높은 투표로 좋은뿐만 아닙니다 dotnetfiddle.net/z9Xkpn
존 슈나이더

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 실제로 수행 장면 뒤에 새로운 배열을 할당하고 복사 새에 이전 배열의 요소. 여전히이 솔루션의 간결함이 마음에 듭니다.
존 슈나이더

비교적 작은 배열인지 확실하고 명확합니다.
Darren

1
@JonSchneider의 의견을 계속하면 "같은 배열 인스턴스"가 아닙니다. 그렇기 때문에 메소드 ref를 호출 할 때 사용해야 합니다 Resize. 배열 인스턴스의 길이는 고정되어 있으며 변경할 수 없습니다.
Jeppe Stig Nielsen

2
요소의 순서가 중요하지 않은 경우 모든 요소를 ​​아래쪽으로 이동하는 대신 요소를 색인의 마지막 요소와 바꾸고 크기를 조정할 수 있습니다. arr [index] = arr [arr.Length-1]; Array.Resize (참조 arr, arr. 길이-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

이 문제를 해결하는 방법은 아니지만 상황이 사소하고 시간을 소중하게 생각하면 nullable 유형에 대해 시도해 볼 수 있습니다.

Foos[index] = null

나중에 논리에서 null 항목을 확인하십시오.


이것이 내가 게임을 위해 한 방법입니다. 자주 변경되는 영역에 대해서는 널 입력 가능 버퍼를 사용하십시오.
Krythic

2

평소처럼, 나는 파티에 늦었다 ...

이미 존재하는 멋진 솔루션 목록에 다른 옵션을 추가하고 싶습니다. =)
나는 이것이 Extensions의 좋은 기회라고 생각합니다.

참조 : 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 이상의 모든 배열 항목을 섞은 다음 크기 조정 방법을 사용하여 크기를 조정해야합니다. 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특정 유형의 여러 요소를 허용하여 해당 유형의 새 배열을 생성합니다. 다른 모든 메소드는 원래 배열을 참조로 허용해야하므로 결과는 이미 내부적으로 발생하므로 나중에 결과를 할당 할 필요가 없습니다.

Merge두 배열을 병합 하는 방법을 정의했습니다 . 그러나 Add실제 배열과 여러 개별 요소를 전달 하여 메소드로 이미 수행 할 수 있습니다 . 따라서 Add다음 두 가지 방법으로 두 요소 세트를 결합 할 수 있습니다.

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

또는

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

-1

나는이 기사가 열 살이되었을 것이므로 아마 죽었을 것입니다. 그러나 여기에 내가 시도한 것이 있습니다 :

System.Linq에있는 IEnumerable.Skip () 메서드를 사용하십시오 . 배열에서 선택한 요소를 건너 뛰고 선택한 개체를 제외한 모든 항목 만 포함하는 다른 배열 복사본을 반환합니다. 그런 다음 제거하려는 모든 요소에 대해 반복하고 변수에 저장하십시오.

예를 들어, 5 개의 숫자를 가진 "Sample"(int [] 유형)이라는 배열이있는 경우. 우리는 두 번째 것을 제거하고 싶기 때문에 "Sample.Skip (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;
} 

두 번째 단계
목록을 배열로 다시 변환하는 확장 메소드 작성

// 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>... 기존의 서열을 얻어 생성자
user7116
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.