목록 항목을 가장 좋은 방법으로 바꾸는 방법


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

위와 같이 교체했습니다. 이것보다 비교하는 다른 최선의 방법이 있습니까?

답변:


109

Lambda를 사용하여 목록에서 인덱스를 찾고이 인덱스를 사용하여 목록 항목을 바꿉니다.

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

14
-1 확인! 항목 못했을 경우에 대비 컬렉션에 존재하지 않습니다
Surender 싱 말리크

3
플러스 FindIndex 사용 하나
아론 바커

2
이것은 물체를 비교하는 데에도 사용할 수 있기 때문에 가장 보편적 인 대답 IMHO입니다.
Simcha Khabinsky

-1을 확인하는 Fej의 개선 사항을 참조하십시오 . 간단한 Equals테스트의 경우 좋은 오래된 IndexOf것도 잘 작동하며 Tim의 대답 에서처럼 더 간결 합니다.
ToolmakerSteve

109

더 읽기 쉽고 효율적으로 만들 수 있습니다.

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

인덱스를 한 번만 요청합니다. 귀하의 접근 방식은 Contains모든 항목을 반복 해야하는 (최악의 경우) 먼저 사용 IndexOf하고 항목을 다시 열거하는 데 필요한 것을 사용 합니다.


2
이것은 정수, 문자열과 같은 리터럴을 찾기위한 정답이지만 객체를 찾기에는 그리 좋지 않습니다. 하지만 롯 쿠찬의 대답은 보편적이기 때문에 더 좋아합니다.
Simcha Khabinsky

1
@SimchaKhabinsky : 참조 유형에서도 작동합니다. 유형은 재정의 만하면 Equals되며 동일한 참조 인 경우에만 객체를 찾을 수 있습니다. 참고 string또한 물체 (참조 형)이다.
Tim Schmelter

네, 맞아요. 그러나, 나는 구현하기 위해 기억하지 못하는 많은 개발자를 본 Equals 당신은 같은 시간에 가끔 기억이 당신을 구현해야GetHashCode
Simcha Khabinsky

1
@SimchaKhabinsky : 예, 당신은 항상 오버라이드 (override) 할 필요가 GetHashCode재정의 경우 EqualsGetHashCode객체가 세트 (철에 저장되어있는 경우에만 사용됩니다 Dictionary또는 HashSet)이 사용되지 그래서, IndexOf또는 ContainsEquals.
Tim Schmelter

팀, 이것 vs 롯 쿠찬에 대해 질문이 있습니다. IndexOf사용 하는 문서에서 읽었습니다 EqualityComparer<T>.Default. 결국 item.Equals(target)목록의 각 항목을 호출 하므로 rokkuchan의 대답과 똑같은 동작이 발생한다는 말입니까?
ToolmakerSteve

16

한 요소를 대체하기 위해 목록에 두 번 액세스하고 있습니다. 간단한 for루프로 충분 하다고 생각합니다 .

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

1
@gzaxx. "하나의 요소를 교체하기 위해 목록에 두 번 액세스하고 있습니다. 간단한 for 루프로 충분해야한다고 생각합니다." 그리고 for 루프 메이트의 목록에 몇 번이나 액세스합니까?
Pap

5
@Pap 죄송합니다, 나는 충분히 명확하지 않았습니다. 그는 자신의 목록을 두 번 반복하고 있습니다 (첫 번째는 항목이 목록 안에 있는지 확인하고 두 번째는 항목 인덱스를 가져옴).
gzaxx

13

확장 방법을 사용하지 않는 이유는 무엇입니까?

다음 코드를 고려하십시오.

        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}
  • 중복 된 코드 없음
  • 긴 linq 표현식을 입력 할 필요가 없습니다.
  • 추가로 사용할 필요가 없습니다.

소스 코드 :

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

참조 유형의 객체를 제자리에서 변경하기 위해 처음 두 가지 방법이 추가되었습니다. 물론 모든 유형에 대해 세 번째 방법 만 사용할 수 있습니다.

PS Mike의 관찰 덕분에 ReplaceAll 메서드를 추가했습니다.


1
다시 "참조 유형의 개체를 제자리에서 변경" - T참조 유형 인지 여부 는 관련이 없습니다. 중요한 것은 목록을 변경 (변경)할지 또는 새 목록을 반환할지 여부입니다. 당신이 그렇게 물론 세 번째 방법은, 원래 목록을 변경하지 않습니다 수 없습니다 단지 세 번째 방법을 사용하여 ... . 첫 번째 방법은 질문 한 특정 질문에 답하는 방법입니다. 우수한 코드 - 단지 :) 방법이 무엇의 당신의 설명을 수정
ToolmakerSteve

7

rokkuchan의 답변에 따라 약간의 업그레이드 :

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

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

5

FindIndex및 람다를 사용 하여 값을 찾고 바꿉니다.

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

조건 자 조건을 기반으로하는 다음 확장을 사용할 수 있습니다.

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

참고 :-ThrowIfArgumentIsNull 확장 대신 다음과 같은 일반적인 접근 방식을 사용할 수 있습니다.

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

따라서 이러한 확장을 사용하는 경우는 다음과 같이 해결할 수 있습니다.

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

1

최고인지 아닌지는 모르겠지만 당신도 사용할 수 있어요

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

또는 Rusian L.의 제안에 따라 검색중인 항목이 목록에 두 번 이상있을 수있는 경우 :

[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

이렇게 람다 식을 사용할 수 있습니다.

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

0

빠르고 간단하게하기 위해 최선을 다합니다

  1. 목록에서 항목 찾기

    var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
  2. 현재에서 복제하다

    OrderDetail dd = d;
  3. UR 클론 업데이트

    dd.Quantity++;
  4. 목록에서 색인 찾기

    int idx = Details.IndexOf(d);
  5. (1)에서 발견 된 항목 제거

      Details.Remove(d);
  6. 끼워 넣다

     if (idx > -1)
          Details.Insert(idx, dd);
      else
          Details.Insert(Details.Count, dd);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.