Xóa phần tử của một mảng thông thường


135

Tôi có một mảng các đối tượng Foo. Làm thế nào để tôi loại bỏ phần tử thứ hai của mảng?

Tôi cần một cái gì đó tương tự RemoveAt()nhưng cho một mảng thông thường.


1
Sử dụng System.Collections.ObjectModel.Collection<Foo>.
abatishchev

1
Đối với trò chơi của tôi, tôi đã sử dụng cơ sở hạ tầng "null at index". Về cơ bản, mảng bên trong (bộ đệm) có kích thước tĩnh, và thay vì loại bỏ chỉ mục và thay đổi kích thước mảng, tôi chỉ làm cho chỉ mục null. Khi tôi cần thêm một mục, tôi chỉ cần tìm chỉ mục không null đầu tiên và đặt nó ở đó. Hoạt động khá tốt, nhưng rõ ràng không phải cho tất cả mọi thứ.
Krythic

Câu trả lời:


202

Nếu bạn không muốn sử dụng Danh sách:

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

Bạn có thể thử phương pháp mở rộng này mà tôi chưa thực sự thử nghiệm:

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

Và sử dụng nó như:

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

8
Ví dụ đầu tiên được đưa ra trong câu trả lời này kém hiệu quả hơn nhiều so với câu hỏi thứ hai. Nó đòi hỏi hai bản sao mảng và một sự thay đổi của tất cả mọi thứ sau chỉ mục chứ không phải là một bản sao mảng chọn lọc.
Martin Brown

2
Tất nhiên +1, nhưng chúng tôi cũng có thể sử dụng danh sách HOẶC Danh sách <Foo> danh sách = Danh sách mới <Foll> (GetFoos ()); danh sách.Remove (my_foo); danh sách.RemoveAt (2); nơi GetFoos () sẽ trả về mảng Foos !!!!
shahjapan

2
Dòng đầu tiên bên trong phương thức sẽ nói 'nguồn. Độ dài' thay vì 'mảng. Độ dài'.
Nelson

1
Ngoài ra, hãy nhớ rằng bất kỳ biến nào lưu trữ tham chiếu đến mảng ban đầu sẽ tiếp tục chứa dữ liệu gốc và mọi so sánh đẳng thức tham chiếu giữa mảng trong nguồn và mảng đầu ra sẽ trả về âm.
bkqc

1
@MartinBrown Trên thực tế, việc chuyển đổi danh sách thành \ from và mảng chậm hơn nhiều so với bản sao mảng (có thể sao chép dữ liệu ở tốc độ tối đa được CPU cho phép chỉ bằng một vài lệnh ASM). Ngoài ra, việc chuyển danh sách rất nhanh vì đó chỉ là vấn đề hoán đổi một vài con trỏ và xóa dữ liệu nút (chỉ có 8 byte [cộng thêm 16 cho con trỏ head \ tail] trong trường hợp này).
krowe2

66

Bản chất của mảng là chiều dài của chúng là bất biến. Bạn không thể thêm hoặc xóa bất kỳ mục mảng nào.

Bạn sẽ phải tạo một mảng mới ngắn hơn một phần tử và sao chép các mục cũ sang mảng mới, ngoại trừ phần tử bạn muốn xóa.

Vì vậy, có lẽ tốt hơn là sử dụng Danh sách thay vì một mảng.


4
Chuyển đổi mảng thành một danh sáchList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Màu xanh bất tử

1
@ImmortalBlue hoặc chỉ var myList = myArray.ToList();sử dụng Enumerable.ToList()phương thức từ System.Linqkhông gian tên.
Dyndrilliac

58

Tôi sử dụng phương pháp này để loại bỏ một phần tử khỏi một mảng đối tượng. Trong tình huống của tôi, mảng của tôi có chiều dài nhỏ. Vì vậy, nếu bạn có mảng lớn, bạn có thể cần một giải pháp khác.

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
Cá nhân, tôi thích câu trả lời này tốt hơn câu trả lời được chấp nhận. Nó sẽ chỉ là hiệu quả, và nó dễ đọc hơn nhiều. Tôi có thể nhìn vào nó và biết nó là chính xác. Tôi sẽ phải kiểm tra cái khác để đảm bảo những bản sao đó được viết chính xác.
oillio

1
Thật là xấu hổ vì câu trả lời này quá thấp, khi nó tốt hơn nhiều so với hai câu hỏi trên.
Sepulchritude

Aaarhg, đó là câu trả lời tôi đang tìm kiếm! Đây là phương pháp tốt nhất mà không có Danh sách.
Jordi Huertas

47

Giải pháp một dòng LINQ:

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

Trong 1ví dụ đó là chỉ mục của phần tử cần loại bỏ - trong ví dụ này, theo câu hỏi ban đầu, phần tử thứ 2 (với 1phần tử thứ hai trong lập chỉ mục mảng dựa trên C # zero).

Một ví dụ đầy đủ hơn:

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

Sau khi chạy đoạn mã đó, giá trị của myArraysẽ là { "a", "c", "d", "e" }.


1
Đối với các khu vực yêu cầu hiệu suất cao / truy cập thường xuyên, LINQ không được khuyến nghị.
Krythic

3
@Krythic Đó là một nhận xét công bằng. Chạy hàng ngàn lần trong một vòng lặp chặt chẽ, hiệu suất của giải pháp này không tốt bằng một số giải pháp được bình chọn cao khác trên trang này: dotnetfiddle.net/z9Xkpn
Jon Schneider

9

Đây là một cách để xóa một phần tử mảng, kể từ .Net 3.5, mà không sao chép sang một mảng khác - sử dụng cùng một thể hiện mảng với 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
"mà không cần sao chép sang một mảng" - theo các tài liệu liên quan, Array.Resize thực hiện cấp phát một mảng mới đằng sau hậu trường, và bản sao các yếu tố từ mảng cũ sang cái mới. Tuy nhiên, tôi thích sự đơn giản của giải pháp này.
Jon Schneider

Rất đẹp và rõ ràng nếu bạn chắc chắn đó là một mảng tương đối nhỏ.
Darren

1
Tiếp tục bình luận của @ JonSchneider, đó không phải là "ví dụ mảng giống nhau". Đó là lý do tại sao bạn cần sử dụng refkhi bạn gọi Resizephương thức. Độ dài của một thể hiện mảng là cố định và bất biến.
Jeppe Stig Nielsen

2
Nếu thứ tự của các phần tử không quan trọng, thay vì di chuyển tất cả các phần tử xuống dưới, bạn có thể trao đổi phần tử tại chỉ mục với phần tử cuối cùng và sau đó thay đổi kích thước: Array [index] = Array [Array.Lạng - 1]; Array.Resize (ref mảng, Array.Lipse - 1);
Bartel

5

Đây là một phiên bản cũ mà tôi có, hoạt động trên phiên bản 1.0 của .NET framework và không cần các loại chung chung.

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

Điều này được sử dụng như thế này:

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

Không chính xác cách để giải quyết vấn đề này, nhưng nếu tình huống này không quan trọng và bạn coi trọng thời gian của mình, bạn có thể thử điều này cho các loại không thể.

Foos[index] = null

và sau đó kiểm tra các mục rỗng trong logic của bạn ..


Đây là cách tôi đã làm nó cho trò chơi của tôi. Đi với bộ đệm nullable cho các khu vực được thay đổi rất thường xuyên.
Krythic

2

Như thường lệ, tôi đến bữa tiệc muộn ...

Tôi muốn thêm một tùy chọn khác vào danh sách giải pháp tốt đẹp đã có. =)
Tôi sẽ xem đây là một cơ hội tốt cho Tiện ích mở rộng.

Tham khảo: http://msdn.microsoft.com/en-us/l Library / bb311042.aspx

Vì vậy, chúng tôi định nghĩa một số lớp tĩnh và trong đó, Phương thức của chúng tôi.
Sau đó, chúng ta có thể sử dụng phương pháp mở rộng của mình 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

Hãy thử mã dưới đây:

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

hoặc là

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

1

Đây là cách tôi đã là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

Trong một mảng bình thường, bạn phải xáo trộn tất cả các mục mảng trên 2 và sau đó thay đổi kích thước nó bằng phương pháp Thay đổi kích thước. Bạn có thể tốt hơn khi sử dụng 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

Đây là một bộ sưu tập nhỏ các phương thức trợ giúp tôi tạo ra dựa trên một số câu trả lời hiện có. Nó sử dụng cả phần mở rộng và phương thức tĩnh với các tham số tham chiếu để có tính lý tưởng tối đa:

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

Hiệu suất khôn ngoan, nó là tốt, nhưng nó có thể được cải thiện. Removedựa vào IndexOfvà một mảng mới được tạo cho mỗi phần tử bạn muốn loại bỏ bằng cách gọi RemoveAt.

IndexOflà phương thức mở rộng duy nhất vì nó không cần trả về mảng ban đầu. Newchấp nhận nhiều yếu tố của một số loại để tạo ra một mảng mới của loại đã nói. Tất cả các phương thức khác phải chấp nhận mảng ban đầu làm tham chiếu, do đó không cần gán kết quả sau đó vì điều đó đã xảy ra trong nội bộ.

Tôi đã định nghĩa một Mergephương thức để hợp nhất hai mảng; tuy nhiên, điều đó đã có thể được thực hiện bằng Addphương thức bằng cách chuyển vào một mảng thực tế so với nhiều phần tử riêng lẻ. Do đó, Addcó thể được sử dụng theo hai cách sau để nối hai bộ phần tử:

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

Hoặc là

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

-1

Tôi biết bài viết này đã mười tuổi và do đó có lẽ đã chết, nhưng đây là những gì tôi sẽ thử làm:

Sử dụng phương thức IEnumerable.Skip (), được tìm thấy trong System.Linq . Nó sẽ bỏ qua phần tử được chọn từ mảng và trả về một bản sao khác của mảng chỉ chứa mọi thứ trừ đối tượng được chọn. Sau đó, chỉ cần lặp lại rằng cho mọi phần tử bạn muốn loại bỏ và sau đó lưu nó vào một biến.

Ví dụ: nếu chúng ta có một mảng có tên "Mẫu" (thuộc kiểu int []) với 5 số. Chúng tôi muốn xóa cái thứ 2, vì vậy hãy thử "Sample.Skip (2);" sẽ trả về cùng một mảng trừ khi không có số thứ 2.


Không phải phương thức này chỉ bỏ qua một số phần tử được chỉ định trong một chuỗi và sau đó trả về các phần tử còn lại ? Trong ví dụ của bạn, bạn sẽ "bỏ qua" hai yếu tố đầu tiên của danh sách chung và không chỉ là yếu tố thứ hai!
xnr_z

-4

Bước đầu tiên
Bạn cần chuyển đổi mảng thành một danh sách, bạn có thể viết một phương thức mở rộng như thế này

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

Bước thứ hai
Viết phương thức mở rộng để chuyển đổi danh sách thành một mảng

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

Các bước cuối cùng
Viết phương thức cuối cùng của bạn, nhưng nhớ xóa phần tử tại chỉ mục trước khi chuyển đổi trở lại một mảng như hiển thị mã

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

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

mã ví dụ có thể được tìm thấy trên blog của tôi , tiếp tục theo dõi.


13
Điều này thật điên rồ khi xem xét sự tồn tại của .ToArray()và một nhà List<T>xây dựng có trình tự hiện có ...
user7116
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.