Author Topic: List<T> MoveItem  (Read 2568 times)

0 Members and 1 Guest are viewing this topic.

kdub_nz

  • Mesozoic keyThumper
  • SuperMod
  • Water Moccasin
  • Posts: 2138
  • class keyThumper<T>:ILazy<T>
List<T> MoveItem
« on: May 29, 2017, 09:21:11 AM »
Anyone like to cast an eye over this looking for edge cases I've missed.


Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4.  
  5. namespace ReOrderList
  6. {
  7.     public static class Program
  8.     {
  9.         static void Main(string[] args)
  10.         {
  11.             var dataList = new List<string>() {"Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot"};
  12.  
  13.             var newList = dataList.ToList();
  14.  
  15.             Console.WriteLine("newList List Data");
  16.             foreach (var data in dataList){
  17.                 Console.WriteLine(data);
  18.             }
  19.  
  20.             newList = newList.MoveItem(0, 4);
  21.             Console.WriteLine("\nRevised List Data: newList.MoveItem(0, 4)");
  22.             foreach (var data in newList){
  23.                 Console.WriteLine(data);
  24.             }
  25.  
  26.             newList = newList.MoveItem(0, 5);
  27.             Console.WriteLine("\nRevised List Data: newList.MoveItem(0, 5)");
  28.             foreach (var data in newList){
  29.                 Console.WriteLine(data);
  30.             }
  31.  
  32.             newList = newList.MoveItem(5, 1);
  33.             Console.WriteLine("\nRevised List Data: newList.MoveItem(5, 1)");
  34.             foreach (var data in newList){
  35.                 Console.WriteLine(data);
  36.             }
  37.  
  38.             newList = newList.MoveItem(5, 0);   //<<<<<revised was (5, 1)
  39.             Console.WriteLine("\nRevised List Data: newList.MoveItem(5, 0)");
  40.             foreach (var data in newList){
  41.                 Console.WriteLine(data);
  42.             }
  43.  
  44.             newList = newList.MoveItem(1, 1);
  45.             Console.WriteLine("\nRevised List Data: newList.MoveItem(1, 1)");
  46.             foreach (var data in newList){
  47.                 Console.WriteLine(data);
  48.             }
  49.  
  50.             newList = newList.MoveItem(0, -1);
  51.             Console.WriteLine("\nRevised List Data: newList.MoveItem(0, -1)");
  52.             foreach (var data in newList){
  53.                 Console.WriteLine(data);
  54.             }
  55.  
  56.             newList = newList.MoveItem(0, 6);
  57.             Console.WriteLine("\nRevised List Data: newList.MoveItem(0, 6)");
  58.             foreach (var data in newList){
  59.                 Console.WriteLine(data);
  60.             }
  61.         }
  62.     }
  63.  
  64.     public static class ListExtensions
  65.     {
  66.         public static List<T> MoveItem<T>(this List<T> list, int oldIndex, int newIndex)
  67.         {
  68.             // return unchanged if index's are outside scope
  69.             if ((oldIndex == newIndex) ||
  70.                 (oldIndex < 0) ||
  71.                 (oldIndex >= list.Count) ||
  72.                 (newIndex < 0) ||
  73.                 (newIndex >= list.Count))
  74.                 return list;
  75.  
  76.             T dataToMove = list[oldIndex];
  77.  
  78.             list.RemoveAt(oldIndex);
  79.             list.Insert(newIndex, dataToMove);
  80.             return list;
  81.         }
  82.     }
  83. }
  84.  




newList List Data
Alpha
Bravo
Charlie
Delta
Echo
Foxtrot

Revised List Data: newList.MoveItem(0, 4)
Bravo
Charlie
Delta
Echo
Alpha
Foxtrot

Revised List Data: newList.MoveItem(0, 5)
Charlie
Delta
Echo
Alpha
Foxtrot
Bravo

Revised List Data: newList.MoveItem(5, 1)
Charlie
Bravo
Delta
Echo
Alpha
Foxtrot

Revised List Data: newList.MoveItem(5, 0)
Charlie
Foxtrot
Bravo
Delta
Echo
Alpha

Revised List Data: newList.MoveItem(1, 1)
Charlie
Foxtrot
Bravo
Delta
Echo
Alpha

Revised List Data: newList.MoveItem(0, -1)
Charlie
Foxtrot
Bravo
Delta
Echo
Alpha

Revised List Data: newList.MoveItem(0, 6)
Charlie
Foxtrot
Bravo
Delta
Echo
Alpha




The last 3 examples are returning the param list because the oldIndex or newIndex are out of range.

For those with an inquiring mind, I'm sure a use could be found for this extension method.

Regards,
« Last Edit: May 30, 2017, 07:48:42 AM by kdub »
Called Kerry in my other life
Retired; but they dragged me back in !

I live at UTC + 13.00

---
some people complain about loading the dishwasher.
Sometimes the question is more important than the answer.

n.yuan

  • Bull Frog
  • Posts: 348
Re: List<T> MoveItem
« Reply #1 on: May 29, 2017, 12:45:52 PM »
Practical, simple and useful code. Thanks for sharing.

Just a thought, though:

Seeing the actual implementation of the extension method, the code does not create a new List<T> object. It only shuffle the other of the same T references in the same List object. Thus, it might be better that the extension method being void, instead of returning the same list back, because if one programmer uses this extension method without knowing how it is implemented, he/she might think the returned list is a new List object (holding the same references to T types (thus, calling this methods, it ends up with 2 List<T> objects, both hold the same list of T objects).

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: List<T> MoveItem
« Reply #2 on: May 29, 2017, 01:18:41 PM »
Nice Kerry,

Just two one remarks:
As you modify the input list, the method could return void (like the List.Sort () method). (see Norman's reply)
By my side, I would let the OutOfRangeException occur instead of hiding it as you do.

Here's how I'd write it for a T[] array (the same can be done with a List<T>)

Code - C#: [Select]
  1.         public static T[] MoveItem<T>(this T[] source, int oldIndex, int newIndex)
  2.         {
  3.             var result = new T[source.Length];
  4.             source.CopyTo(result, 0);
  5.             if (oldIndex < newIndex)
  6.             {
  7.                 for (int i = oldIndex; i < newIndex; i++)
  8.                 {
  9.                     result[i] = source[i + 1];
  10.                 }
  11.             }
  12.             else if (newIndex < oldIndex)
  13.             {
  14.                 for (int i = newIndex; i < oldIndex; i++)
  15.                 {
  16.                     result[i + 1] = source[i];
  17.                 }
  18.             }
  19.             result[newIndex] = source[oldIndex];
  20.             return result;
  21.         }
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: List<T> MoveItem
« Reply #3 on: May 29, 2017, 01:51:30 PM »
Closer to built-in IEnumerable<T> extension methods:

Code - C#: [Select]
  1.         public static IEnumerable<T> MoveItem<T>(this IEnumerable<T> source, int oldIndex, int newIndex)
  2.         {
  3.             var array = source.ToArray();
  4.             if (oldIndex < 0 || oldIndex >= array.Length)
  5.                 throw new ArgumentOutOfRangeException(nameof(oldIndex));
  6.             if (newIndex < 0 || newIndex >= array.Length)
  7.                 throw new ArgumentOutOfRangeException(nameof(newIndex));
  8.             if (oldIndex < newIndex)
  9.             {
  10.                 for (int i = 0; i < oldIndex; i++)
  11.                 {
  12.                     yield return array[i];
  13.                 }
  14.                 for (int i = oldIndex; i < newIndex; i++)
  15.                 {
  16.                     yield return array[i + 1];
  17.                 }
  18.                 yield return array[oldIndex];
  19.                 for (int i = newIndex + 1; i < array.Length; i++)
  20.                 {
  21.                     yield return array[i];
  22.                 }
  23.             }
  24.             else
  25.             {
  26.                 for (int i = 0; i < newIndex; i++)
  27.                 {
  28.                     yield return array[i];
  29.                 }
  30.                 yield return array[oldIndex];
  31.                 for (int i = newIndex; i < oldIndex; i++)
  32.                 {
  33.                     yield return array[i];
  34.                 }
  35.                 for (int i = oldIndex + 1; i < array.Length; i++)
  36.                 {
  37.                     yield return array[i];
  38.                 }
  39.             }
  40.         }
« Last Edit: May 29, 2017, 05:23:06 PM by gile »
Speaking English as a French Frog

kdub_nz

  • Mesozoic keyThumper
  • SuperMod
  • Water Moccasin
  • Posts: 2138
  • class keyThumper<T>:ILazy<T>
Re: List<T> MoveItem
« Reply #4 on: May 29, 2017, 06:50:41 PM »

Norman, Gilles,

Thank you gentlemen.

I was losing sight of user expectations ... concentrating on the mechanics of the index shuffling :)

You are both correct, returning void would be more conventional and practical.

I'll re-visit this tonight.


I appreciate the input.

Regards,
Kerry
Called Kerry in my other life
Retired; but they dragged me back in !

I live at UTC + 13.00

---
some people complain about loading the dishwasher.
Sometimes the question is more important than the answer.

kdub_nz

  • Mesozoic keyThumper
  • SuperMod
  • Water Moccasin
  • Posts: 2138
  • class keyThumper<T>:ILazy<T>
Re: List<T> MoveItem
« Reply #5 on: May 30, 2017, 07:02:38 AM »
Closer to built-in IEnumerable<T> extension methods:

Code - C#: [Select]
  1.         public static IEnumerable<T> MoveItem<T>(this IEnumerable<T> source, int oldIndex, int newIndex)
  2.         {
  3.             //< ,,, >
  4.             }
  5.         }


Gilles,

Do you have the code you tested this with ?

Regards,


Called Kerry in my other life
Retired; but they dragged me back in !

I live at UTC + 13.00

---
some people complain about loading the dishwasher.
Sometimes the question is more important than the answer.

kdub_nz

  • Mesozoic keyThumper
  • SuperMod
  • Water Moccasin
  • Posts: 2138
  • class keyThumper<T>:ILazy<T>
Re: List<T> MoveItem
« Reply #6 on: May 30, 2017, 08:18:05 AM »
Revised for void return, consistent with extension methods generally:

Code - C#: [Select]
  1. public static class ListExtensions
  2. {
  3.     public static void MoveItem<T>(this List<T> list, int oldIndex, int newIndex)
  4.     {
  5.         // return unchanged if index's are outside scope
  6.         // TODO raise Exception for these conditions !!!
  7.         if ((oldIndex == newIndex) ||
  8.             (oldIndex < 0) ||
  9.             (oldIndex >= list.Count) ||
  10.             (newIndex < 0) ||
  11.             (newIndex >= list.Count))
  12.             return;
  13.  
  14.         T dataToMove = list[oldIndex];
  15.  
  16.         list.RemoveAt(oldIndex);
  17.         list.Insert(newIndex, dataToMove);
  18.     }
  19. }
  20.  

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4.  
  5. namespace ReOrderList
  6. {
  7.     public static class Program
  8.     {
  9.         static void Main(string[] args)
  10.         {
  11.             var dataList = new List<string>() {"Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot"};
  12.  
  13.             Console.WriteLine("dataList List Data");
  14.             foreach (var data in dataList) Console.WriteLine(data);
  15.            
  16.             dataList.MoveItem(0, 4);
  17.             Console.WriteLine("\nRevised List Data: dataList.MoveItem(0, 4)");
  18.             foreach (var data in dataList) Console.WriteLine(data);
  19.            
  20.  
  21.             dataList.MoveItem(0, 5);
  22.             Console.WriteLine("\nRevised List Data: dataList.MoveItem(0, 5)");
  23.             foreach (var data in dataList) Console.WriteLine(data);
  24.            
  25.  
  26.             dataList.MoveItem(5, 1);
  27.             Console.WriteLine("\nRevised List Data: dataList.MoveItem(5, 1)");
  28.             foreach (var data in dataList) Console.WriteLine(data);
  29.            
  30.  
  31.             dataList.MoveItem(5, 0);
  32.             Console.WriteLine("\nRevised List Data: dataList.MoveItem(5, 0)");
  33.             foreach (var data in dataList) Console.WriteLine(data);
  34.  
  35.             //dataList.MoveItem(1, 1); // TODO raise Exception for these conditions !!!
  36.             //Console.WriteLine("\nRevised List Data: dataList.MoveItem(1, 1)");
  37.             //foreach (var data in dataList) Console.WriteLine(data);
  38.  
  39.             //dataList.MoveItem(0, -1); // TODO raise Exception for these conditions !!!
  40.             //Console.WriteLine("\nRevised List Data: dataList.MoveItem(0, -1)");
  41.             //foreach (var data in dataList) Console.WriteLine(data);
  42.  
  43.  
  44.             //dataList.MoveItem(0, 6);  // TODO raise Exception for these conditions !!!
  45.             //Console.WriteLine("\nRevised List Data: dataList.MoveItem(0, 6)");
  46.             //foreach (var data in dataList) Console.WriteLine(data);
  47.  
  48.             Console.ReadLine();
  49.         }
  50.     }
  51. }
  52.  


dataList List Data
Alpha
Bravo
Charlie
Delta
Echo
Foxtrot

Revised List Data: dataList.MoveItem(0, 4)
Bravo
Charlie
Delta
Echo
Alpha
Foxtrot

Revised List Data: dataList.MoveItem(0, 5)
Charlie
Delta
Echo
Alpha
Foxtrot
Bravo

Revised List Data: dataList.MoveItem(5, 1)
Charlie
Bravo
Delta
Echo
Alpha
Foxtrot

Revised List Data: dataList.MoveItem(5, 0)
Foxtrot
Charlie
Bravo
Delta
Echo
Alpha



« Last Edit: May 30, 2017, 08:21:47 AM by kdub »
Called Kerry in my other life
Retired; but they dragged me back in !

I live at UTC + 13.00

---
some people complain about loading the dishwasher.
Sometimes the question is more important than the answer.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: List<T> MoveItem
« Reply #7 on: May 30, 2017, 09:01:09 AM »
Closer to built-in IEnumerable<T> extension methods:

Code - C#: [Select]
  1.         public static IEnumerable<T> MoveItem<T>(this IEnumerable<T> source, int oldIndex, int newIndex)
  2.         {
  3.             //< ,,, >
  4.             }
  5.         }



Gilles,

Do you have the code you tested this with ?

Regards,

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using static System.Console;
  5.  
  6. namespace MoveItemSample
  7. {
  8.     class Program
  9.     {
  10.         static void Main(string[] args)
  11.         {
  12.             //var data = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  13.             var data = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  14.             int fromIndex = GetIndex("Enter the item current index:");
  15.             int toIndex = GetIndex("Enter the item destination index:");
  16.             try
  17.             {
  18.                 WriteLine($"data.MoveItem({fromIndex}, {toIndex})");
  19.                 foreach (int i in data.MoveItem(fromIndex, toIndex))
  20.                 {
  21.                     WriteLine(i);
  22.                 }
  23.             }
  24.             catch (Exception ex)
  25.             {
  26.                 WriteLine(ex.Message);
  27.             }
  28.         }
  29.  
  30.         private static int GetIndex(string message)
  31.         {
  32.             int index;
  33.             while (true)
  34.             {
  35.                 WriteLine(message);
  36.                 if (int.TryParse(ReadLine(), out index))
  37.                     return index;
  38.                 WriteLine("The value must be an integer.");
  39.             }
  40.         }
  41.     }
  42. }

To test the same way you did, we have to explicitly change datalist value using IEnumerable.ToList():

Code - C#: [Select]
  1.         static void Main()
  2.         {
  3.             var dataList = new List<string>() { "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot" };
  4.  
  5.             WriteLine("dataList List Data");
  6.             foreach (var data in dataList) WriteLine(data);
  7.  
  8.             dataList = dataList.MoveItem(0, 4).ToList();
  9.             WriteLine("\nRevised List Data: dataList.MoveItem(0, 4)");
  10.             foreach (var data in dataList) WriteLine(data);
  11.  
  12.  
  13.             dataList = dataList.MoveItem(0, 5).ToList();
  14.             WriteLine("\nRevised List Data: dataList.MoveItem(0, 5)");
  15.             foreach (var data in dataList) WriteLine(data);
  16.  
  17.  
  18.             dataList = dataList.MoveItem(5, 1).ToList();
  19.             WriteLine("\nRevised List Data: dataList.MoveItem(5, 1)");
  20.             foreach (var data in dataList) WriteLine(data);
  21.  
  22.  
  23.             dataList = dataList.MoveItem(5, 0).ToList();
  24.             WriteLine("\nRevised List Data: dataList.MoveItem(5, 0)");
  25.             foreach (var data in dataList) WriteLine(data);
  26.         }
Speaking English as a French Frog