내부에서 두 항목의 위치를 바꾸는 LINQ 방법이 list<T>
있습니까?
내부에서 두 항목의 위치를 바꾸는 LINQ 방법이 list<T>
있습니까?
답변:
C #의 Marc의 답변 : Good / best implementation of Swap method .
public static void Swap<T>(IList<T> list, int indexA, int indexB)
{
T tmp = list[indexA];
list[indexA] = list[indexB];
list[indexB] = tmp;
}
다음과 같이 연결될 수 있습니다.
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);
누군가이 작업을 수행하는 영리한 방법을 생각할 수 있지만 그렇게해서는 안됩니다. 목록에서 두 항목을 바꾸는 것은 본질적으로 부작용이 많지만 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()
는 두 개 (또는 그 이상)의 순서 역전 단에있어서, 연속적인 품목.
your_list.Reverse(index, 2);
두 번째 매개 변수 2
는 주어진에서 항목부터 시작하여 2 개 항목의 순서를 반대로한다는 것을 나타냅니다 index
.
출처 : https://msdn.microsoft.com/en-us/library/hf2ay11y(v=vs.110).aspx
기존의 스왑 방법이 없으므로 직접 만들어야합니다. 물론 linqify 할 수 있지만 한 가지 (작성되지 않은?) 규칙을 염두에두고 수행해야합니다. 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;
}
이 솔루션의 한 가지 단점은 전체 목록을 복사하여 메모리를 소비하고 이로 인해 솔루션이 다소 느려진다는 것입니다.
다음 솔루션을 고려할 수 있습니다.
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 ( comparison with sequence property )에서 사용하십시오.