Bagaimana cara mengganti item daftar dengan cara terbaik


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

Saya telah mengganti seperti di atas. Apakah ada cara terbaik lain untuk membandingkan selain yang ini?

Jawaban:


109

Gunakan Lambda untuk menemukan indeks dalam Daftar dan gunakan indeks ini untuk mengganti item daftar.

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

15
Periksa -1! Jika item tersebut tidak ada dalam koleksi
Surender Singh Malik

3
ditambah satu untuk penggunaan FindIndex
Aaron Barker

2
Ini adalah jawaban universal IMHO terbaik karena ini dapat digunakan untuk membandingkan objek juga.
Simcha Khabinsky

Lihat peningkatan Fej yang memeriksa -1 . Meskipun untuk Equalstes sederhana , lama yang bagus IndexOfjuga berhasil, dan lebih ringkas - seperti dalam jawaban Tim .
ToolmakerSteve

109

Anda bisa membuatnya lebih mudah dibaca dan lebih efisien:

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

Ini hanya meminta indeks sekali. Pendekatan Anda menggunakan Containspertama yang perlu mengulang semua item (dalam kasus terburuk), lalu Anda menggunakan IndexOfyang perlu menghitung item lagi.


2
Ini adalah jawaban yang benar untuk menemukan literal - int, string, tetapi tidak terlalu baik untuk menemukan objek. Namun, saya lebih suka jawaban rokkuchan karena itu universal.
Simcha Khabinsky

1
@SimchaKhabinsky: berfungsi juga dengan tipe referensi, tipe tersebut hanya perlu diganti Equalsatau Anda hanya akan menemukan objek jika referensi yang sama. Perhatikan bahwa stringitu juga merupakan objek (tipe referensi).
Tim Schmelter

Ya kamu benar. Namun, saya telah melihat banyak pengembang gagal mengingat untuk mengimplementasikan Equals dan Anda juga harus ingat bahwa terkadang pada saat yang sama Anda harus mengimplementasikanGetHashCode
Simcha Khabinsky

1
@SimchaKhabinsky: ya, Anda harus selalu menimpa GetHashCodejika Anda menimpa Equalstetapi GetHashCodehanya digunakan jika objek disimpan dalam satu set (fe Dictionaryatau HashSet), jadi tidak digunakan dengan IndexOfatau Contains, hanya Equals.
Tim Schmelter

Tim, saya punya pertanyaan tentang vs rokkuchan ini. Saya membaca di dokumen yang IndexOfmenggunakan EqualityComparer<T>.Default. Apakah Anda mengatakan bahwa pada akhirnya akan memanggil item.Equals(target)setiap item dalam daftar, dan karena itu memiliki perilaku yang sama persis dengan jawaban rokkuchan?
ToolmakerSteve

16

Anda mengakses daftar Anda dua kali untuk mengganti satu elemen. Saya pikir forloop sederhana seharusnya cukup:

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

1
@tokopedia "Anda mengakses daftar Anda dua kali untuk mengganti satu elemen. Saya pikir loop for sederhana sudah cukup". Dan berapa kali Anda mengakses daftar di loop mate Anda?
Pap

5
@Pap maaf, saya kurang jelas. Dia mengulang daftarnya dua kali (pertama untuk memeriksa apakah item ada di dalam daftar, kedua untuk mendapatkan indeks item).
gzaxx

14

Mengapa tidak menggunakan metode ekstensi?

Perhatikan kode berikut:

        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}
  • Tidak ada duplikasi kode
  • Tidak perlu mengetik ekspresi linq yang panjang
  • Tidak perlu penggunaan tambahan

Kode sumber:

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

Dua metode pertama telah ditambahkan untuk mengubah objek tipe referensi pada tempatnya. Tentu saja, Anda hanya dapat menggunakan metode ketiga untuk semua jenis.

PS Terima kasih untuk pengamatan mike , saya telah menambahkan metode ReplaceAll.


1
Kembali "mengubah objek jenis referensi di tempat" - apakah Tjenis referensi itu tidak relevan atau tidak. Yang penting adalah apakah Anda ingin mengubah (mengubah) daftar, atau mengembalikan daftar baru. Metode ketiga tentu saja tidak akan mengubah daftar asli, jadi Anda tidak dapat menggunakan hanya metode ketiga… . Metode pertama adalah metode yang menjawab pertanyaan spesifik yang diajukan. Kode yang sangat baik - hanya mengoreksi deskripsi Anda tentang apa yang dilakukan metode :)
ToolmakerSteve

7

Berikut jawaban rokkuchan, tinggal sedikit peningkatan:

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

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

5

Gunakan FindIndexdan lambda untuk menemukan dan mengganti nilai Anda:

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

Anda dapat menggunakan ekstensi berikutnya yang didasarkan pada kondisi predikat:

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

Catatan: - Daripada ekstensi ThrowIfArgumentIsNull, Anda dapat menggunakan pendekatan umum seperti:

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

Jadi kasus Anda dengan ekstensi ini dapat diselesaikan sebagai:

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

1

Saya tidak jika itu yang terbaik atau tidak, tetapi Anda juga dapat menggunakannya

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

Atau, berdasarkan saran Rusian L., jika item yang Anda cari bisa ada di daftar lebih dari sekali ::

[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

Anda dapat menggunakan ekspresi lambda seperti ini.

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

0

Saya menemukan yang terbaik untuk melakukannya dengan cepat dan sederhana

  1. temukan item Anda dalam daftar

    var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
  2. buat klon dari arus

    OrderDetail dd = d;
  3. Perbarui klon Anda

    dd.Quantity++;
  4. temukan indeks dalam daftar

    int idx = Details.IndexOf(d);
  5. hapus item yang didirikan di (1)

      Details.Remove(d);
  6. memasukkan

     if (idx > -1)
          Details.Insert(idx, dd);
      else
          Details.Insert(Details.Count, dd);
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.