Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Moving a section of items within the list

Tags:

c#

list

I'm finding this is a lot harder than I thought. How can I move a section of items within 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 would contain { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }.

How can I move sections of the List around? Say I want to move { 7, 8, 9 } to the the 4th index, making it:

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

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

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

Can anyone provide some code? Something that takes 3 values like the following would be great.

MoveSection(insertionPoint, startIndex, endIndex)

Note when you remove sections from the beginning the insertion location has changed. This makes it quite a bit more difficult.

like image 231
rotaercz Avatar asked Jun 17 '13 22:06

rotaercz


1 Answers

You can do this very generally for any IEnumerable using an iterator block relatively simply. I always find that using the yield return construct solves this type of problem in a clear and concise way. Here, I've also made the method into 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 methods Skip and Take, which are often useful. Also, you might be interested in the Enumerable.Range method, which allows for easy creation of the ranges like you do with a for loop instead.

You can then invoke the method like so:

myList.MoveSection(8, 1, 3);
like image 128
Ben Reich Avatar answered Oct 05 '22 13:10

Ben Reich