我发现这比我想象的要困难得多。如何在列表中移动一部分项目?
例如,如果我有以下列表:
List<int> myList = new List<int>();
for(int i=0; i<10; i++) {
myList.Add(i);
}
此列表包含{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
。
如何移动List的各个部分?假设我想将{ 7, 8, 9 }
移动到第4个索引,使其成为:
{ 0, 1, 2, 3, 7, 8, 9, 4, 5, 6 }
或者说,我想将{ 1, 2 }
中的{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
移至第8个索引,并将其设为:
{ 0, 3, 4, 5, 6, 7, 1, 2, 8, 9 }
任何人都可以提供一些代码吗?像下面那样需要3个值的东西会很棒。
MoveSection(insertionPoint, startIndex, endIndex)
请注意,从开头删除部分时,插入位置已更改。这使得它变得更加困难。
答案 0 :(得分:5)
对于使用迭代器块的任何IEnumerable
,您可以相对简单地执行此操作。我总是发现使用yield return
构造以清晰简洁的方式解决了这类问题。在这里,我还将该方法转换为易于使用的扩展方法:
public static class Extension
{
public static IEnumerable<T> MoveSection<T>(this IEnumerable<T> @this, int insertionPoint, int startIndex, int endIndex)
{
var counter = 0;
var numElements = endIndex - startIndex;
var range = Enumerable.Range(startIndex, numElements);
foreach(var i in @this)
{
if (counter == insertionPoint) {
foreach(var j in @this.Skip(startIndex).Take(numElements)) {
yield return j;
}
}
if (!range.Contains(counter)) {
yield return i;
}
counter++;
}
//The insertion point might have been after the entire list:
if (counter++ == insertionPoint) {
foreach(var j in @this.Skip(startIndex).Take(numElements)) {
yield return j;
}
}
}
}
在这里,我使用Linq方法Skip和Take,这些方法通常很有用。此外,您可能对Enumerable.Range
方法感兴趣,该方法可以像使用for
循环一样轻松创建范围。
然后您可以像这样调用方法:
myList.MoveSection(8, 1, 3);
答案 1 :(得分:1)
如果您可以创建另一个列表,请使用GetRange/AddRange
进行一些简单的更正
如果你想在现场进行,它就是这样的(似乎与你的用例一起使用,但我建议进行一些适当的单元测试):
public static void MoveRange<T>(this IList<T> list, int startIndex, int count, int targetIndex) {
var correctedStartIndex = startIndex;
var correctedTargetIndex = targetIndex;
for (var i = count - 1; i >= 0; i--) {
var item = list[correctedStartIndex + i];
list.RemoveAt(correctedStartIndex + i);
if (correctedTargetIndex > correctedStartIndex + i)
correctedTargetIndex -= 1;
list.Insert(correctedTargetIndex, item);
if (correctedStartIndex > correctedTargetIndex)
correctedStartIndex += 1;
}
}
请注意,我没有添加任何验证(与插入点的范围交集,源列表在列表之外等)。如果您在实际项目中进行扩展方法,我建议验证所有这些。
答案 2 :(得分:1)
好的,详细说明我上面的评论,让我们尝试将实现作为LinkedList<T>
的扩展方法:
我无法以任何方式测试它,我只是用记事本编码。
startIndex
=您要移动的部分的起始索引
endIndex
=您要移动的部分的结束索引(包括)
moveIndex
=要将部分移动到的索引。 0
=列表的开头,list.Count
=列表的结尾。
public static bool MoveSection<T>(this LinkedList<T> list, int startIndex, int endIndex, int moveIndex){
//bounds checking
if (startIndex < moveIndex && moveIndex < endIndex){
return false;
}
if (list.Count <= startIndex || list.Count <= endIndex || list.Count+1 <= moveIndex){
return false;
}
if (startIndex >= endIndex){
return false;
}
LinkedListNode<T> startNode = list.ElementAt(startIndex);
LinkedListNode<T> endNode = list.ElementAt(endIndex);
LinkedListNode<T> restMoveNode = null;
LinkedListNode<T> insertAfterNode;
if (moveIndex < list.Count) {
//when not inserting at the end of the list
restMoveNode = list.ElementAt(moveIndex);
insertAfterNode = restMoveNode.Previous;
} else {
//when inserting at the end of the list
insertAfterNode = list.ElementAt(moveIndex - 1);
}
if (insertAfterNode == null){
//when inserting at the beginning of the list
list.AddFirst(startNode);
} else {
insertAfterNode.Next = startNode;
}
//restore previous list elements
endNode.next = restMoveNode;
return true;
}
虽然@jmyns已经发布了链接列表的答案,但我认为我的解决方案可以更好地提供链接列表的概念。
答案 3 :(得分:0)
如何用自己的方法包装这样的东西:
List<int> subList = myList.GetRange(startIndex, count);
myList.RemoveRange(startIndex, count);
myList.InsertRange(insertionPoint, subList);
答案 4 :(得分:0)
链接列表可能是理想的,但它取决于您的初始列表的大小。每个节点都有一个指向下一个节点的指针,这使得移动变得简单。增加了费用,许多人更喜欢使用常规列表。
LinkedList<int> linked = new LinkedList<int>();
linked = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
声明要在数组中移动的项目并将其从myList中删除
int [] itemsToMove = { 1, 2 };
for (int i = 0; i < itemsToMove -1; i++)
{
linked.Remove(itemsToMove[i])
}
现在链接将是
linked = { 0, 3, 4, 5, 6, 7, 8, 9 }
识别目标节点
LinkedListNode<int> targetNode = linked.Find("7");
然后迭代在目标节点之后添加项目的项目。如果顺序重要,首先要反转数组。
Array.Reverse(itemsToMove);
for (int i = 0; i < itemsToMove.Length - 1; i++)
{
linked.AddAfter(targetNode , itemsToMove[i]);
}