web-dev-qa-db-ja.com

汎用リスト-リスト内でアイテムを移動する

したがって、汎用リストと、oldIndexおよびnewIndexの値があります。

oldIndexのアイテムをnewIndexにできるだけ簡単に移動したい。

助言がありますか?

注意

アイテムは、(newIndex - 1)newIndexのアイテムの間にあるはずですbefore削除されました。

137
Richard Everett

「ジェネリックリスト」と言ったのは知っていますが、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:Move および MoveItem のソースコードを見ることができます.NETはオープンソースであるため、Reflector/ILSpyを使用せずに自分でcorefxのメソッドを使用できます。

120
jpierson
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);
117
Garry Shutler

私はこの質問が古いことを知っていますが、私は THIS C#へのjavascriptコードの応答に適応しました。それが役に立てば幸い

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

List <T> .Remove()およびList <T> .RemoveAt()は、削除されるアイテムを返しません。

したがって、これを使用する必要があります。

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

現在oldIndexにあるアイテムをnewIndexに挿入してから、元のインスタンスを削除します。

list.Insert(newIndex, list[oldIndex]);
if (newIndex <= oldIndex) ++oldIndex;
list.RemoveAt(oldIndex);

削除するアイテムのインデックスが挿入のために変更される可能性があることを考慮する必要があります。

5
Megacan

リスト内のアイテムを移動するための拡張メソッドを作成しました。

リスト内のexistingインデックス位置にアイテムを移動するため、existingアイテムを移動している場合、インデックスはシフトしません。 。

@Oliverが以下で言及する(ケースをリストの最後に移動する)Edgeの場合、実際にはテストが失敗しますが、これは仕様によるものです。リストの最後にnewアイテムを挿入するには、List<T>.Addを呼び出すだけです。 list.Move(predicate, list.Count)shouldは、移動前にこのインデックス位置が存在しないため失敗します。

いずれにせよ、MoveToEndMoveToBeginningの2つの追加の拡張メソッドを作成しました。そのソースは here です。

/// <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);
        };
    }
}
4
Ben Foster

私は次のいずれかを期待します:

// 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がありません。

1
Aaron Maenpaa

最も簡単な方法:

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

編集

質問はあまり明確ではありません... 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;
    }

このソリューションは、リストの挿入/削除を伴わないため、最速です。

0
bruno conde