Gibt es eine LINQ-Möglichkeit, die Position von zwei Elementen innerhalb eines zu tauschen list<T>
?
Gibt es eine LINQ-Möglichkeit, die Position von zwei Elementen innerhalb eines zu tauschen list<T>
?
Antworten:
Überprüfen Sie die Antwort von Marc aus C #: Gute / beste Implementierung der Swap-Methode .
public static void Swap<T>(IList<T> list, int indexA, int indexB)
{
T tmp = list[indexA];
list[indexA] = list[indexB];
list[indexB] = tmp;
}
das kann wie linq-i-fied sein
public static IList<T> Swap<T>(this IList<T> list, int indexA, int indexB)
{
T tmp = list[indexA];
list[indexA] = list[indexB];
list[indexB] = tmp;
return list;
}
var lst = new List<int>() { 8, 3, 2, 4 };
lst = lst.Swap(1, 2);
Vielleicht wird sich jemand einen cleveren Weg überlegen, dies zu tun, aber das sollten Sie nicht. Das Austauschen von zwei Elementen in einer Liste ist von Natur aus mit Nebenwirkungen verbunden, LINQ-Vorgänge sollten jedoch ohne Nebenwirkungen sein. Verwenden Sie daher einfach eine einfache Erweiterungsmethode:
static class IListExtensions {
public static void Swap<T>(
this IList<T> list,
int firstIndex,
int secondIndex
) {
Contract.Requires(list != null);
Contract.Requires(firstIndex >= 0 && firstIndex < list.Count);
Contract.Requires(secondIndex >= 0 && secondIndex < list.Count);
if (firstIndex == secondIndex) {
return;
}
T temp = list[firstIndex];
list[firstIndex] = list[secondIndex];
list[secondIndex] = temp;
}
}
List<T>
hat eine Reverse()
Methode, kehrt jedoch nur die Reihenfolge von zwei (oder mehr) aufeinanderfolgenden Elementen um.
your_list.Reverse(index, 2);
Wenn der zweite Parameter 2
angibt, kehren wir die Reihenfolge von 2 Elementen um, beginnend mit dem angegebenen Element index
.
Quelle: https://msdn.microsoft.com/en-us/library/hf2ay11y(v=vs.110).aspx
Es gibt keine Swap-Methode, daher müssen Sie selbst eine erstellen. Natürlich können Sie es linqifizieren, aber das muss unter Berücksichtigung einer (ungeschriebenen?) Regel erfolgen: LINQ-Operationen ändern die Eingabeparameter nicht!
In den anderen "linqify" -Antworten wird die (Eingabe-) Liste geändert und zurückgegeben, aber diese Aktion bremst diese Regel. Wenn es seltsam wäre, wenn Sie eine Liste mit unsortierten Elementen haben, führen Sie eine LINQ "OrderBy" -Operation durch und stellen Sie dann fest, dass die Eingabeliste ebenfalls sortiert ist (genau wie das Ergebnis). Das darf nicht passieren!
Also ... wie machen wir das?
Mein erster Gedanke war nur, die Sammlung wiederherzustellen, nachdem die Iteration abgeschlossen war. Dies ist jedoch eine schmutzige Lösung. Verwenden Sie sie daher nicht:
static public IEnumerable<T> Swap1<T>(this IList<T> source, int index1, int index2)
{
// Parameter checking is skipped in this example.
// Swap the items.
T temp = source[index1];
source[index1] = source[index2];
source[index2] = temp;
// Return the items in the new order.
foreach (T item in source)
yield return item;
// Restore the collection.
source[index2] = source[index1];
source[index1] = temp;
}
Diese Lösung ist fehlerhaft, da sie die Eingabeliste ändert , auch wenn sie den ursprünglichen Zustand wiederherstellt. Dies kann verschiedene Probleme verursachen:
Es gibt eine bessere (und kürzere) Lösung: Erstellen Sie einfach eine Kopie der ursprünglichen Liste. (Dies ermöglicht auch die Verwendung einer IEnumerable als Parameter anstelle einer IList):
static public IEnumerable<T> Swap2<T>(this IList<T> source, int index1, int index2)
{
// Parameter checking is skipped in this example.
// If nothing needs to be swapped, just return the original collection.
if (index1 == index2)
return source;
// Make a copy.
List<T> copy = source.ToList();
// Swap the items.
T temp = copy[index1];
copy[index1] = copy[index2];
copy[index2] = temp;
// Return the copy with the swapped items.
return copy;
}
Ein Nachteil dieser Lösung besteht darin, dass sie die gesamte Liste kopiert, die Speicher verbraucht, und die Lösung dadurch ziemlich langsam wird.
Sie könnten die folgende Lösung in Betracht ziehen:
static public IEnumerable<T> Swap3<T>(this IList<T> source, int index1, int index2)
{
// Parameter checking is skipped in this example.
// It is assumed that index1 < index2. Otherwise a check should be build in and both indexes should be swapped.
using (IEnumerator<T> e = source.GetEnumerator())
{
// Iterate to the first index.
for (int i = 0; i < index1; i++)
yield return source[i];
// Return the item at the second index.
yield return source[index2];
if (index1 != index2)
{
// Return the items between the first and second index.
for (int i = index1 + 1; i < index2; i++)
yield return source[i];
// Return the item at the first index.
yield return source[index1];
}
// Return the remaining items.
for (int i = index2 + 1; i < source.Count; i++)
yield return source[i];
}
}
Und wenn Sie einen Parameter eingeben möchten, der IEnumerable ist:
static public IEnumerable<T> Swap4<T>(this IEnumerable<T> source, int index1, int index2)
{
// Parameter checking is skipped in this example.
// It is assumed that index1 < index2. Otherwise a check should be build in and both indexes should be swapped.
using(IEnumerator<T> e = source.GetEnumerator())
{
// Iterate to the first index.
for(int i = 0; i < index1; i++)
{
if (!e.MoveNext())
yield break;
yield return e.Current;
}
if (index1 != index2)
{
// Remember the item at the first position.
if (!e.MoveNext())
yield break;
T rememberedItem = e.Current;
// Store the items between the first and second index in a temporary list.
List<T> subset = new List<T>(index2 - index1 - 1);
for (int i = index1 + 1; i < index2; i++)
{
if (!e.MoveNext())
break;
subset.Add(e.Current);
}
// Return the item at the second index.
if (e.MoveNext())
yield return e.Current;
// Return the items in the subset.
foreach (T item in subset)
yield return item;
// Return the first (remembered) item.
yield return rememberedItem;
}
// Return the remaining items in the list.
while (e.MoveNext())
yield return e.Current;
}
}
Swap4 erstellt auch eine Kopie (eine Teilmenge davon) der Quelle. Im schlimmsten Fall ist es also so langsam und speicherintensiv wie die Funktion Swap2.
Wenn die Reihenfolge wichtig ist, sollten Sie eine Eigenschaft für die "T" -Objekte in Ihrer Liste behalten, die die Reihenfolge angibt. Um sie auszutauschen, tauschen Sie einfach den Wert dieser Eigenschaft aus und verwenden Sie ihn dann in der .Sort ( Vergleich mit der Sequenzeigenschaft ).