web-dev-qa-db-fra.com

Liste générique - déplacer un élément de la liste

J'ai donc une liste générique et une valeur oldIndex et une valeur newIndex.

Je souhaite déplacer l'élément de oldIndex vers newIndex... aussi simplement que possible.

Aucune suggestion?

Remarque

L'élément doit être fini entre les éléments à (newIndex - 1) et newIndexavant il a été supprimé.

137
Richard Everett

Je sais que vous avez dit "liste générique" mais vous n'avez pas précisé que vous deviez utiliser la classe List (T), voici donc un aperçu de quelque chose de différent.

La classe ObservableCollection (T) a une méthode de déplacement qui fait exactement ce que vous voulez.

public void Move(int oldIndex, int newIndex)

En dessous, il est en gros implémenté comme ceci.

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

Comme vous pouvez le constater, la méthode de swap suggérée par d’autres personnes correspond essentiellement à ce que ObservableCollection utilise dans sa propre méthode de déplacement.

UPDATE 2015-12-30: Vous pouvez voir le code source de Move et MoveItem méthodes dans corefx maintenant pour vous-même sans utiliser Reflector/ILSpy puisque .NET est open source.

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

Je sais que cette question est ancienne mais je me suis adapté THIS réponse du code javascript à C #. J'espère que ça aide

 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 () et List <T> .RemoveAt () ne renvoient pas l'élément en cours de suppression.

Par conséquent, vous devez utiliser ceci:

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

Insérez l'élément actuellement à oldIndex pour qu'il soit à newIndex, puis supprimez l'instance d'origine.

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

Vous devez tenir compte du fait que l'index de l'élément que vous souhaitez supprimer peut changer en raison de l'insertion.

5
Megacan

J'ai créé une méthode d'extension pour déplacer des éléments dans une liste.

Un index ne doit pas être déplacé si nous déplaçons un élément existant , car nous déplaçons un élément vers un existant position d'index dans la liste.

Le cas Edge auquel @Oliver fait référence ci-dessous (déplacer un élément à la fin de la liste) provoquerait en réalité l'échec des tests, mais cela est voulu par la conception même. Pour insérer un nouvel élément à la fin de la liste, nous appelons simplement List<T>.Add. list.Move(predicate, list.Count) devrait échouer car cette position d'index n'existe pas avant le déplacement.

Dans tous les cas, j'ai créé deux méthodes d'extension supplémentaires, MoveToEnd et MoveToBeginning, dont on peut trouver la source ici .

/// <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

Je m'attendrais soit:

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

... ou:

// 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);

... ferait l'affaire, mais je n'ai pas de VS sur cette machine à vérifier.

1
Aaron Maenpaa

Manière la plus simple:

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

EDIT

La question n'est pas très claire ... Puisque nous ne nous soucions pas de savoir où va l'élément list[newIndex], Je pense que la façon la plus simple de procéder est la suivante (avec ou sans méthode d'extension):

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

Cette solution est la plus rapide car elle n’implique aucune insertion/suppression de liste.

0
bruno conde