So ersetzen Sie Listenelemente am besten


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

Ich habe wie oben ersetzt. Gibt es einen anderen Best-Place-Vergleich als diesen?

Antworten:


107

Verwenden Sie Lambda, um den Index in der Liste zu finden, und verwenden Sie diesen Index, um das Listenelement zu ersetzen.

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

14
Auf -1 prüfen! Falls der Artikel nicht in Sammlungen vorhanden ist
Surender Singh Malik

3
plus eins für die Verwendung von FindIndex
Aaron Barker

2
Dies ist meiner Meinung nach die beste universelle Antwort, da hiermit auch Objekte verglichen werden können.
Simcha Khabinsky

Siehe Fejs Erweiterung, die nach -1 sucht . Für einen einfachen EqualsTest IndexOffunktioniert gutes altes genauso gut und ist prägnanter - wie in Tims Antwort .
ToolmakerSteve

109

Sie könnten es lesbarer und effizienter machen:

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

Dies fragt nur einmal nach dem Index. Ihr Ansatz verwendet Containszuerst, um alle Elemente zu schleifen (im schlimmsten Fall), und dann, IndexOfum die Elemente erneut aufzulisten.


2
Dies ist die richtige Antwort, um Literale zu finden - Ints, Strings, aber nicht sehr gut, um Objekte zu finden. Ich mag jedoch Rokkuchans Antwort besser, da sie universell ist.
Simcha Khabinsky

1
@ SimchaKhabinsky: Funktioniert auch mit Referenztypen, der Typ muss nur überschrieben werden Equalsoder Sie finden das Objekt nur, wenn es dieselbe Referenz ist. Beachten Sie, dass dies stringauch ein Objekt ist (Referenztyp).
Tim Schmelter

Ja, du hast recht. Ich habe jedoch viele Entwickler gesehen, die sich nicht an die Implementierung erinnern konnten, Equals und Sie müssen sich auch daran erinnern, dass Sie manchmal gleichzeitig implementieren müssenGetHashCode
Simcha Khabinsky

1
@SimchaKhabinsky: ja, sollten Sie immer außer Kraft setzen , GetHashCodewenn Sie außer Kraft setzen , Equalssondern GetHashCodewird nur verwendet , wenn das Objekt in einem Satz (fe gespeichert ist Dictionaryoder HashSet), so ist es nicht mit verwendet IndexOfoder Containsnur Equals.
Tim Schmelter

Tim, ich habe eine Frage zu diesem gegen Rokkuchan. Ich habe in Dokumenten gelesen, die IndexOfverwenden EqualityComparer<T>.Default. Wollen Sie damit sagen, dass irgendwann item.Equals(target)jedes Element in der Liste aufgerufen wird und sich daher genauso verhält wie Rokkuchans Antwort?
ToolmakerSteve

15

Sie greifen zweimal auf Ihre Liste zu, um ein Element zu ersetzen. Ich denke, eine einfache forSchleife sollte ausreichen:

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

1
@gzaxx. "Sie greifen zweimal auf Ihre Liste zu, um ein Element zu ersetzen. Ich denke, eine einfache for-Schleife sollte ausreichen." Und wie oft greifen Sie auf die Liste in Ihrem for-Loop-Partner zu?
Pap

5
@Pap sorry, ich war nicht klar genug. Er wiederholt seine Liste zweimal (erstens, um zu überprüfen, ob sich das Element in der Liste befindet, zweitens, um den Elementindex zu erhalten).
Gzaxx

13

Warum nicht die Erweiterungsmethoden verwenden?

Betrachten Sie den folgenden Code:

        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}
  • Kein Code-Duplikat
  • Es ist nicht erforderlich, lange Linq-Ausdrücke einzugeben
  • Es sind keine zusätzlichen Verwendungen erforderlich

Der Quellcode:

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

Die ersten beiden Methoden wurden hinzugefügt, um die Objekte der Referenztypen zu ändern. Natürlich können Sie für alle Typen nur die dritte Methode verwenden.

PS Dank Mikes Beobachtung habe ich die ReplaceAll-Methode hinzugefügt.


1
Betreff "Objekte der Referenztypen an Ort und Stelle ändern" - ob Tes sich um einen Referenztyp handelt oder nicht, spielt keine Rolle. Entscheidend ist, ob Sie die Liste mutieren (ändern) oder eine neue Liste zurückgeben möchten. Die dritte Methode natürlich wird die ursprüngliche Liste nicht ändern, so dass Sie nicht können nur die dritte Methode verwenden ... . Die erste Methode beantwortet die spezifische Frage. Ausgezeichneter Code - korrigieren Sie einfach Ihre Beschreibung der Methoden :)
ToolmakerSteve

7

Nach Rokkuchans Antwort nur ein kleines Upgrade:

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

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

5

Verwenden Sie FindIndexund Lambda, um Ihre Werte zu finden und zu ersetzen:

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

Sie können die nächsten Erweiterungen verwenden, die auf einer Prädikatbedingung basieren:

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

Anmerkungen: - Anstelle der Erweiterung ThrowIfArgumentIsNull können Sie einen allgemeinen Ansatz wie den folgenden verwenden:

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

Ihr Fall mit diesen Erweiterungen kann also wie folgt gelöst werden:

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

1

Ich weiß nicht, ob es am besten ist oder nicht, aber Sie können es auch verwenden

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

Oder bauen Sie auf den Vorschlag von Rusian L. auf, wenn der gesuchte Artikel mehr als einmal in der Liste enthalten sein kann ::

[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

Sie können einen Lambda-Ausdruck wie diesen verwenden.

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

0

Ich finde es am besten, es schnell und einfach zu machen

  1. Finden Sie Ihren Artikel in der Liste

    var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
  2. Klon aus dem Strom machen

    OrderDetail dd = d;
  3. Aktualisieren Sie Ihren Klon

    dd.Quantity++;
  4. Index in Liste finden

    int idx = Details.IndexOf(d);
  5. Gründungsgegenstand in (1) entfernen

      Details.Remove(d);
  6. einfügen

     if (idx > -1)
          Details.Insert(idx, dd);
      else
          Details.Insert(Details.Count, dd);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.