Remover elemento de uma matriz regular

135

Eu tenho uma variedade de objetos Foo. Como faço para remover o segundo elemento da matriz?

Eu preciso de algo semelhante a RemoveAt(), mas para uma matriz regular.

leora
fonte
1
Use System.Collections.ObjectModel.Collection<Foo>.
abatishchev
1
Para o meu jogo, fui com uma estrutura de dados "null at index". Basicamente, a matriz interna (buffer) é de tamanho estático e, em vez de remover o índice e redimensionar a matriz, apenas anulo o índice. Quando preciso adicionar um item, localizo o primeiro índice não nulo e o coloco lá. Funciona muito bem, mas obviamente não para tudo.
Krythic 17/09/16

Respostas:

202

Se você não quiser usar a Lista:

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

Você pode tentar este método de extensão que eu realmente não testei:

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

E use-o como:

Foo[] bar = GetFoos();
bar = bar.RemoveAt(2);
Andrew Kennan
fonte
8
O primeiro exemplo dado nesta resposta é muito menos eficiente que o segundo. Requer duas cópias de matriz e uma troca de tudo após o índice, em vez de uma cópia de matriz seletiva.
Martin Brown
2
+1, é claro, mas também podemos usar a lista OU OU List <Foo> list = new List <Foll> (GetFoos ()); list.Remove (my_foo); list.RemoveAt (2); onde GetFoos () retornará a matriz de Foos !!!!
21009 shahjapan
2
A primeira linha dentro do método deve dizer 'source.Length' em vez de 'array.Length'.
Nelson
1
Além disso, lembre-se de que qualquer variável que armazene uma referência à matriz original continuará a conter os dados originais e que qualquer comparação de igualdade de referência entre a matriz na origem e a matriz de saída retornará um valor negativo.
bkqc
1
@MartinBrown Na verdade, a conversão de uma lista para \ e array é muito mais lenta que uma cópia de array (que é capaz de copiar os dados na velocidade máxima permitida pela CPU com apenas algumas instruções ASM). Além disso, mudar uma lista é muito rápido, porque é apenas uma questão de trocar alguns ponteiros e remover os dados do nó (neste caso, são apenas 8 bytes [mais outros 16 para os ponteiros head \ tail]).
precisa saber é o seguinte
66

A natureza das matrizes é que seu comprimento é imutável. Você não pode adicionar ou excluir nenhum dos itens da matriz.

Você precisará criar uma nova matriz com um elemento mais curto e copiar os itens antigos para a nova matriz, excluindo o elemento que deseja excluir.

Portanto, provavelmente é melhor usar uma lista em vez de uma matriz.

Sebastian Dietz
fonte
4
Converta a matriz em uma listaList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Immortal Blue
1
@ImmortalBlue ou apenas var myList = myArray.ToList();usando o Enumerable.ToList()método no System.Linqespaço para nome.
Dyndrilliac
58

Eu uso esse método para remover um elemento de uma matriz de objetos. Na minha situação, minhas matrizes são pequenas. Portanto, se você tiver matrizes grandes, poderá precisar de outra solução.

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;
}
EdHellyer
fonte
7
Pessoalmente, gosto mais desta resposta do que da resposta aceita. Deve ser igualmente eficiente e muito mais fácil de ler. Eu posso olhar e saber que está correto. Eu teria que testar o outro para garantir que essas cópias foram escritas corretamente.
Oillio 8/03
1
É realmente uma pena que essa resposta seja tão baixa, quando é de longe melhor do que as duas acima.
Sepulchritude
Aaarhg, essa é a resposta que eu estava procurando! Este é o melhor método sem listas.
Jordi Huertas
47

Solução de linha única LINQ:

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

O 1exemplo nesse exemplo é o índice do elemento a ser removido - neste exemplo, pela pergunta original, o 2º elemento ( 1sendo o segundo elemento na indexação de matriz baseada em zero em C #).

Um exemplo mais completo:

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

Depois de executar esse snippet, o valor de myArrayserá { "a", "c", "d", "e" }.

Jon Schneider
fonte
1
Para áreas que exigem acesso frequente / de alto desempenho, o LINQ não é recomendado.
Krythic 17/09/16
3
@ Krythic Esse é um comentário justo. Executar milhares de vezes em um loop apertado, o desempenho deste solução não é tão tão bom quanto algumas das outras soluções altamente votada esta página: dotnetfiddle.net/z9Xkpn
Jon Schneider
9

Esta é uma maneira de excluir um elemento da matriz, a partir do .Net 3.5, sem copiar para outra matriz - usando a mesma instância da matriz com 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);
}
infografnet
fonte
2
"sem copiar para outro array" - acordo com a documentação vinculada, Array.Resize realmente faz alocar uma nova matriz nos bastidores, e copia os elementos da antiga matriz para o novo. Ainda assim, gosto da concisão dessa solução.
9119 Jon Schneider
Muito bonito e claro, se você tem certeza de que é uma matriz relativamente pequena.
Darren
1
Continuando o comentário de @ JonSchneider, não é "a mesma instância de matriz". É por isso que você precisa usar refquando chama o Resizemétodo. O comprimento de uma instância de matriz é fixo e imutável.
Jeppe Stig Nielsen
2
Se a ordem dos elementos não for importante, em vez de mover todos os elementos para baixo, você poderá trocar o elemento no índice pelo último elemento e redimensionar: arr [index] = arr [arr.Length - 1]; Array.Resize (ref arr, arr.Length - 1);
Bartel
5

Aqui está uma versão antiga que funciona na versão 1.0 do .NET framework e não precisa de tipos genéricos.

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

Isso é usado assim:

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]);
    }
}
Martin Brown
fonte
3

Não é exatamente o caminho a seguir, mas se a situação for trivial e você valorizar seu tempo, tente isso para tipos anuláveis.

Foos[index] = null

e depois verifique se há entradas nulas na sua lógica.

nawfal
fonte
Foi assim que fiz no meu jogo. Vá com buffers anuláveis ​​para áreas que são alteradas com muita frequência.
Krythic 17/09/19
2

Como sempre, estou atrasado para a festa ...

Eu gostaria de adicionar outra opção à boa lista de soluções já presente. =)
Eu consideraria isso uma boa oportunidade para extensões.

Referência: http://msdn.microsoft.com/en-us/library/bb311042.aspx

Então, definimos alguma classe estática e nela, nosso método.
Depois disso, podemos usar nosso método estendido, quer ou não. =)

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

Experimente o código abaixo:

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

ou

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

Aqui está como eu fiz isso ...

    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;
    }
Paul Mitchell
fonte
1

Em uma matriz normal, você deve embaralhar todas as entradas da matriz acima de 2 e redimensioná-la usando o método Resize. Você pode estar melhor usando um ArrayList.

gkrogers
fonte
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;
    }
user2884232
fonte
0

Aqui está uma pequena coleção de métodos auxiliares que produzi com base em algumas das respostas existentes. Ele utiliza extensões e métodos estáticos com parâmetros de referência para máxima idealidade:

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

Em termos de desempenho, é decente, mas provavelmente poderia ser melhorado. Removedepende IndexOfe uma nova matriz é criada para cada elemento que você deseja remover chamando RemoveAt.

IndexOfé o único método de extensão, pois não precisa retornar a matriz original. Newaceita vários elementos de algum tipo para produzir uma nova matriz do referido tipo. Todos os outros métodos devem aceitar a matriz original como referência, para que não seja necessário atribuir o resultado posteriormente, pois isso já ocorre internamente.

Eu teria definido um Mergemétodo para mesclar duas matrizes; no entanto, isso já pode ser realizado com o Addmétodo passando uma matriz real versus vários elementos individuais. Portanto, Addpode ser usado das duas maneiras a seguir para unir dois conjuntos de elementos:

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

Ou

Arr.Add<string>(ref myArray, anotherArray);
James M
fonte
-1

Eu sei que este artigo tem dez anos e, portanto, provavelmente está morto, mas eis o que eu tentaria fazer:

Use o método IEnumerable.Skip (), encontrado em System.Linq . Ele pulará o elemento selecionado da matriz e retornará outra cópia da matriz que contém apenas tudo, exceto o objeto selecionado. Em seguida, basta repetir isso para cada elemento que você deseja remover e depois salvá-lo em uma variável.

Por exemplo, se tivermos uma matriz denominada "Amostra" (do tipo int []) com 5 números. Queremos remover o segundo, tentando "Sample.Skip (2);" deve retornar a mesma matriz, exceto sem o segundo número.

commandertuna
fonte
Esse método não ignora apenas um número especificado de elementos em uma sequência e retorna os elementos restantes ? No seu exemplo, você "pulará" os dois primeiros elementos da lista genérica e não apenas o segundo!
Xnr_z 11/11/19
-4

Primeiro passo:
você precisa converter o array em uma lista, pode escrever um método de extensão como este

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

Segundo passo
Escreva um método de extensão para converter novamente a lista em uma matriz

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

Últimos passos
Escreva seu método final, mas lembre-se de remover o elemento no índice antes de converter novamente em uma matriz como o código show

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

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

exemplos de códigos podem ser encontrados no meu blog , acompanhe.

Bamara Coulibaly
fonte
13
Este é ligeiramente insano considerando a existência de .ToArray()e um List<T>construtor que leva uma seqüência existente ...
user7116