Liste öğesi en iyi şekilde nasıl değiştirilir


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

Yukarıdaki gibi değiştirdim. Karşılaştırmanın bundan başka en iyi yolu var mı?

Yanıtlar:


119

Listedeki dizini bulmak için Lambda'yı kullanın ve liste öğesini değiştirmek için bu dizini kullanın.

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

16
-1'i kontrol edin! Ürünün koleksiyonlarda olmaması durumunda
Surender Singh Malik

3
artı bir FindIndex
Aaron Barker

2
Bu, nesneleri karşılaştırmak için de kullanılabileceğinden, en iyi evrensel cevap IMHO'dur.
Simcha Khabinsky

Fej'in -1'i kontrol eden geliştirmesine bakın . Basit bir Equalstest için olsa da , iyi eski de IndexOfişe yarar ve daha özlüdür - Tim'in cevabında olduğu gibi .
ToolmakerSteve

110

Daha okunaklı ve daha verimli hale getirebilirsiniz:

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

Bu yalnızca bir kez dizini sorar. Yaklaşımınız Containsönce tüm öğeleri döngüye sokması gereken (en kötü durumda) kullanır, ardından IndexOföğeleri tekrar numaralandırmanız gerekenleri kullanıyorsunuz .


2
Bu, değişmez değerleri bulmak için doğru cevaptır - ints, string, ancak nesneleri bulmak için pek iyi değil. Bununla birlikte, evrensel olduğu için rokkuchan'ın cevabını daha çok seviyorum.
Simcha Khabinsky

1
@SimchaKhabinsky: referans türleriyle de çalışır, türün geçersiz kılınması gerekir Equalsveya nesneyi yalnızca aynı referanssa bulursunuz. Bunun stringaynı zamanda bir nesne (başvuru türü) olduğunu unutmayın.
Tim Schmelter

Evet haklısın. Ancak, uygulamayı unutmayan birçok geliştirici gördüm Equals ve bazen aynı zamanda uygulamak zorunda olduğunuzu da hatırlamalısınızGetHashCode
Simcha Khabinsky

1
@SimchaKhabinsky: evet, her zaman geçersiz kılar GetHashCodegeçersiz kılmak eğer Equalsancak GetHashCodenesne kümesi (fe saklanır yalnızca kullanılır Dictionaryveya HashSet) onunla alışkın değil bu yüzden, IndexOfya Containssadece Equals.
Tim Schmelter

Tim, bu ve rokkuchan hakkında bir sorum var. IndexOfKullanan belgelerde okudum EqualityComparer<T>.Default. Sonunda item.Equals(target)listedeki her bir maddeyi arayacağını mı söylüyorsunuz ve bu nedenle rokkuchan'ın cevabı ile aynı davranışa mı sahip?
ToolmakerSteve

16

Listenize bir öğeyi değiştirmek için iki kez erişiyorsunuz. Bence basit fordöngü yeterli olmalı:

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

1
@gzaxx. "Listenize bir öğeyi değiştirmek için iki kez erişiyorsunuz. Basit bir for döngüsü yeterli olmalı". Ve for döngüsü arkadaşınızdaki listeye kaç kez erişiyorsunuz?
Pap

5
@Pap üzgünüm, yeterince açık değildim. Listesini iki kez yineliyor (önce öğenin listede olup olmadığını kontrol etmek için, ikincisi öğe dizinini almak için).
gzaxx

14

Neden uzantı yöntemlerini kullanmıyorsunuz?

Aşağıdaki kodu göz önünde bulundurun:

        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}
  • Kod kopyası yok
  • Uzun linq ifadeleri yazmaya gerek yoktur
  • Ek kullanımlara gerek yoktur

Kaynak kodu:

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(nameof(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(nameof(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(nameof(source));

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

Referans türlerinin nesnelerini yerinde değiştirmek için ilk iki yöntem eklenmiştir. Tabii ki, tüm türler için sadece üçüncü yöntemi kullanabilirsiniz.

Not: Mike'ın gözlemi sayesinde ReplaceAll yöntemini ekledim.


1
Yeniden "referans türlerinin nesnelerini yerinde değiştirin" - Tbir referans türü olup olmadığı önemli değildir. Önemli olan, listeyi mutasyona uğratmak (değiştirmek) veya yeni bir liste döndürmek isteyip istemediğinizdir. Bunu elbette Üçüncü yöntem, orijinal listeyi değiştirmez olamaz sadece üçüncü yöntemi kullanın ... . İlk yöntem, sorulan belirli soruyu yanıtlayan yöntemdir. Mükemmel kod - sadece yöntemlerin ne işe yaradığına dair açıklamanızı düzeltir :)
ToolmakerSteve

8

Rokkuchan'ın cevabının ardından, sadece küçük bir güncelleme:

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

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

5

FindIndexDeğerlerinizi bulmak ve değiştirmek için ve lambda kullanın :

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

Bir dayanak koşulu temel alan sonraki uzantıları kullanabilirsiniz:

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

Notlar: - ThrowIfArgumentIsNull uzantısı yerine, aşağıdaki gibi genel bir yaklaşım kullanabilirsiniz:

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

Yani bu uzantılarla ilgili durumunuz şu şekilde çözülebilir:

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

1

En iyisi mi değil mi bilmiyorum ama sen de kullanabilirsin

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

Veya, aradığınız öğe listede birden çok kez yer alabiliyorsa, Rusian L.'nin önerisine dayanarak:

[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

Lambda ifadesini bu şekilde kullanabilirsiniz.

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

0

hızlı ve basit yapmak için en iyisini buluyorum

  1. öğeyi listede bul

    var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
    
  2. akımdan klon yap

    OrderDetail dd = d;
    
  3. Klonunuzu güncelleyin

    dd.Quantity++;
    
  4. listede dizin bul

    int idx = Details.IndexOf(d);
    
  5. (1) içindeki bulunan öğeyi kaldır

      Details.Remove(d);
    
  6. eklemek

     if (idx > -1)
          Details.Insert(idx, dd);
      else
          Details.Insert(Details.Count, dd);
    
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.