9

これは思ったより大変なことだと思います。リスト内の項目のセクションを移動するにはどうすればよいですか?

たとえば、次のリストがあるとします。

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 }

リストのセクションを移動するにはどうすればよいですか? { 7, 8, 9 }4 番目のインデックスに移動して、次のようにします。

{ 0, 1, 2, 3, 7, 8, 9, 4, 5, 6 }

または、次のようにして、8 番目のインデックスに移動{ 1, 2 }したいとします。{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }

{ 0, 3, 4, 5, 6, 7, 1, 2, 8, 9 }

誰でもコードを提供できますか? 次のような 3 つの値を取るものは素晴らしいでしょう。

MoveSection(insertionPoint, startIndex, endIndex)

最初からセクションを削除すると、挿入場所が変更されることに注意してください。これにより、かなり難しくなります。

4

5 に答える 5

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);
于 2013-06-17T23:08:50.987 に答える
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;            
    }
}

検証をまったく追加していないことに注意してください (挿入ポイントとの範囲の交差、ソース範囲がリストの外にあるなど)。実際のプロジェクトで拡張メソッドを実行している場合は、そのすべてを検証することをお勧めします。

于 2013-06-17T23:14:20.097 に答える
1

OK、上記の私のコメントを詳しく説明して、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 はすでにリンクされたリストを使用して回答を投稿していますが、私のソリューションはリンクされたリストのアイデアにより適していると思います。

于 2013-06-18T10:30:51.630 に答える
0

あなた自身の方法でこのようなものをラップするのはどうですか:

List<int> subList = myList.GetRange(startIndex, count);
myList.RemoveRange(startIndex, count);
myList.InsertRange(insertionPoint, subList);
于 2013-06-17T23:08:19.243 に答える
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]);
}
于 2013-06-17T23:39:17.710 に答える