Cómo reemplazar el elemento de la lista de la mejor manera

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

He reemplazado como arriba. ¿Hay alguna otra mejor manera de comparar el lugar que esta?

Ragavan
fuente

Respuestas:

109

Use Lambda para buscar el índice en la Lista y use este índice para reemplazar el elemento de la lista.

List<string> listOfStrings = new List<string> {"abc", "123", "ghi"};
listOfStrings[listOfStrings.FindIndex(ind=>ind.Equals("123"))] =  "def";
rokkuchan
fuente
15
¡Compruebe -1! En caso de que el artículo no esté presente en colecciones
Surender Singh Malik
3
más uno para el uso de FindIndex
Aaron Barker
2
Esta es la mejor respuesta universal en mi humilde opinión, ya que también se puede usar para comparar objetos.
Simcha Khabinsky
Vea la mejora de Fej que busca -1 . Aunque para una Equalsprueba simple , el buen viejo IndexOffunciona igual de bien y es más conciso, como en la respuesta de Tim .
ToolmakerSteve
109

Podría hacerlo más legible y más eficiente:

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

Esto solicita solo una vez el índice. Su enfoque usa Containsprimero el que necesita repetir todos los elementos (en el peor de los casos), luego está usando el IndexOfque necesita enumerar los elementos nuevamente.

Tim Schmelter
fuente
2
Esta es la respuesta correcta para encontrar literales: ints, cadenas, pero no muy buena para encontrar objetos. Sin embargo, me gusta más la respuesta de rokkuchan ya que es universal.
Simcha Khabinsky
1
@SimchaKhabinsky: también funciona con tipos de referencia, el tipo solo necesita anularse Equalso solo encontrará el objeto si es la misma referencia. Tenga en cuenta que stringtambién es un objeto (tipo de referencia).
Tim Schmelter
Sí, tiene usted razón. Sin embargo, he visto a muchos desarrolladores que no recuerdan implementar Equals y también debe recordar que a veces al mismo tiempo tiene que implementarGetHashCode
Simcha Khabinsky
1
@SimchaKhabinsky: sí, siempre debe anular GetHashCodesi anula, Equalspero GetHashCodesolo se usa si el objeto está almacenado en un conjunto (fe Dictionaryo HashSet), por lo que no se usa con IndexOfo Contains, solo Equals.
Tim Schmelter
Tim, tengo una pregunta sobre esto vs rokkuchan. Leí en documentos que IndexOfusa EqualityComparer<T>.Default. ¿Estás diciendo que eventualmente llamará item.Equals(target)a cada elemento de la lista y, por lo tanto, tiene exactamente el mismo comportamiento que la respuesta de rokkuchan?
ToolmakerSteve
16

Estás accediendo a tu lista dos veces para reemplazar un elemento. Creo que un forbucle simple debería ser suficiente:

var key = valueFieldValue.ToString();
for (int i = 0; i < listofelements.Count; i++)
{
    if (listofelements[i] == key)
    {
        listofelements[i] = value.ToString();
        break;
    }
}
gzaxx
fuente
1
@gzaxx. "Estás accediendo a tu lista dos veces para reemplazar un elemento. Creo que un ciclo for simple debería ser suficiente". ¿Y cuántas veces accedes a la lista en tu bucle for mate?
Pap
5
@Pap lo siento, no estaba siendo lo suficientemente claro. Está iterando su lista dos veces (primero para verificar si el elemento está dentro de la lista, segundo para obtener el índice del elemento).
gzaxx
14

¿Por qué no utilizar los métodos de extensión?

Considere el siguiente 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}
  • Sin duplicación de código
  • No es necesario escribir expresiones linq largas
  • No hay necesidad de usos adicionales

El código fuente:

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

Se han agregado los dos primeros métodos para cambiar los objetos de los tipos de referencia en su lugar. Por supuesto, puede usar solo el tercer método para todos los tipos.

PD Gracias a la observación de Mike , agregué el método ReplaceAll.

Ruslan L.
fuente
1
Volver a "cambiar los objetos de los tipos de referencia en su lugar" - si Tes un tipo de referencia o no es irrelevante. Lo que importa es si desea mutar (alterar) la lista o devolver una nueva lista. El tercer método, por supuesto, no alterará la lista original, por lo que no se puede utilizar sólo el tercer método ... . El primer método es el que responde a la pregunta específica formulada. Excelente código: simplemente corrige su descripción de lo que hacen los métodos :)
ToolmakerSteve
7

Siguiendo la respuesta de rokkuchan, solo una pequeña actualización:

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

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

Utilice FindIndexy lambda para buscar y reemplazar sus 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
Alex Jolig
fuente
3

Puede usar las siguientes extensiones que se basan en una condición 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;
    }

Notas: - En lugar de la extensión ThrowIfArgumentIsNull, puede utilizar un enfoque general como:

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

Entonces su caso con estas extensiones se puede resolver como:

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

No sé si es mejor o no, pero también puedes usarlo.

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");
Nik
fuente
1

O, basándose en la sugerencia de Rusian L., si el artículo que está buscando puede estar en la lista más de una 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);
}
Miguel
fuente
1

Puede usar una expresión lambda como esta.

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

encuentro lo mejor para hacerlo rápido y simple

  1. encuentra tu artículo en la lista

    var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
  2. hacer un clon de la corriente

    OrderDetail dd = d;
  3. Actualizar tu clon

    dd.Quantity++;
  4. buscar índice en la lista

    int idx = Details.IndexOf(d);
  5. eliminar artículo fundado en (1)

      Details.Remove(d);
  6. insertar

     if (idx > -1)
          Details.Insert(idx, dd);
      else
          Details.Insert(Details.Count, dd);
Tarrah Arshad
fuente