List <T>の2つのアイテムを交換します


82

内の2つのアイテムの位置を交換するLINQの方法はありlist<T>ますか?


57
なぜ彼がこれをしたいのかが重要なのはなぜですか。「スワップリストアイテムc#」をグーグルで検索している人は、この特定の質問に対する直接的な答えを求めています。
ダニエルマシアス2012年

10
@DanielMaciasこれは本当です。これらの答えは「しかし、なぜあなたはこれをしているのですか?」とても迷惑です。理由を議論する前に、少なくとも実行可能な答えを提供する必要があると思います。
julealgon 2014

なぜこれを行うためにLINQを使用したいのですか?LINQ固有の場合は、タイトルを変更してLINQを追加しませんか
2017年

回答:


119

C#のMarcからの回答を確認してください:Swapメソッドの適切/最良の実装

public static void Swap<T>(IList<T> list, int indexA, int indexB)
{
    T tmp = list[indexA];
    list[indexA] = list[indexB];
    list[indexB] = tmp;
}

これは、次のようにlinq-i-fiedできます

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

10
この拡張メソッドがリストを返す必要があるのはなぜですか?リストをインプレースで変更しています。
vargonian 2012

13
次に、メソッドを連鎖させることができます。
Jan Jongboom 2012

Unity3Dでこれを使用するのに疲れている場合は、拡張メソッドを公開することを忘れないでください!(あなたがいない場合、Unityはそれを見つけることができません)
col000r 2014

素晴らしくて簡単。ありがとう
fnc12 2015年

5
警告:「LINQified」バージョンでも元のリストが変更されます。
PhilippMichalski19年

32

誰かがこれを行うための賢い方法を考えるかもしれませんが、あなたはそうすべきではありません。リスト内の2つのアイテムを交換すると、本質的に副作用が発生しますが、LINQ操作には副作用がないはずです。したがって、単純な拡張メソッドを使用するだけです。

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

副作用?詳細を教えていただけますか?
トニーライオン

12
何も変更していないのと同じ照会データとは対照的に、 -副作用によって、彼は、彼らが、リスト、リスト内の可能性の項目を変更する意味
saret

「Ttemp = list [firstIndex];」を実行します リスト内のオブジェクトのディープコピーを作成しますか?
ポールマッカーシー

1
@PaulMcCarthyいいえ、元のオブジェクトへの新しいポインタを作成します。コピーはまったく行いません。
NetMage

12

List<T>にはReverse()メソッドがありますが、2つ(またはそれ以上)の連続するアイテムの順序を逆にするだけです。

your_list.Reverse(index, 2);

2番目のパラメーター2は、指定されたのアイテムから始めて、2つのアイテムの順序を逆にしていることを示していますindex

ソース:https//msdn.microsoft.com/en-us/library/hf2ay11y(v = vs.110).aspx


1
グーグルが私をここに連れてきましたが、これは私が探していたものです。
Jnr 2018年

1
.Reverse()関数はLinqではなく、Swapとは呼ばれませんが、質問の目的は、スワップを実行する「組み込み」関数を見つけることであるように思われます。これが唯一の答えです。
アメリカス2018

ただし、隣接する要素を交換する場合にのみ役立つため、実際には答えにはなりません。
NetMage

10

既存のスワップメソッドはないため、自分で作成する必要があります。もちろん、それをlinqifyすることはできますが、それは1つの(書かれていない?)ルールを念頭に置いて行う必要があります。LINQ-操作は入力パラメーターを変更しません!

他の「linqify」の回答では、(入力)リストが変更されて返されますが、このアクションはそのルールを破ります。並べ替えられていないアイテムを含むリストがある場合に奇妙な場合は、LINQ "OrderBy"操作を実行してから、入力リストも並べ替えられていることを確認します(結果と同じように)。これは許可されていません!

だから...どうやってこれを行うのですか?

私が最初に考えたのは、反復が終了した後にコレクションを復元することだけでした。しかし、これは汚い解決策なので、使用しないでください。

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

それはので、このソリューションは、汚れてた入力リストを変更することが元の状態に戻した場合でも、。これにより、いくつかの問題が発生する可能性があります。

  1. リストは読み取り専用である可能性があり、例外がスローされます。
  2. リストが複数のスレッドで共有されている場合、この機能の実行中に他のスレッドのリストが変更されます。
  3. 反復中に例外が発生した場合、リストは復元されません。(これは、swap関数内にtry-finallyを記述し、finallyブロック内にrestore-codeを配置することで解決できます)。

より良い(そしてより短い)解決策があります:元のリストのコピーを作成するだけです。(これにより、IListの代わりにIEnumerableをパラメーターとして使用することもできます):

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

このソリューションの欠点の1つは、リスト全体をコピーしてメモリを消費するため、ソリューションがかなり遅くなることです。

次の解決策を検討してください。

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

また、パラメータをIEnumerableに入力する場合は、次のようにします。

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は、ソース(のサブセット)のコピーも作成します。したがって、最悪のシナリオでは、関数Swap2と同じくらい遅く、メモリを消費します。


0

順序が重要な場合は、リスト内の「T」オブジェクトにシーケンスを示すプロパティを保持する必要があります。それらを交換するために、ちょうどそのプロパティの値を交換し、その後.Sort(にそれを使用する配列プロパティとの比較


これは、Tに固有の「順序」があることに概念的に同意できる場合ですが、UIなど、固有の「順序」なしで任意の方法で並べ替えたい場合はそうではありません。
Dave Van den Eynde 2010年

@DaveVandenEynde、私はかなり初心者のプログラマーなので、これが大きな質問ではない場合はご容赦ください。状況に順序の概念が含まれていない場合、リスト内のアイテムを交換することの意味は何ですか?
Mathieu K.

@ MathieuK.well私が言いたかったのは、この回答は、順序がシーケンスを並べ替えることができるオブジェクトのプロパティから派生することを提案しているということです。通常はそうではありません。
Dave Van den Eynde 2017年

私はこの答えを(おそらく誤解して)「オブジェクトに番号を付け、プロパティに番号を格納します。次に、2つのアイテムを交換するには、それらの番号を交換します。このプロパティでソートされたリストを使用します」と理解しています。
Mathieu K.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.