Comment remplacer un élément de liste de la meilleure façon


97
if (listofelements.Contains(valueFieldValue.ToString()))
{
    listofelements[listofelements.IndexOf(valueFieldValue.ToString())] = value.ToString();
}

J'ai remplacé comme ci-dessus. Existe-t-il une autre meilleure façon de comparer que celle-ci?

Réponses:


109

Utilisez Lambda pour trouver l'index dans la liste et utilisez cet index pour remplacer l'élément de liste.

List<string> listOfStrings = new List<string> {"abc", "123", "ghi"};
listOfStrings[listOfStrings.FindIndex(ind=>ind.Equals("123"))] =  "def";

15
Vérifiez -1! Incase l'article n'est pas présent dans les collections
Surender Singh Malik

3
plus un pour l'utilisation de FindIndex
Aaron Barker

2
C'est la meilleure réponse universelle à mon humble avis, car elle peut également être utilisée pour comparer des objets.
Simcha Khabinsky

Voir l'amélioration de Fej qui vérifie -1 . Bien que pour un Equalstest simple , le bon vieux IndexOffonctionne aussi bien, et est plus concis - comme dans la réponse de Tim .
ToolmakerSteve

109

Vous pourriez le rendre plus lisible et plus efficace:

string oldValue = valueFieldValue.ToString();
string newValue = value.ToString();
int index = listofelements.IndexOf(oldValue);
if(index != -1)
    listofelements[index] = newValue;

Cela ne demande qu'une seule fois l'index. Votre approche utilise d' Containsabord ce qui doit boucler tous les éléments (dans le pire des cas), puis vous utilisez IndexOflequel doit à nouveau énumérer les éléments.


2
C'est la bonne réponse pour trouver des littéraux - entiers, chaînes, mais pas très bonne pour trouver des objets. Cependant, j'aime mieux la réponse de rokkuchan car elle est universelle.
Simcha Khabinsky

1
@SimchaKhabinsky: fonctionne aussi avec les types de référence, le type doit juste être remplacé Equalsou vous ne trouverez l'objet que s'il s'agit de la même référence. Notez qu'il strings'agit également d'un objet (type de référence).
Tim Schmelter

Oui, tu as raison. Cependant, j'ai vu de nombreux développeurs ne pas se souvenir de l'implémentation Equals et vous devez également vous rappeler que parfois en même temps vous devez implémenterGetHashCode
Simcha Khabinsky

1
@SimchaKhabinsky: oui, vous devriez toujours remplacer GetHashCodesi vous remplacez Equalsmais GetHashCoden'est utilisé que si l'objet est stocké dans un ensemble (fe Dictionaryou HashSet), donc il n'est pas utilisé avec IndexOfou Contains, uniquement Equals.
Tim Schmelter

Tim, j'ai une question à propos de ce vs rokkuchan. Je lis dans des documents qui IndexOfutilise EqualityComparer<T>.Default. Êtes-vous en train de dire que cela demandera éventuellement item.Equals(target)chaque élément de la liste, et qu'il aura donc exactement le même comportement que la réponse de rokkuchan?
ToolmakerSteve

16

Vous accédez à votre liste deux fois pour remplacer un élément. Je pense forqu'une simple boucle devrait suffire:

var key = valueFieldValue.ToString();
for (int i = 0; i < listofelements.Count; i++)
{
    if (listofelements[i] == key)
    {
        listofelements[i] = value.ToString();
        break;
    }
}

1
@gzaxx. "Vous accédez à votre liste deux fois pour remplacer un élément. Je pense qu'une simple boucle for devrait suffire". Et combien de fois accédez-vous à la liste de votre compagnon de boucle for?
Pap

5
@Pap désolé, je n'étais pas assez clair. Il itère sa liste deux fois (d'abord pour vérifier si l'élément est dans la liste, ensuite pour obtenir l'index de l'élément).
gzaxx

14

Pourquoi ne pas utiliser les méthodes d'extension?

Considérez le code suivant:

        var intArray = new int[] { 0, 1, 1, 2, 3, 4 };
        // Replaces the first occurance and returns the index
        var index = intArray.Replace(1, 0);
        // {0, 0, 1, 2, 3, 4}; index=1

        var stringList = new List<string> { "a", "a", "c", "d"};
        stringList.ReplaceAll("a", "b");
        // {"b", "b", "c", "d"};

        var intEnum = intArray.Select(x => x);
        intEnum = intEnum.Replace(0, 1);
        // {0, 0, 1, 2, 3, 4} => {1, 1, 1, 2, 3, 4}
  • Pas de duplication de code
  • Il n'est pas nécessaire de taper des expressions linq longues
  • Il n'y a pas besoin d'utilisations supplémentaires

Le code source:

namespace System.Collections.Generic
{
    public static class Extensions
    {
        public static int Replace<T>(this IList<T> source, T oldValue, T newValue)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            var index = source.IndexOf(oldValue);
            if (index != -1)
                source[index] = newValue;
            return index;
        }

        public static void ReplaceAll<T>(this IList<T> source, T oldValue, T newValue)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            int index = -1;
            do
            {
                index = source.IndexOf(oldValue);
                if (index != -1)
                    source[index] = newValue;
            } while (index != -1);
        }


        public static IEnumerable<T> Replace<T>(this IEnumerable<T> source, T oldValue, T newValue)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            return source.Select(x => EqualityComparer<T>.Default.Equals(x, oldValue) ? newValue : x);
        }
    }
}

Les deux premières méthodes ont été ajoutées pour modifier les objets des types de référence en place. Bien sûr, vous ne pouvez utiliser que la troisième méthode pour tous les types.

PS Grâce à l'observation de Mike , j'ai ajouté la méthode ReplaceAll.


1
Re "changer les objets des types de référence en place" - que ce Tsoit un type de référence ou non n'est pas pertinent. Ce qui compte, c'est de savoir si vous voulez muter (modifier) ​​la liste ou renvoyer une nouvelle liste. La troisième méthode de cours ne modifiera pas la liste initiale, de sorte que vous ne pouvez pas utiliser juste la troisième méthode ... . La première méthode est celle qui répond à la question spécifique posée. Excellent code - juste corriger votre description de ce que font les méthodes :)
ToolmakerSteve

7

Suite à la réponse de rokkuchan, juste une petite mise à jour:

List<string> listOfStrings = new List<string> {"abc", "123", "ghi"};

int index = listOfStrings.FindIndex(ind => ind.Equals("123"));
if (index > -1)
    listOfStrings[index] =  "def";

5

Utilisez FindIndexet lambda pour rechercher et remplacer vos valeurs:

int j = listofelements.FindIndex(i => i.Contains(valueFieldValue.ToString())); //Finds the item index

lstString[j] = lstString[j].Replace(valueFieldValue.ToString(), value.ToString()); //Replaces the item by new value

3

Vous pouvez utiliser les extensions suivantes basées sur une condition de prédicat:

    /// <summary>
    /// Find an index of a first element that satisfies <paramref name="match"/>
    /// </summary>
    /// <typeparam name="T">Type of elements in the source collection</typeparam>
    /// <param name="this">This</param>
    /// <param name="match">Match predicate</param>
    /// <returns>Zero based index of an element. -1 if there is not such matches</returns>
    public static int IndexOf<T>(this IList<T> @this, Predicate<T> match)
    {
        @this.ThrowIfArgumentIsNull();
        match.ThrowIfArgumentIsNull();

        for (int i = 0; i < @this.Count; ++i)
            if (match(@this[i]))
                return i;

        return -1;
    }

    /// <summary>
    /// Replace the first occurance of an oldValue which satisfies the <paramref name="removeByCondition"/> by a newValue
    /// </summary>
    /// <typeparam name="T">Type of elements of a target list</typeparam>
    /// <param name="this">Source collection</param>
    /// <param name="removeByCondition">A condition which decides is a value should be replaced or not</param>
    /// <param name="newValue">A new value instead of replaced</param>
    /// <returns>This</returns>
    public static IList<T> Replace<T>(this IList<T> @this, Predicate<T> replaceByCondition, T newValue)
    {
        @this.ThrowIfArgumentIsNull();
        removeByCondition.ThrowIfArgumentIsNull();

        int index = @this.IndexOf(replaceByCondition);
        if (index != -1)
            @this[index] = newValue;

        return @this;
    }

    /// <summary>
    /// Replace all occurance of values which satisfy the <paramref name="removeByCondition"/> by a newValue
    /// </summary>
    /// <typeparam name="T">Type of elements of a target list</typeparam>
    /// <param name="this">Source collection</param>
    /// <param name="removeByCondition">A condition which decides is a value should be replaced or not</param>
    /// <param name="newValue">A new value instead of replaced</param>
    /// <returns>This</returns>
    public static IList<T> ReplaceAll<T>(this IList<T> @this, Predicate<T> replaceByCondition, T newValue)
    {
        @this.ThrowIfArgumentIsNull();
        removeByCondition.ThrowIfArgumentIsNull();

        for (int i = 0; i < @this.Count; ++i)
            if (replaceByCondition(@this[i]))
                @this[i] = newValue;

        return @this;
    }

Remarques: - Au lieu de l'extension ThrowIfArgumentIsNull, vous pouvez utiliser une approche générale comme:

if (argName == null) throw new ArgumentNullException(nameof(argName));

Ainsi, votre cas avec ces extensions peut être résolu comme suit:

string targetString = valueFieldValue.ToString();
listofelements.Replace(x => x.Equals(targetString), value.ToString());

1

Je ne sais pas si c'est mieux ou pas mais vous pouvez aussi l'utiliser

List<string> data = new List<string>
(new string[]   { "Computer", "A", "B", "Computer", "B", "A" });
int[] indexes = Enumerable.Range(0, data.Count).Where
                 (i => data[i] == "Computer").ToArray();
Array.ForEach(indexes, i => data[i] = "Calculator");

1

Ou, en s'appuyant sur la suggestion de Rusian L., si l'élément que vous recherchez peut être dans la liste plus d'une fois:

[Extension()]
public void ReplaceAll<T>(List<T> input, T search, T replace)
{
    int i = 0;
    do {
        i = input.FindIndex(i, s => EqualityComparer<T>.Default.Equals(s, search));

        if (i > -1) {
            FileSystem.input(i) = replace;
            continue;
        }

        break;  
    } while (true);
}

1

Vous pouvez utiliser une expression lambda comme celle-ci.

int index = listOfElements.FindIndex(item => item.Id == id);  
if (index != -1) 
{
    listOfElements[index] = newValue;
}

0

je trouve le meilleur pour le faire rapidement et simplement

  1. trouver votre article dans la liste

    var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
  2. faire cloner à partir du courant

    OrderDetail dd = d;
  3. Mettre à jour votre clone

    dd.Quantity++;
  4. trouver l'index dans la liste

    int idx = Details.IndexOf(d);
  5. supprimer l'élément trouvé dans (1)

      Details.Remove(d);
  6. insérer

     if (idx > -1)
          Details.Insert(idx, dd);
      else
          Details.Insert(Details.Count, dd);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.