Видаліть елемент звичайного масиву


135

У мене є масив об'єктів Foo. Як видалити другий елемент масиву?

Мені потрібно щось подібне, RemoveAt()але для звичайного масиву.


1
Використовуйте System.Collections.ObjectModel.Collection<Foo>.
абатищев

1
Для своєї гри я перейшов із структурою даних "null at index". В основному внутрішній масив (буфер) має статичний розмір, і замість того, щоб видаляти індекс і змінювати розмір масиву, я просто роблю індекс нульовим. Коли мені потрібно додати елемент, я просто знаходжу перший ненульовий індекс і розміщую його там. Працює досить добре, але очевидно, не для всього.
Критичний

Відповіді:


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 звичайно, але ми також можемо використовувати список АБО список <Foo> список = новий список <Foll> (GetFoos ()); list.Remove (my_foo); list.RemoveAt (2); де GetFoos () поверне масив Foos !!!!
shahjapan

2
Перший рядок у методі повинен сказати "source.Length" замість "array.Length".
Нельсон

1
Також майте на увазі, що будь-яка змінна, що зберігає посилання на вихідний масив, буде продовжувати містити вихідні дані і що будь-яке порівняння порівняння рівності між масивом у вихідному масиві та вихідному масиві поверне мінус.
bkqc

1
@MartinBrown Насправді перетворення списку в \ з та масиву відбувається набагато повільніше, ніж копія масиву (яка здатна копіювати дані на максимальній швидкості, дозволеній процесором, лише за допомогою декількох інструкцій ASM). Крім того, переміщення списку відбувається дуже швидко, оскільки справа лише в заміні кількох покажчиків та видаленні даних вузла (що в цьому випадку становить лише 8 байт [плюс ще 16 для покажчиків голова \ хвіст]).
krowe2

66

Характер масивів полягає в тому, що їх довжина незмінна. Ви не можете додати або видалити жоден елемент масиву.

Вам доведеться створити новий масив, який на один елемент коротший, і скопіювати старі елементи в новий масив, виключаючи елемент, який потрібно видалити.

Тому, ймовірно, краще використовувати Список замість масиву.


4
Перетворити масив у списокList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Безсмертний синій

1
@ImmortalBlue або просто var myList = myArray.ToList();використовуючи Enumerable.ToList()метод з System.Linqпростору імен.
Діндріліак

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, це відповідь, яку я шукав! Це найкращий метод без списків.
Джорді

47

Однорядне рішення LINQ:

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

У 1цьому прикладі є індекс елемента, який потрібно видалити - у цьому прикладі за оригінальним запитанням, другий елемент (з 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 не рекомендується.
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 фактично робить виділити новий масив за лаштунками, і копіює елементи зі старого масиву в новий. Все-таки мені подобається стислість цього рішення.
Джон Шнайдер

Дуже приємно і зрозуміло, якщо ви впевнені, що це порівняно невеликий масив.
Даррен

1
Продовжуючи коментар @ JonSchneider, це не "той самий екземпляр масиву". Саме тому вам потрібно користуватися refпри виклику Resizeметоду. Довжина екземпляра масиву є фіксованою та незмінною.
Джеппе Стіг Нільсен

2
Якщо порядок елементів не важливий, замість переміщення всіх елементів вниз, ви можете поміняти елемент в індексі з останнім елементом, а потім змінити розмір: arr [index] = arr [arr.Length - 1]; Array.Resize (ref arr, arr.Length - 1);
Bartel

5

Ось у мене стара версія, яка працює на версії 1.0 рамки .NET і не потребує загальних типів.

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

Не зовсім так, як це зробити, але якщо ситуація тривіальна і ви цінуєте свій час, ви можете спробувати це для змінних типів.

Foos[index] = null

а пізніше перевірити наявність логічних записів у вашій логіці ..


Ось як я це зробив для своєї гри. Перейдіть з нульовими буферами для областей, які дуже часто змінюються.
Критичний

2

Як завжди, я спізнююсь на вечірку ...

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

Довідка: 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"
             */
        }
    }
}

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приймає кілька елементів деякого типу для створення нового масиву згаданого типу. Усі інші методи повинні сприймати оригінальний масив як опорний, тому немає необхідності присвоювати результат згодом, як це вже відбувається внутрішньо.

Я б визначив Mergeметод об'єднання двох масивів; однак це вже можна досягти Addметодом, передаючи фактичний масив проти кількох окремих елементів. Тому Addможуть використовуватися наступні два способи з'єднання двох наборів елементів:

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

Або

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

-1

Я знаю, що цій статті десять років і тому, ймовірно, мертвий, але ось що я б спробував зробити:

Використовуйте метод IEnumerable.Skip (), знайдений у System.Linq . Він буде пропускати вибраний елемент з масиву та повертає іншу копію масиву, яка містить лише все, крім вибраного об'єкта. Потім просто повторіть, що для кожного елемента, який ви хочете видалити, а потім збережіть його до змінної.

Наприклад, якщо у нас є масив під назвою "Sample" (типу int []) з 5 числами. Ми хочемо видалити 2-ю, тому спробуємо "Sample.Skip (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;
} 

Другий крок
Напишіть метод розширення для перетворення списку в масив

// 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.