it-swarm.com.de

Tauschen Sie zwei Elemente in Liste <T> aus

Gibt es eine LINQ-Möglichkeit, die Position zweier Elemente in einem list<T> Zu tauschen?

63
Tony The Lion

Ü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);
100
Jan Jongboom

Vielleicht fällt jemandem ein kluger Weg dazu ein, aber das sollten Sie nicht. Das Vertauschen von zwei Elementen in einer Liste ist von Natur aus nebenwirkungsfrei, LINQ-Vorgänge sollten jedoch nebenwirkungsfrei 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;
    }
}
30
jason

Da es keine Swap-Methode gibt, müssen Sie selbst eine erstellen. Natürlich können Sie es auch linqifizieren, aber das muss mit einer (ungeschriebenen?) Regel geschehen: 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 hätten, führen Sie eine LINQ "OrderBy" -Operation durch und stellen Sie dann fest, dass die Eingabeliste auch sortiert ist (genau wie das Ergebnis). Das darf nicht passieren!

Also ... wie machen wir das?

Mein erster Gedanke war, die Sammlung wiederherzustellen, nachdem die Iteration abgeschlossen war. Aber das ist eine schmutzige Lösung, also benutze sie 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 does die Eingabeliste ändert, auch wenn sie den ursprünglichen Zustand wiederherstellt. Dies kann verschiedene Probleme verursachen:

  1. Die Liste könnte schreibgeschützt sein, was eine Ausnahme auslöst.
  2. Wenn die Liste von mehreren Threads gemeinsam genutzt wird, ändert sich die Liste für die anderen Threads während der Dauer dieser Funktion.
  3. Wenn während der Iteration eine Ausnahme auftritt, wird die Liste nicht wiederhergestellt. (Dies könnte gelöst werden, um ein try-finally in die Swap-Funktion zu schreiben und den Wiederherstellungscode in den finally-Block zu schreiben.).

Es gibt eine bessere (und kürzere) Lösung: Machen Sie einfach eine Kopie der ursprünglichen Liste. (Dies ermöglicht auch die Verwendung eines 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 dass 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 Parameter eingeben möchten, um IEnumerable zu sein:

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) der Quelle. Daher ist das Worst-Case-Szenario so langsam und speicherintensiv wie die Funktion Swap2.

10
Martin Mulder

Die Liste hat die umgekehrte Methode.

your_list.Reverse(i, 2) // will swap elements with indexs i, i + 1. 

Quelle: https://msdn.Microsoft.com/en-us/library/hf2ay11y (v = vs.110) .aspx

7
user1920925

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 .Sort (Vergleich mit der Sequenzeigenschaft).

0
CaffGeek