Come sostituire nel migliore dei modi l'elemento dell'elenco


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

Ho sostituito come sopra. C'è qualche altro modo migliore per confrontare rispetto a questo?

Risposte:


109

Utilizza Lambda per trovare l'indice nell'elenco e utilizza questo indice per sostituire l'elemento dell'elenco.

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

15
Controlla -1! Nel caso in cui l'oggetto non sia presente nelle collezioni
Surender Singh Malik

3
più uno per l'utilizzo di FindIndex
Aaron Barker

2
Questa è la migliore risposta universale IMHO poiché può essere utilizzata anche per confrontare gli oggetti.
Simcha Khabinsky

Vedi il miglioramento di Fej che controlla -1 . Anche se per un semplice Equalstest, il buon vecchio IndexOffunziona altrettanto bene ed è più conciso, come nella risposta di Tim .
ToolmakerSteve

109

Potresti renderlo più leggibile ed efficiente:

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

Questo richiede solo una volta per l'indice. Il tuo approccio utilizza Containsprima che deve eseguire il ciclo di tutti gli elementi (nel caso peggiore), quindi stai utilizzando IndexOfquale deve enumerare nuovamente gli elementi.


2
Questa è la risposta corretta per trovare i letterali: int, stringhe, ma non molto buona per trovare oggetti. Tuttavia, mi piace di più la risposta di rokkuchan poiché è universale.
Simcha Khabinsky

1
@ SimchaKhabinsky: funziona anche con i tipi di riferimento, il tipo deve solo sovrascrivere Equalso troverai l'oggetto solo se è lo stesso riferimento. Nota che stringè anche un oggetto (tipo di riferimento).
Tim Schmelter

Si hai ragione. Tuttavia, ho visto molti sviluppatori non ricordare di implementare Equals e devi anche ricordare che a volte allo stesso tempo devi implementareGetHashCode
Simcha Khabinsky

1
@SimchaKhabinsky: sì, si dovrebbe sempre prevalere GetHashCodese si ignora Equals, ma GetHashCodeviene utilizzato solo se l'oggetto è memorizzato in un set (fe Dictionaryo HashSet), quindi non è usato con IndexOfo Contains, solo Equals.
Tim Schmelter

Tim, ho una domanda su questo vs Rokkuchan. Ho letto nei documenti che IndexOfutilizza EqualityComparer<T>.Default. Stai dicendo che alla fine chiamerà item.Equals(target)ogni elemento nell'elenco, e quindi ha lo stesso identico comportamento della risposta di rokkuchan?
ToolmakerSteve

16

Stai accedendo al tuo elenco due volte per sostituire un elemento. Penso che un semplice forciclo dovrebbe essere sufficiente:

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

1
@gzaxx. "Stai accedendo alla tua lista due volte per sostituire un elemento. Penso che il semplice ciclo for dovrebbe essere sufficiente". E quante volte accedi all'elenco nel tuo compagno di loop for?
Pap

5
@Pap scusa, non sono stato abbastanza chiaro. Sta iterando la sua lista due volte (la prima per verificare se l'elemento è all'interno dell'elenco, la seconda per ottenere l'indice dell'elemento).
gzaxx

13

Perché non utilizzare i metodi di estensione?

Considera il codice seguente:

        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}
  • Nessun codice duplicato
  • Non è necessario digitare lunghe espressioni linq
  • Non sono necessari ulteriori utilizzi

Il codice sorgente:

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

I primi due metodi sono stati aggiunti per modificare gli oggetti dei tipi di riferimento in posizione. Ovviamente puoi usare solo il terzo metodo per tutti i tipi.

PS Grazie all'osservazione di Mike , ho aggiunto il metodo ReplaceAll.


1
Ri "cambiare gli oggetti dei tipi di riferimento in posizione" - se Tè un tipo di riferimento o meno è irrilevante. Ciò che conta è se si desidera modificare (alterare) l'elenco o restituire un nuovo elenco. Il terzo metodo, naturalmente, non altera la lista originale, in modo da non si può utilizzare solo il terzo metodo ... . Il primo metodo è quello che risponde alla domanda specifica posta. Codice eccellente - correggo solo la descrizione di ciò che fanno i metodi :)
ToolmakerSteve

7

Seguendo la risposta di rokkuchan, solo un piccolo aggiornamento:

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

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

5

Usa FindIndexe lambda per trovare e sostituire i tuoi valori:

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

Puoi utilizzare le prossime estensioni basate su una condizione del predicato:

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

Note: - Invece dell'estensione ThrowIfArgumentIsNull, puoi utilizzare un approccio generale come:

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

Quindi il tuo caso con queste estensioni può essere risolto come:

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

1

Non so se è meglio o no, ma puoi anche usarlo

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

Oppure, basandosi sul suggerimento di Rusian L., se l'elemento che stai cercando può essere nell'elenco più di una volta:

[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

Puoi usare un'espressione lambda come questa.

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

0

trovo il modo migliore per farlo in modo semplice e veloce

  1. trova il tuo articolo nell'elenco

    var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
  2. fare clone da corrente

    OrderDetail dd = d;
  3. Aggiorna il tuo clone

    dd.Quantity++;
  4. trova indice nell'elenco

    int idx = Details.IndexOf(d);
  5. rimuovere l'elemento trovato in (1)

      Details.Remove(d);
  6. inserire

     if (idx > -1)
          Details.Insert(idx, dd);
      else
          Details.Insert(Details.Count, dd);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.