C#移动列表中的一部分项目

时间:2013-06-17 22:42:53

标签: c# list

我发现这比我想象的要困难得多。如何在列表中移动一部分项目?

例如,如果我有以下列表:

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)

请注意,从开头删除部分时,插入位置已更改。这使得它变得更加困难。

5 个答案:

答案 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方法SkipTake,这些方法通常很有用。此外,您可能对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]);
}