Удалить элемент обычного массива

#c# #list #arraylist

#c# #.net #массивы

Вопрос:

У меня есть массив объектов Foo. Как мне удалить второй элемент массива?

Мне нужно что-то похожее на RemoveAt() , но для обычного массива.

Комментарии:

1. Используйте System.Collections.ObjectModel.Collection<Foo> .

2. Для моей игры я использовал структуру данных с нулевым индексом. По сути, внутренний массив (буфер) имеет статический размер, и вместо удаления индекса и изменения размера массива я просто делаю индекс нулевым. Когда мне нужно добавить элемент, я просто нахожу первый ненулевой индекс и помещаю его туда. Работает довольно хорошо, но, очевидно, не для всего.

Ответ №1:

Если вы не хотите использовать List:

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

Комментарии:

1. Первый пример, приведенный в этом ответе, намного менее эффективен, чем второй. Для этого требуется две копии массива и сдвиг всего после индекса, а не одна выборочная копия массива.

2. 1, конечно, но мы также можем использовать list ИЛИ List<Foo> list = new List<Foll>(GetFoos()); list.Remove(my_foo); list.RemoveAt(2); где GetFoos() вернет массив Foo !!!!

3. В первой строке внутри метода должно быть написано ‘source. Длина’ вместо ‘массива. Длина’.

4. Также имейте в виду, что любая переменная, хранящая ссылку на исходный массив, будет продолжать содержать исходные данные и что любое сравнение равенства ссылок между массивом в источнике и выходным массивом вернет отрицательное значение.

5. @MartinBrown На самом деле преобразование списка в из и массива происходит намного медленнее, чем копирование массива (который способен копировать данные с максимальной скоростью, разрешенной процессором, всего несколькими инструкциями ASM). Кроме того, перемещение списка происходит очень быстро, потому что это всего лишь вопрос замены нескольких указателей и удаления данных узла (что составляет всего 8 байт [плюс еще 16 для указателей head tail] в данном случае).

Ответ №2:

Природа массивов такова, что их длина неизменна. Вы не можете добавлять или удалять какие-либо элементы массива.

Вам нужно будет создать новый массив, который на один элемент короче, и скопировать старые элементы в новый массив, исключая элемент, который вы хотите удалить.

Поэтому, вероятно, лучше использовать список вместо массива.

Комментарии:

1. Преобразовать массив в список List<mydatatype> array = new List<mydatatype>(arrayofmydatatype)

2. @ImmortalBlue или просто var myList = myArray.ToList(); используя Enumerable.ToList() метод из System.Linq пространства имен.

Ответ №3:

Я использую этот метод для удаления элемента из массива объектов. В моей ситуации мои массивы невелики по длине. Итак, если у вас большие массивы, вам может понадобиться другое решение.

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

Комментарии:

1. Лично мне этот ответ нравится больше, чем принятый ответ. Это должно быть так же эффективно, и его намного легче читать. Я могу посмотреть на это и знать, что это правильно. Мне пришлось бы протестировать другой, чтобы убедиться, что эти копии были написаны правильно.

2. Действительно жаль, что этот ответ настолько низок, когда он намного лучше, чем два выше него.

3. Ааааа, это тот ответ, который я искал! Это лучший метод без списков.

Ответ №4:

Однострочное решение LINQ:

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

1 В этом примере это индекс элемента, который нужно удалить — в этом примере, согласно исходному вопросу, 2-й элемент (при этом 1 являющийся вторым элементом в C # индексации массива на основе нуля).

Более полный пример:

 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 не рекомендуется.

2. @Krythic Это справедливый комментарий. После тысячного запуска в замкнутом цикле производительность этого решения не так хороша, как у некоторых других решений, получивших высокие оценки на этой странице: dotnetfiddle.net/z9Xkpn

Ответ №5:

Это способ удалить элемент массива, начиная с .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);
}
  

Комментарии:

1. «без копирования в другой массив» — согласно связанной документации, Array.Resize фактически выделяет новый массив за кулисами и копирует элементы из старого массива в новый. Тем не менее, мне нравится лаконичность этого решения.

2. Очень красиво и понятно, если вы уверены, что это относительно небольшой массив.

3. Продолжая комментарий @JonSchneider, это не «тот же экземпляр массива». Вот почему вам нужно использовать ref при вызове Resize метода. Длина экземпляра массива является фиксированной и неизменяемой.

4. Если порядок элементов не важен, вместо перемещения всех элементов вниз, вы могли бы поменять элемент по индексу на последний элемент, а затем изменить размер: arr[index] = arr[arr.Length — 1]; Array.Resize(ссылка arr, arr.Length — 1);

Ответ №6:

Вот старая версия, которая у меня есть, которая работает с версией 1.0 .NET framework и не нуждается в универсальных типах.

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

Ответ №7:

Это не совсем то, как это делается, но если ситуация тривиальна и вы цените свое время, вы можете попробовать это для типов с нулевым значением.

 Foos[index] = null
  

и позже проверьте наличие нулевых записей в вашей логике..

Комментарии:

1. Вот как я это сделал для своей игры. Используйте буферы с возможностью обнуления для областей, которые меняются очень часто.

Ответ №8:

Попробуйте приведенный ниже код:

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

или

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

Ответ №9:

Как обычно, я опаздываю на вечеринку…

Я хотел бы добавить еще один вариант в уже существующий список хороших решений. =)
Я бы рассматривал это как хорошую возможность для расширений.

Ссылка:http://msdn.microsoft.com/en-us/library/bb311042.aspx

Итак, мы определяем некоторый статический класс и в нем наш метод.
После этого мы можем волей-неволей использовать наш расширенный метод. =)

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

Ответ №10:

Вот как я это сделал…

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

Ответ №11:

В обычном массиве вам нужно перетасовать все записи массива выше 2, а затем изменить его размер с помощью метода Resize. Возможно, вам было бы лучше использовать ArrayList.

Ответ №12:

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

Комментарии:

1. обратите внимание, что первоначальный вопрос касается удаления одного элемента по индексу , в то время как это решение удаляет элементы по значению

Ответ №13:

Вот небольшая коллекция вспомогательных методов, которые я создал на основе некоторых существующих ответов. Он использует как расширения, так и статические методы со ссылочными параметрами для максимальной идеальности:

 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 = Resu<
    }
}
  

С точки зрения производительности он приличный, но, вероятно, его можно было бы улучшить. Remove полагается на IndexOf , и для каждого элемента, который вы хотите удалить, вызывая RemoveAt , создается новый массив.

IndexOf это единственный метод расширения, поскольку ему не нужно возвращать исходный массив. New принимает несколько элементов некоторого типа для создания нового массива указанного типа. Все другие методы должны принимать исходный массив в качестве ссылки, поэтому нет необходимости присваивать результат позже, поскольку это уже происходит внутри.

Я бы определил Merge метод для объединения двух массивов; однако это уже может быть выполнено с помощью Add метода путем передачи фактического массива в сравнении с несколькими отдельными элементами. Следовательно, Add может использоваться следующими двумя способами для соединения двух наборов элементов:

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

Или

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

Ответ №14:

Я знаю, что этой статье десять лет, и поэтому, вероятно, она мертва, но вот что я бы попытался сделать:

Используйте метод IEnumerable.Skip(), найденный в System.Linq. Он пропустит выбранный элемент из массива и вернет другую копию массива, которая содержит только все, кроме выбранного объекта. Затем просто повторите это для каждого элемента, который вы хотите удалить, и после этого сохраните его в переменной.

Например, если у нас есть массив с именем «Sample» (типа int[]) с 5 числами. Мы хотим удалить 2-й, поэтому пытаемся «Пример.Skip(2);» должен возвращать тот же массив, но без 2-го номера.

Комментарии:

1. Разве этот метод не просто обходит указанное количество элементов в последовательности, а затем возвращает оставшиеся элементы ? В вашем примере вы «пропустите» первые два элемента общего списка, а не только 2-й!

Ответ №15:

Первый шаг
Вам нужно преобразовать массив в список, вы могли бы написать метод расширения, подобный этому

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

примеры кодов можно найти в моем блоге, продолжайте отслеживать.

Комментарии:

1. Это слегка безумно, учитывая существование .ToArray() и List<T> конструктора, который принимает существующую последовательность…