if (listofelements.Contains(valueFieldValue.ToString()))
{
listofelements[listofelements.IndexOf(valueFieldValue.ToString())] = value.ToString();
}
Wymieniłem jak wyżej. Czy jest jakieś inne najlepsze miejsce do porównania niż to?
Odpowiedzi:
Użyj Lambda, aby znaleźć indeks na liście i użyj tego indeksu do zastąpienia elementu listy.
List<string> listOfStrings = new List<string> {"abc", "123", "ghi"};
listOfStrings[listOfStrings.FindIndex(ind=>ind.Equals("123"))] = "def";
Equalstestu, stare dobre IndexOfdziała równie dobrze i jest bardziej zwięzłe - jak w odpowiedzi Tima .
Możesz uczynić go bardziej czytelnym i wydajniejszym:
string oldValue = valueFieldValue.ToString();
string newValue = value.ToString();
int index = listofelements.IndexOf(oldValue);
if(index != -1)
listofelements[index] = newValue;
To pyta tylko raz o indeks. Twoje podejście wykorzystuje Containsnajpierw, który musi zapętlić wszystkie elementy (w najgorszym przypadku), a następnie używasz, IndexOfktóry wymaga ponownego wyliczenia elementów.
Equalslub znajdziesz obiekt tylko wtedy, gdy jest to to samo odniesienie. Zauważ, że stringjest to również obiekt (typ referencyjny).
Equals i trzeba też pamiętać, że czasami w tym samym czasie trzeba to zaimplementowaćGetHashCode
GetHashCodejeśli przesłonić Equals, ale GetHashCodejest stosowany tylko wtedy, gdy obiekt jest przechowywany w zbiorze (Fe Dictionarylub HashSet), więc to nie jest stosowany z IndexOflub Containstylko Equals.
IndexOfużywają EqualityComparer<T>.Default. Czy chcesz powiedzieć, że w końcu wywoła item.Equals(target)każdą pozycję z listy, a zatem zachowuje się dokładnie tak samo, jak odpowiedź rokkuchana?
Uzyskujesz dostęp do listy dwukrotnie, aby zamienić jeden element. Myślę, że prosta forpętla powinna wystarczyć:
var key = valueFieldValue.ToString();
for (int i = 0; i < listofelements.Count; i++)
{
if (listofelements[i] == key)
{
listofelements[i] = value.ToString();
break;
}
}
Dlaczego nie skorzystać z metod rozszerzających?
Rozważ następujący kod:
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}
Kod źródłowy:
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(nameof(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(nameof(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(nameof(source));
return source.Select(x => EqualityComparer<T>.Default.Equals(x, oldValue) ? newValue : x);
}
}
}
Dwie pierwsze metody zostały dodane w celu zmiany obiektów typów referencyjnych w miejscu. Oczywiście możesz użyć tylko trzeciej metody dla wszystkich typów.
PS Dzięki obserwacji Mike'a dodałem metodę ReplaceAll.
Tnie ma znaczenia, czy jest to typ referencyjny, czy nie. Liczy się to, czy chcesz zmutować (zmienić) listę, czy zwrócić nową listę. Trzecia metoda oczywiście nie zmienia pierwotną listę, więc nie można używać tylko z trzeciej metody ... . Pierwsza metoda to ta, która odpowiada na określone pytanie. Doskonały kod - wystarczy poprawić opis tego, co robią metody :)
Użyj FindIndexi lambda, aby znaleźć i zamienić wartości:
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
Możesz użyć kolejnych rozszerzeń, które są oparte na warunku predykatu:
/// <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;
}
Uwagi: - Zamiast rozszerzenia ThrowIfArgumentIsNull można zastosować ogólne podejście, takie jak:
if (argName == null) throw new ArgumentNullException(nameof(argName));
Więc twój przypadek z tymi rozszerzeniami można rozwiązać jako:
string targetString = valueFieldValue.ToString();
listofelements.Replace(x => x.Equals(targetString), value.ToString());
Nie wiem, czy jest to najlepsze, czy nie, ale możesz go również użyć
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");
Lub, opierając się na sugestii Rusian L., jeśli szukany przedmiot może znajdować się na liście więcej niż raz:
[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);
}
uważam, że najlepiej zrobić to szybko i prosto
znajdź swój przedmiot na liście
var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
zrobić klon z bieżącego
OrderDetail dd = d;
Zaktualizuj klon ur
dd.Quantity++;
znajdź indeks na liście
int idx = Details.IndexOf(d);
usuń znalezioną pozycję w (1)
Details.Remove(d);
wstawić
if (idx > -1)
Details.Insert(idx, dd);
else
Details.Insert(Details.Count, dd);