内の2つのアイテムの位置を交換するLINQの方法はありlist<T>
ますか?
内の2つのアイテムの位置を交換するLINQの方法はありlist<T>
ますか?
回答:
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);
誰かがこれを行うための賢い方法を考えるかもしれませんが、あなたはそうすべきではありません。リスト内の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;
}
}
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
既存のスワップメソッドはないため、自分で作成する必要があります。もちろん、それを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;
}
それはので、このソリューションは、汚れていた入力リストを変更することが元の状態に戻した場合でも、。これにより、いくつかの問題が発生する可能性があります。
より良い(そしてより短い)解決策があります:元のリストのコピーを作成するだけです。(これにより、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と同じくらい遅く、メモリを消費します。
順序が重要な場合は、リスト内の「T」オブジェクトにシーケンスを示すプロパティを保持する必要があります。それらを交換するために、ちょうどそのプロパティの値を交換し、その後.Sort(にそれを使用する配列プロパティとの比較)