Como substituir o item da lista da melhor maneira


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

Eu substituí como acima. Existe alguma outra melhor maneira de comparar do que esta?

Respostas:


109

Use Lambda para encontrar o índice na Lista e use este índice para substituir o item da lista.

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

15
Verifique se há -1! No caso de o item não estar presente nas coleções
Surender Singh Malik

3
mais um para uso de FindIndex
Aaron Barker

2
Esta é a melhor resposta universal IMHO, já que também pode ser usada para comparar objetos.
Simcha Khabinsky de

Veja o aprimoramento de Fej que verifica -1 . Embora para um Equalsteste simples , o bom e velho IndexOffunciona tão bem e é mais conciso - como na resposta de Tim .
ToolmakerSteve

109

Você pode torná-lo mais legível e mais eficiente:

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

Isso pede apenas uma vez para o índice. Sua abordagem usa Containsprimeiro o que precisa fazer um loop de todos os itens (no pior caso), depois você usa o IndexOfque precisa enumerar os itens novamente.


2
Esta é a resposta correta para localizar literais - ints, strings, mas não muito boa para localizar objetos. No entanto, gosto mais da resposta de rokkuchan, pois é universal.
Simcha Khabinsky de

1
@SimchaKhabinsky: funciona também com tipos de referência, o tipo só precisa ser sobrescrito Equalsou você só encontrará o objeto se for a mesma referência. Observe que stringtambém é um objeto (tipo de referência).
Tim Schmelter

Sim você está certo. No entanto, tenho visto muitos desenvolvedores não conseguirem se lembrar de implementar Equals e você também deve se lembrar que às vezes, ao mesmo tempo, você tem que implementarGetHashCode
Simcha Khabinsky

1
@SimchaKhabinsky: sim, você sempre deve sobrescrever GetHashCodese sobrescrever, Equalsmas GetHashCodesó é usado se o objeto estiver armazenado em um conjunto (fe Dictionaryou HashSet), então não é usado com IndexOfou Contains, apenas Equals.
Tim Schmelter de

Tim, tenho uma pergunta sobre isso vs rokkuchan. Eu li em docs que IndexOfusa EqualityComparer<T>.Default. Você está dizendo que acabará chamando item.Equals(target)cada item da lista e, portanto, tem exatamente o mesmo comportamento da resposta de rokkuchan?
Toolmaker Steve

16

Você está acessando sua lista duas vezes para substituir um elemento. Acho que um forloop simples deve ser suficiente:

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

1
@gzaxx. “Você está acessando sua lista duas vezes para substituir um elemento. Eu acho que o loop for simples deve ser suficiente”. E quantas vezes você acessa a lista em seu companheiro de loop for?
Pap

5
@Pap, desculpe, eu não estava sendo claro o suficiente. Ele está iterando sua lista duas vezes (primeiro para verificar se o item está dentro da lista, segundo para obter o índice do item).
gzaxx

13

Por que não usar os métodos de extensão?

Considere o seguinte código:

        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}
  • Nenhum código duplicado
  • Não há necessidade de digitar expressões linq longas
  • Não há necessidade de usos adicionais

O código-fonte:

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

Os primeiros dois métodos foram adicionados para alterar os objetos de tipos de referência no local. Claro, você pode usar apenas o terceiro método para todos os tipos.

PS: Graças à observação de Mike , adicionei o método ReplaceAll.


1
Re "alterar os objetos de tipos de referência no local" - se Té um tipo de referência ou não é irrelevante. O que importa é se você deseja modificar (alterar) a lista ou retornar uma nova lista. O terceiro método é claro que não irá alterar a lista original, para que você não pode usar apenas o terceiro método ... . O primeiro método é aquele que responde à pergunta específica feita. Excelente código - apenas corrigindo sua descrição do que os métodos fazem :)
ToolmakerSteve

7

Seguindo a resposta de rokkuchan, apenas uma pequena atualização:

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

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

5

Use FindIndexe lambda para encontrar e substituir seus valores:

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

Você pode usar as próximas extensões que são baseadas em uma condição de predicado:

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

Observações: - Em vez da extensão ThrowIfArgumentIsNull, você pode usar uma abordagem geral como:

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

Portanto, seu caso com essas extensões pode ser resolvido como:

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

1

Não sei se é melhor ou não, mas você também pode usar

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

Ou, com base na sugestão de Rusian L., se o item que você está procurando puder estar na lista mais de uma vez:

[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

Você pode usar a expressão lambda assim.

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

0

eu acho o melhor para fazer rápido e simples

  1. encontre seu item na lista

    var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
  2. fazer clone do atual

    OrderDetail dd = d;
  3. Atualize seu clone

    dd.Quantity++;
  4. encontrar índice na lista

    int idx = Details.IndexOf(d);
  5. remova o item fundado em (1)

      Details.Remove(d);
  6. inserir

     if (idx > -1)
          Details.Insert(idx, dd);
      else
          Details.Insert(Details.Count, dd);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.