W mojej grze korzystałem z danych strukturalnych „zero na indeksie”. Zasadniczo wewnętrzna tablica (bufor) ma rozmiar statyczny i zamiast usuwać indeks i zmieniać rozmiar tablicy, po prostu ustawiam indeks na null. Kiedy muszę dodać element, po prostu znajduję pierwszy niezerowy indeks i umieszczam go tam. Działa całkiem dobrze, ale oczywiście nie do wszystkiego.
Krythic
Odpowiedzi:
204
Jeśli nie chcesz korzystać z listy:
var foos = new List<Foo>(array);
foos.RemoveAt(index);
return foos.ToArray();
Możesz wypróbować tę metodę rozszerzenia, której tak naprawdę nie testowałem:
publicstatic 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;
}
Pierwszy przykład podany w tej odpowiedzi jest znacznie mniej skuteczny niż drugi. Wymaga dwóch kopii tablicy i przesunięcia wszystkiego po indeksie, a nie jednej selektywnej kopii tablicy.
Martin Brown,
2
+1 oczywiście, ale możemy też użyć list LUB List <Foo> list = new List <Foll> (GetFoos ()); list.Remove (my_foo); list.RemoveAt (2); gdzie GetFoos () zwróci tablicę Foos !!!!
shahjapan
2
Pierwsza linia w metodzie powinna zawierać tekst „source.Length” zamiast „array.Length”.
Nelson
1
Należy również pamiętać, że każda zmienna przechowująca odniesienie do oryginalnej tablicy będzie nadal zawierać oryginalne dane i że każde porównanie równości odniesienia między tablicą w źródle a tablicą wyjściową zwróci wartość ujemną.
bkqc
1
@MartinBrown W rzeczywistości konwersja listy na \ from i array jest znacznie wolniejsza niż kopia tablicy (która jest w stanie skopiować dane z maksymalną prędkością dozwoloną przez procesor za pomocą zaledwie kilku instrukcji ASM). Ponadto przesuwanie listy jest bardzo szybkie, ponieważ jest to tylko kwestia zamiany kilku wskaźników i usunięcia danych węzła (w tym przypadku jest to tylko 8 bajtów [plus kolejne 16 dla wskaźników nagłówka / końca]).
krowe2
68
Naturą tablic jest to, że ich długość jest niezmienna. Nie możesz dodawać ani usuwać żadnych elementów tablicy.
Będziesz musiał utworzyć nową tablicę, która jest o jeden element krótsza, i skopiować stare elementy do nowej tablicy, z wyłączeniem elementu, który chcesz usunąć.
Więc prawdopodobnie lepiej jest użyć listy zamiast tablicy.
Konwertuj tablicę na listęList<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
Immortal Blue
1
@ImmortalBlue lub po prostu var myList = myArray.ToList();używając Enumerable.ToList()metody z System.Linqprzestrzeni nazw.
Dyndrilliac
59
Używam tej metody do usuwania elementu z tablicy obiektów. W mojej sytuacji moje tablice mają małą długość. Więc jeśli masz duże tablice, możesz potrzebować innego rozwiązania.
privateint[] RemoveIndices(int[] IndicesArray, int RemoveAt)
{
int[] newIndicesArray = newint[IndicesArray.Length - 1];
int i = 0;
int j = 0;
while (i < IndicesArray.Length)
{
if (i != RemoveAt)
{
newIndicesArray[j] = IndicesArray[i];
j++;
}
i++;
}
return newIndicesArray;
}
Osobiście podoba mi się ta odpowiedź bardziej niż zaakceptowana odpowiedź. Powinien być równie wydajny i znacznie łatwiejszy do odczytania. Mogę na to spojrzeć i wiem, że jest poprawna. Musiałbym przetestować drugą, aby upewnić się, że te kopie zostały napisane poprawnie.
oillio
1
Naprawdę szkoda, że ta odpowiedź jest tak niska, podczas gdy jest o wiele lepsza niż dwie powyżej.
Sepulchritude
Aaarhg, to jest odpowiedź, której szukałem! To najlepsza metoda bez list.
Jordi Huertas
47
Rozwiązanie jednowierszowe LINQ:
myArray = myArray.Where((source, index) => index != 1).ToArray();
W 1tym przykładzie jest to indeks elementu do usunięcia - w tym przykładzie, zgodnie z oryginalnym pytaniem, drugi element ( 1będący drugim elementem w C # indeksowaniu tablicy od zera).
W przypadku obszarów wymagających wysokiej wydajności / częstego dostępu LINQ nie jest zalecane.
Krythic
3
@Krythic To uczciwy komentarz. Uruchomione tysiące razy w ciasnej pętli, wydajność tego rozwiązania nie jest tak dobra, jak niektórych innych wysoko ocenianych
Jon Schneider
9
Jest to sposób na usunięcie elementu tablicy, począwszy od .Net 3.5, bez kopiowania do innej tablicy - używając tej samej instancji tablicy z Array.Resize<T>:
publicstaticvoidRemoveAt<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);
}
„bez kopiowania do innej tablicy” - za połączonego dokumentacji Array.Resize faktycznie robi przydzielić nową tablicę za kulisy, a kopie elementów ze starej tablicy na nową. Mimo wszystko podoba mi się zwięzłość tego rozwiązania.
Jon Schneider,
Bardzo ładne i jasne, jeśli masz pewność, że jest to stosunkowo mała tablica.
Darren,
1
Kontynuując komentarz @ JonSchneider, nie jest to „ta sama instancja tablicy”. Dlatego musisz użyć reftej Resizemetody. Długość wystąpienia tablicy jest stała i niezmienna.
Jeppe Stig Nielsen
2
Jeśli kolejność elementów nie jest ważna, zamiast przesuwać wszystkie elementy w dół, można zamienić element pod indeksem na ostatni element, a następnie zmienić rozmiar: arr [index] = arr [arr.Length - 1]; Array.Resize (ref arr, arr.Length - 1);
Bartel
5
Oto moja stara wersja, która działa na wersji 1.0 platformy .NET i nie wymaga typów ogólnych.
publicstatic Array RemoveAt(Array source, int index)
{
if (source == null)
thrownew ArgumentNullException("source");
if (0 > index || index >= source.Length)
thrownew 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;
}
Jest to używane w następujący sposób:
classProgram
{
staticvoidMain(string[] args)
{
string[] x = newstring[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]);
}
}
Nie do końca jest to sposób na zrobienie tego, ale jeśli sytuacja jest trywialna i cenisz swój czas, możesz spróbować tego dla typów dopuszczających wartość null.
publicstatic 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;
}
W normalnej tablicy musisz przetasować wszystkie wpisy tablicy powyżej 2, a następnie zmienić ich rozmiar za pomocą metody Resize. Lepiej byłoby użyć ArrayList.
Oto mały zbiór metod pomocniczych, które stworzyłem na podstawie niektórych istniejących odpowiedzi. Wykorzystuje zarówno rozszerzenia, jak i metody statyczne z parametrami referencyjnymi dla maksymalnej idealności:
publicstaticclassArr
{
publicstaticintIndexOf<TElement>(this TElement[] Source, TElement Element)
{
for (var i = 0; i < Source.Length; i++)
{
if (Source[i].Equals(Element))
return i;
}
return-1;
}
publicstatic 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;
}
publicstatic TElement[] New<TElement>(params TElement[] Elements)
{
return Elements ?? new TElement[0];
}
publicstaticvoidRemove<TElement>(ref TElement[] Source, params TElement[] Elements)
{
foreach (var i in Elements)
RemoveAt(ref Source, Source.IndexOf(i));
}
publicstaticvoidRemoveAt<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;
}
}
Pod względem wydajności jest przyzwoity, ale prawdopodobnie można by go poprawić. Removeopiera się IndexOfi tworzona jest nowa tablica dla każdego elementu, który chcesz usunąć przez wywołanie RemoveAt.
IndexOfjest jedyną metodą rozszerzającą, ponieważ nie musi zwracać oryginalnej tablicy. Newakceptuje wiele elementów pewnego typu, aby utworzyć nową tablicę tego typu. Wszystkie inne metody muszą akceptować oryginalną tablicę jako odniesienie, więc nie ma potrzeby późniejszego przypisywania wyniku, ponieważ dzieje się to już wewnętrznie.
Zdefiniowałbym Mergemetodę łączenia dwóch tablic; Jednak można to już osiągnąć za pomocą Addmetody, przekazując rzeczywistą tablicę w porównaniu z wieloma pojedynczymi elementami. Dlatego Addmożna go użyć na dwa sposoby, aby połączyć dwa zestawy elementów:
Wiem, że ten artykuł ma dziesięć lat i dlatego prawdopodobnie nie żyje, ale oto, co spróbuję zrobić:
Użyj metody IEnumerable.Skip (), którą można znaleźć w System.Linq . Pominie wybrany element z tablicy i zwróci kolejną kopię tablicy, która zawiera tylko wszystko oprócz zaznaczonego obiektu. Następnie powtórz to dla każdego elementu, który chcesz usunąć, a następnie zapisz go w zmiennej.
Na przykład, jeśli mamy tablicę o nazwie „Próbka” (typu int []) z 5 liczbami. Chcemy usunąć drugi, więc próbujemy "Sample.Skip (2);" powinien zwrócić tę samą tablicę, ale bez drugiej liczby.
Czy ta metoda nie pomija określoną liczbę elementów w sekwencji, a następnie zwraca pozostałe elementy ? W twoim przykładzie „pominiesz” pierwsze dwa elementy listy ogólnej, a nie tylko drugi!
xnr_z
-5
Pierwszy krok
Musisz przekonwertować tablicę na listę, możesz napisać taką metodę rozszerzającą
// Convert An array of string to a list of stringpublicstatic List<string> ConnvertArrayToList(thisstring [] 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;
}
Drugi krok
Napisz metodę rozszerzenia, aby przekonwertować listę z powrotem na tablicę
// convert a list of string to an array publicstaticstring[] ConvertListToArray(this List<string> list) {
string[] array = newstring[list.Capacity];
array = list.Select(i => i.ToString()).ToArray();
return array;
}
Ostatnie kroki
Napisz ostateczną metodę, ale pamiętaj, aby usunąć element w indeksie przed konwersją z powrotem do tablicy, takiej jak w kodzie
System.Collections.ObjectModel.Collection<Foo>
.Odpowiedzi:
Jeśli nie chcesz korzystać z listy:
var foos = new List<Foo>(array); foos.RemoveAt(index); return foos.ToArray();
Możesz wypróbować tę metodę rozszerzenia, której tak naprawdę nie testowałem:
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; }
I używaj go jak:
Foo[] bar = GetFoos(); bar = bar.RemoveAt(2);
źródło
Naturą tablic jest to, że ich długość jest niezmienna. Nie możesz dodawać ani usuwać żadnych elementów tablicy.
Będziesz musiał utworzyć nową tablicę, która jest o jeden element krótsza, i skopiować stare elementy do nowej tablicy, z wyłączeniem elementu, który chcesz usunąć.
Więc prawdopodobnie lepiej jest użyć listy zamiast tablicy.
źródło
List<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
var myList = myArray.ToList();
używającEnumerable.ToList()
metody zSystem.Linq
przestrzeni nazw.Używam tej metody do usuwania elementu z tablicy obiektów. W mojej sytuacji moje tablice mają małą długość. Więc jeśli masz duże tablice, możesz potrzebować innego rozwiązania.
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; }
źródło
Rozwiązanie jednowierszowe LINQ:
myArray = myArray.Where((source, index) => index != 1).ToArray();
W
1
tym przykładzie jest to indeks elementu do usunięcia - w tym przykładzie, zgodnie z oryginalnym pytaniem, drugi element (1
będący drugim elementem w C # indeksowaniu tablicy od zera).Bardziej kompletny przykład:
string[] myArray = { "a", "b", "c", "d", "e" }; int indexToRemove = 1; myArray = myArray.Where((source, index) => index != indexToRemove).ToArray();
Po uruchomieniu tego fragmentu wartość
myArray
will będzie{ "a", "c", "d", "e" }
.źródło
Jest to sposób na usunięcie elementu tablicy, począwszy od .Net 3.5, bez kopiowania do innej tablicy - używając tej samej instancji tablicy z
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); }
źródło
ref
tejResize
metody. Długość wystąpienia tablicy jest stała i niezmienna.Oto moja stara wersja, która działa na wersji 1.0 platformy .NET i nie wymaga typów ogólnych.
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; }
Jest to używane w następujący sposób:
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]); } }
źródło
Nie do końca jest to sposób na zrobienie tego, ale jeśli sytuacja jest trywialna i cenisz swój czas, możesz spróbować tego dla typów dopuszczających wartość null.
Foos[index] = null
a później sprawdź puste wpisy w logice.
źródło
Jak zwykle spóźniam się na imprezę ...
Chciałbym dodać kolejną opcję do już istniejącej listy fajnych rozwiązań. =)
Uznałbym to za dobrą okazję do rozszerzeń.
Źródła: http://msdn.microsoft.com/en-us/library/bb311042.aspx
Dlatego definiujemy pewną statyczną klasę, aw niej naszą metodę.
Potem możemy użyć naszej rozszerzonej metody chcąc nie chcąc. =)
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" */ } } }
źródło
Wypróbuj poniższy kod:
lub
myArray = myArray.Where(s => (s != "not_this")).ToArray();
źródło
Oto jak to zrobiłem ...
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; }
źródło
W normalnej tablicy musisz przetasować wszystkie wpisy tablicy powyżej 2, a następnie zmienić ich rozmiar za pomocą metody Resize. Lepiej byłoby użyć ArrayList.
źródło
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; }
źródło
Oto mały zbiór metod pomocniczych, które stworzyłem na podstawie niektórych istniejących odpowiedzi. Wykorzystuje zarówno rozszerzenia, jak i metody statyczne z parametrami referencyjnymi dla maksymalnej idealności:
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; } }
Pod względem wydajności jest przyzwoity, ale prawdopodobnie można by go poprawić.
Remove
opiera sięIndexOf
i tworzona jest nowa tablica dla każdego elementu, który chcesz usunąć przez wywołanieRemoveAt
.IndexOf
jest jedyną metodą rozszerzającą, ponieważ nie musi zwracać oryginalnej tablicy.New
akceptuje wiele elementów pewnego typu, aby utworzyć nową tablicę tego typu. Wszystkie inne metody muszą akceptować oryginalną tablicę jako odniesienie, więc nie ma potrzeby późniejszego przypisywania wyniku, ponieważ dzieje się to już wewnętrznie.Zdefiniowałbym
Merge
metodę łączenia dwóch tablic; Jednak można to już osiągnąć za pomocąAdd
metody, przekazując rzeczywistą tablicę w porównaniu z wieloma pojedynczymi elementami. DlategoAdd
można go użyć na dwa sposoby, aby połączyć dwa zestawy elementów:Arr.Add<string>(ref myArray, "A", "B", "C");
Lub
Arr.Add<string>(ref myArray, anotherArray);
źródło
Wiem, że ten artykuł ma dziesięć lat i dlatego prawdopodobnie nie żyje, ale oto, co spróbuję zrobić:
Użyj metody IEnumerable.Skip (), którą można znaleźć w System.Linq . Pominie wybrany element z tablicy i zwróci kolejną kopię tablicy, która zawiera tylko wszystko oprócz zaznaczonego obiektu. Następnie powtórz to dla każdego elementu, który chcesz usunąć, a następnie zapisz go w zmiennej.
Na przykład, jeśli mamy tablicę o nazwie „Próbka” (typu int []) z 5 liczbami. Chcemy usunąć drugi, więc próbujemy "Sample.Skip (2);" powinien zwrócić tę samą tablicę, ale bez drugiej liczby.
źródło
Pierwszy krok
Musisz przekonwertować tablicę na listę, możesz napisać taką metodę rozszerzającą
// 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; }
Drugi krok
Napisz metodę rozszerzenia, aby przekonwertować listę z powrotem na tablicę
// 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; }
Ostatnie kroki
Napisz ostateczną metodę, ale pamiętaj, aby usunąć element w indeksie przed konwersją z powrotem do tablicy, takiej jak w kodzie
public static string[] removeAt(string[] array, int index) { List<string> myList = array.ConnvertArrayToList(); myList.RemoveAt(index); return myList.ConvertListToArray(); }
Przykładowe kody można znaleźć na moim blogu , śledźcie dalej.
źródło
.ToArray()
iList<T>
konstruktora, który bierze istniejącą sekwencję ...