Cách thay thế mục danh sách theo cách tốt nhất


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

Tôi đã thay thế như trên. Có cách nào tốt nhất để so sánh hơn cách này không?

Câu trả lời:


108

Sử dụng Lambda để tìm chỉ mục trong Danh sách và sử dụng chỉ mục này để thay thế mục danh sách.

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

14
Kiểm tra -1! Trong trường hợp mặt hàng không có trong bộ sưu tập
Surender Singh Malik

3
cộng một để sử dụng FindIndex
Aaron Barker

2
Đây là câu trả lời phổ quát nhất IMHO vì nó cũng có thể được sử dụng để so sánh các đối tượng.
Simcha Khabinsky

Xem cải tiến của Fej kiểm tra -1 . Mặc dù đối với một Equalsbài kiểm tra đơn giản , những bài cũ tốt cũng IndexOfhoạt động tốt và ngắn gọn hơn - như trong câu trả lời của Tim .
ToolmakerSteve

109

Bạn có thể làm cho nó dễ đọc hơn và hiệu quả hơn:

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

Điều này chỉ yêu cầu một lần cho chỉ mục. Cách tiếp cận của bạn sử dụng Containsđầu tiên cần lặp lại tất cả các mục (trong trường hợp xấu nhất), sau đó bạn đang sử dụng IndexOfcần liệt kê lại các mục.


2
Đây là câu trả lời chính xác để tìm các chữ - int, chuỗi, nhưng không tốt cho việc tìm các đối tượng. Tuy nhiên, tôi thích câu trả lời của rokkuchan hơn vì nó phổ biến.
Simcha Khabinsky

1
@SimchaKhabinsky: cũng hoạt động với các loại tham chiếu, loại chỉ cần ghi đè Equalshoặc bạn sẽ chỉ tìm thấy đối tượng nếu nó là cùng một tham chiếu. Lưu ý rằng đó stringcũng là một đối tượng (kiểu tham chiếu).
Tim Schmelter

Có bạn đúng. Tuy nhiên, tôi đã thấy nhiều nhà phát triển không phải nhớ để thực hiện Equals và bạn cũng phải nhớ rằng đôi khi cùng một lúc bạn phải thực hiệnGetHashCode
Simcha Khabinsky

1
@SimchaKhabinsky: vâng, bạn phải luôn ghi đè GetHashCodenếu bạn ghi đè Equalsnhưng GetHashCodechỉ được sử dụng nếu đối tượng được lưu trữ trong một tập hợp (fe Dictionaryhoặc HashSet), vì vậy nó không được sử dụng với IndexOfhoặc Containschỉ Equals.
Tim Schmelter

Tim, tôi có một câu hỏi về điều này vs rokkuchan. Tôi đọc trong tài liệu IndexOfsử dụng EqualityComparer<T>.Default. Bạn đang nói rằng điều đó cuối cùng sẽ gọi item.Equals(target)cho từng mục trong danh sách và do đó có hành vi giống hệt như câu trả lời của rokkuchan?
ToolmakerSteve

16

Bạn đang truy cập danh sách của mình hai lần để thay thế một phần tử. Tôi nghĩ rằng forvòng lặp đơn giản là đủ:

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

1
@gzaxx. "Bạn đang truy cập danh sách của mình hai lần để thay thế một phần tử. Tôi nghĩ rằng vòng lặp for đơn giản là đủ". Và bao nhiêu lần bạn truy cập danh sách trong vòng lặp for của bạn?
Pap

5
@Pap xin lỗi, tôi không đủ rõ ràng. Anh ta đang lặp lại danh sách của mình hai lần (lần đầu tiên để kiểm tra xem mục có bên trong danh sách hay không, lần thứ hai để lấy chỉ mục mục).
gzaxx

13

Tại sao không sử dụng các phương pháp mở rộng?

Hãy xem xét đoạn mã sau:

        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}
  • Không có mã trùng lặp
  • Không cần phải nhập các biểu thức linq dài
  • Không cần sử dụng thêm

Mã nguồn:

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

Hai phương thức đầu tiên đã được thêm vào để thay đổi các đối tượng của kiểu tham chiếu tại chỗ. Tất nhiên, bạn có thể chỉ sử dụng phương pháp thứ ba cho tất cả các loại.

PS Nhờ quan sát của mike , tôi đã thêm phương thức ReplaceAll.


1
Re "thay đổi các đối tượng của kiểu tham chiếu tại chỗ" - cho dù có phải Tlà kiểu tham chiếu hay không là không liên quan. Điều quan trọng là bạn muốn thay đổi (thay đổi) danh sách hay trả lại một danh sách mới. Phương pháp thứ ba tất nhiên sẽ không làm thay đổi danh sách ban đầu, vì vậy bạn không thể chỉ sử dụng phương pháp thứ ba ... . Phương pháp đầu tiên là phương pháp trả lời câu hỏi cụ thể được hỏi. Tuyệt vời mã - chỉ sửa chữa mô tả của bạn về những gì các phương pháp làm :)
ToolmakerSteve

7

Sau câu trả lời của rokkuchan, chỉ cần nâng cấp một chút:

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

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

5

Sử dụng FindIndexvà lambda để tìm và thay thế các giá trị của bạ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

Bạn có thể sử dụng các tiện ích mở rộng tiếp theo dựa trên điều kiện vị từ:

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

Lưu ý: - Thay vì phần mở rộng ThrowIfArgumentIsNull, bạn có thể sử dụng một cách tiếp cận chung như:

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

Vì vậy, trường hợp của bạn với các tiện ích mở rộng này có thể được giải quyết như:

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

1

Tôi không biết nó là tốt nhất hay không nhưng bạn cũng có thể sử dụng nó

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

Hoặc, dựa trên gợi ý của Rusian L., nếu mục bạn đang tìm kiếm có thể nằm trong danh sách nhiều lần ::

[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

Bạn có thể sử dụng biểu thức lambda như thế này.

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

0

tôi thấy tốt nhất để làm điều đó nhanh chóng và đơn giản

  1. tìm mục của bạn trong danh sách

    var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
  2. tạo bản sao từ hiện tại

    OrderDetail dd = d;
  3. Cập nhật bản sao của bạn

    dd.Quantity++;
  4. tìm chỉ mục trong danh sách

    int idx = Details.IndexOf(d);
  5. loại bỏ mục thành lập trong (1)

      Details.Remove(d);
  6. chèn

     if (idx > -1)
          Details.Insert(idx, dd);
      else
          Details.Insert(Details.Count, dd);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.