C # Moving a section of items in a list

I find it a lot more complicated than I thought. How to move a section of items in a list?

For example, if I have the following list:

List<int> myList = new List<int>(); for(int i=0; i<10; i++) { myList.Add(i); } 

This list will contain { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } .

How can I move sections of a list? Let's say I want to move { 7, 8, 9 } to the 4th index by doing this:

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

Or, say, I want to move { 1, 2 } to { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } by the 8th index by doing it:

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

Can someone provide the code? Something that takes 3 meanings, such as the following, would be great.

 MoveSection(insertionPoint, startIndex, endIndex) 

Please note that when deleting partitions from the beginning, the insertion location has changed. This makes it rather complicated.

+6
source share
5 answers

You can do this in general for any IEnumerable , using the iterator block is relatively simple. I always find that using the yield return construct solves this type of problem in a clear and concise way. Here I also used the method as an extension method for ease of use:

 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; } } } } 

Here I use the Linq Skip and Take methods, which are often useful. You might also be interested in the Enumerable.Range method, which makes it easy to create ranges, as is the case with the for loop.

Then you can call the method as follows:

 myList.MoveSection(8, 1, 3); 
+4
source

If you're fine with creating another list, use GetRange/AddRange with some simple correction.
If you want to do this in place, it will be something like this (it seems to work with your use cases, but I recommend some correct unit tests):

 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; } } 

Please note that I did not add any check (intersection of the range with the insertion point, the range of the source is outside the list, etc.). If you are implementing the extension method in a real project, I suggest checking all of this.

+1
source

OK, detailing my comment above, try the implementation as an extension method to LinkedList<T> :

I could not test it in any way, I just encoded it in a notebook.
startIndex = start the index of the section you want to move
endIndex = The end index of the section you want to move (inclusive)
moveIndex = index into which you want to move the section. 0 = start of list, list.Count = end of list.

 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; } 

Although @jmyns already posted a response with a linked list, I think my solution best supports the idea of ​​a linked list.

+1
source

How to wrap something like this in your own method:

 List<int> subList = myList.GetRange(startIndex, count); myList.RemoveRange(startIndex, count); myList.InsertRange(insertionPoint, subList); 
0
source

A linked list may be perfect, but it depends on how big your initial list is. Each node has a pointer to the next one, which makes for simple moves. There is an additional expense, and many people prefer to use regular listings.

 LinkedList<int> linked = new LinkedList<int>(); linked = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } 

Declare the elements you want to move in the array and remove them from myList

 int [] itemsToMove = { 1, 2 }; for (int i = 0; i < itemsToMove -1; i++) { linked.Remove(itemsToMove[i]) } 

Now the link will be

 linked = { 0, 3, 4, 5, 6, 7, 8, 9 } 

Define a Node Goal

 LinkedListNode<int> targetNode = linked.Find("7"); 

Then iterate over the elements that add elements after the target node. If order is important, reverse the array first.

 Array.Reverse(itemsToMove); for (int i = 0; i < itemsToMove.Length - 1; i++) { linked.AddAfter(targetNode , itemsToMove[i]); } 
0
source

Source: https://habr.com/ru/post/947508/