通用列表 – 移动列表中的项目

所以我有一个通用的列表,一个oldIndex和一个newIndex值。

我想尽可能简单地将oldIndex的项目移动到newIndex

有什么build议么?

注意

该项目应该在(newIndex - 1)newIndex之间的项目之间被删除。

我知道你说的是“通用列表”,但是你没有指定你需要使用List(T)类,所以这是一个不同的东西。

ObservableCollection(T)类有一个Move方法 ,它正是你想要的。

 public void Move(int oldIndex, int newIndex) 

它下面基本上是这样实现的。

 T item = base[oldIndex]; base.RemoveItem(oldIndex); base.InsertItem(newIndex, item); 

所以你可以看到其他人提出的交换方法本质上就是ObservableCollection在它自己的Move方法中所做的。

UPDATE 2015-12-30:你可以看到corefx中的Move和MoveItem方法的源代码,因为.NET是开源的,所以不需要使用Reflector / ILSpy。

 var item = list[oldIndex]; list.RemoveAt(oldIndex); if (newIndex > oldIndex) newIndex--; // the actual index could have shifted due to the removal list.Insert(newIndex, item); 

我知道这个问题是古老的,但我调整了javascript代码的这个响应到C#。 希望能帮助到你

  public static void Move<T>(this List<T> list, int oldIndex, int newIndex) { // exit if possitions are equal or outside array if ((oldIndex == newIndex) || (0 > oldIndex) || (oldIndex >= list.Count) || (0 > newIndex) || (newIndex >= list.Count)) return; // local variables var i = 0; T tmp = list[oldIndex]; // move element down and shift other elements up if (oldIndex < newIndex) { for (i = oldIndex; i < newIndex; i++) { list[i] = list[i + 1]; } } // move element up and shift other elements down else { for (i = oldIndex; i > newIndex; i--) { list[i] = list[i - 1]; } } // put element from position 1 to destination list[newIndex] = tmp; } 

List <T> .Remove()和List <T> .RemoveAt()不返回被删除的项目。

所以你必须使用这个:

 var item = list[oldIndex]; list.RemoveAt(oldIndex); list.Insert(newIndex, item); 

我创build了一个在列表中移动项目的扩展方法。

如果我们移动现有项目,则索引不应该移位,因为我们正在将项目移动到列表中的现有索引位置。

@Oliver在下面引用的边缘情况(将项目移动到列表的末尾)实际上会导致testing失败,但是这是devise的。 要在列表的末尾插入项目,我们只需调用List<T>.Addlist.Move(predicate, list.Count) 应该失败,因为这个索引位置在移动之前不存在。

无论如何,我已经创build了两个额外的扩展方法, MoveToEndMoveToBeginning ,它的来源可以在这里find。

 /// <summary> /// Extension methods for <see cref="System.Collections.Generic.List{T}"/> /// </summary> public static class ListExtensions { /// <summary> /// Moves the item matching the <paramref name="itemSelector"/> to the <paramref name="newIndex"/> in a list. /// </summary> public static void Move<T>(this List<T> list, Predicate<T> itemSelector, int newIndex) { Ensure.Argument.NotNull(list, "list"); Ensure.Argument.NotNull(itemSelector, "itemSelector"); Ensure.Argument.Is(newIndex >= 0, "New index must be greater than or equal to zero."); var currentIndex = list.FindIndex(itemSelector); Ensure.That<ArgumentException>(currentIndex >= 0, "No item was found that matches the specified selector."); // Copy the current item var item = list[currentIndex]; // Remove the item list.RemoveAt(currentIndex); // Finally add the item at the new index list.Insert(newIndex, item); } } [Subject(typeof(ListExtensions), "Move")] public class List_Move { static List<int> list; public class When_no_matching_item_is_found { static Exception exception; Establish ctx = () => { list = new List<int>(); }; Because of = () => exception = Catch.Exception(() => list.Move(x => x == 10, 10)); It Should_throw_an_exception = () => exception.ShouldBeOfType<ArgumentException>(); } public class When_new_index_is_higher { Establish ctx = () => { list = new List<int> { 1, 2, 3, 4, 5 }; }; Because of = () => list.Move(x => x == 3, 4); // move 3 to end of list (index 4) It Should_be_moved_to_the_specified_index = () => { list[0].ShouldEqual(1); list[1].ShouldEqual(2); list[2].ShouldEqual(4); list[3].ShouldEqual(5); list[4].ShouldEqual(3); }; } public class When_new_index_is_lower { Establish ctx = () => { list = new List<int> { 1, 2, 3, 4, 5 }; }; Because of = () => list.Move(x => x == 4, 0); // move 4 to beginning of list (index 0) It Should_be_moved_to_the_specified_index = () => { list[0].ShouldEqual(4); list[1].ShouldEqual(1); list[2].ShouldEqual(2); list[3].ShouldEqual(3); list[4].ShouldEqual(5); }; } } 

像这样的东西:

 List<object> l; l.Insert(newIndex, l[oldIndex]); if(newIndex <= oldIndex) ++oldIndex; l.RemoveAt(oldIndex); 

编辑:考虑到索引转移,希望其正确。 (tks的意见)

我会期待:

 // Makes sure item is at newIndex after the operation T item = list[oldIndex]; list.RemoveAt(oldIndex); list.Insert(newIndex, item); 

… 要么:

 // Makes sure relative ordering of newIndex is preserved after the operation, // meaning that the item may actually be inserted at newIndex - 1 T item = list[oldIndex]; list.RemoveAt(oldIndex); newIndex = (newIndex > oldIndex ? newIndex - 1, newIndex) list.Insert(newIndex, item); 

…会做的伎俩,但我没有VS在这台机器上检查。

最简单的方法:

 list[newIndex] = list[oldIndex]; list.RemoveAt(oldIndex); 

编辑

问题不是很清楚…因为我们不在乎list[newIndex]项目的list[newIndex]在哪里,我认为最简单的方法如下(有或没有扩展方法):

  public static void Move<T>(this List<T> list, int oldIndex, int newIndex) { T aux = list[newIndex]; list[newIndex] = list[oldIndex]; list[oldIndex] = aux; } 

这个解决scheme是最快的,因为它不涉及列表插入/删除。

这是我实现一个移动元素扩展方法。 它能够很好地处理元素前后的移动。

 public static void MoveElement<T>(this IList<T> list, int fromIndex, int toIndex) { if (!fromIndex.InRange(0, list.Count - 1)) { throw new ArgumentException("From index is invalid"); } if (!toIndex.InRange(0, list.Count - 1)) { throw new ArgumentException("To index is invalid"); } if (fromIndex == toIndex) return; var element = list[fromIndex]; if (fromIndex > toIndex) { list.RemoveAt(fromIndex); list.Insert(toIndex, element); } else { list.Insert(toIndex + 1, element); list.RemoveAt(fromIndex); } } 

更简单的人就是这样做的

  public void MoveUp(object item,List Concepts){ int ind = Concepts.IndexOf(item.ToString()); if (ind != 0) { Concepts.RemoveAt(ind); Concepts.Insert(ind-1,item.ToString()); obtenernombres(); NotifyPropertyChanged("Concepts"); }} 

用MoveDown做同样的事情,但是改变if if(ind!= Concepts.Count())和Concepts.Insert(ind + 1,item.ToString());