有没有一种LINQ方式可以交换a中两个项目的位置list<T>
?
有没有一种LINQ方式可以交换a中两个项目的位置list<T>
?
Answers:
从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;
}
可以像这样
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/zh-cn/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;
}
该解决方案是肮脏的,因为它确实修改了输入列表,即使将其恢复到原始状态也是如此。这可能会导致几个问题:
有一个更好(更短)的解决方案:只需复制原始列表即可。(这也使得可以使用IEnumerable代替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;
}
该解决方案的一个缺点是它复制了整个列表,这将消耗内存,并使解决方案相当慢。
您可以考虑以下解决方案:
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(与sequence属性比较)中使用它即可。