if (listofelements.Contains(valueFieldValue.ToString()))
{
listofelements[listofelements.IndexOf(valueFieldValue.ToString())] = value.ToString();
}
Я заменил как указано выше. Есть ли другой лучший способ провести сравнение, кроме этого?
Ответы:
Используйте Lambda, чтобы найти индекс в списке и использовать этот индекс для замены элемента списка.
List<string> listOfStrings = new List<string> {"abc", "123", "ghi"};
listOfStrings[listOfStrings.FindIndex(ind=>ind.Equals("123"))] = "def";
Equalsтеста старый добрый IndexOfработает точно так же, и он более краток - как в ответе Тима .
Вы можете сделать его более читаемым и эффективным:
string oldValue = valueFieldValue.ToString();
string newValue = value.ToString();
int index = listofelements.IndexOf(oldValue);
if(index != -1)
listofelements[index] = newValue;
Это запрашивает только один раз индекс. Ваш подход использует Containsсначала, который должен зациклить все элементы (в худшем случае), затем вы используете то, IndexOfчто необходимо для повторного перечисления элементов.
Equalsиначе вы найдете объект только в том случае, если это та же ссылка. Обратите внимание, что stringэто тоже объект (ссылочный тип).
Equals и вы также должны помнить, что иногда в одно и то же время вы должны реализоватьGetHashCode
GetHashCodeесли переопределить , Equalsно GetHashCodeиспользуется только если объект хранится в наборе (фе Dictionaryили HashSet), поэтому он не используется IndexOfили Containsтолько Equals.
IndexOfиспользует EqualityComparer<T>.Default. Вы говорите, что в конечном итоге это вызовет item.Equals(target)каждый элемент в списке и, следовательно, будет иметь такое же поведение, как и ответ rokkuchan?
Вы дважды обращаетесь к своему списку, чтобы заменить один элемент. Думаю, достаточно простого forцикла:
var key = valueFieldValue.ToString();
for (int i = 0; i < listofelements.Count; i++)
{
if (listofelements[i] == key)
{
listofelements[i] = value.ToString();
break;
}
}
Почему бы не использовать методы расширения?
Рассмотрим следующий код:
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}
Исходный код:
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);
}
}
}
Первые два метода были добавлены для изменения объектов ссылочных типов на месте. Конечно, вы можете использовать только третий метод для всех типов.
PS Благодаря наблюдению Майка я добавил метод ReplaceAll.
Tссылочный тип это или нет. Важно то, хотите ли вы изменить (изменить) список или вернуть новый список. Третий метод, конечно , не будет изменять первоначальный список, так что вы не можете использовать только третий способ ... . Первый метод - это тот, который отвечает на заданный конкретный вопрос. Отлично - просто корректируя ваше описание того , что делают методы :)
Используйте 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
Вы можете использовать следующие расширения, основанные на условии предиката:
/// <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());
Я не знаю, лучше это или нет, но вы тоже можете его использовать
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");
Или, основываясь на предложении Русиана Л., если искомый элемент может быть в списке более одного раза:
[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);
}
Я считаю, что лучше всего делать это быстро и просто
найди твой пункт в списке
var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
сделать клон из текущего
OrderDetail dd = d;
Обновите свой клон
dd.Quantity++;
найти индекс в списке
int idx = Details.IndexOf(d);
удалить найденный элемент в (1)
Details.Remove(d);
вставить
if (idx > -1)
Details.Insert(idx, dd);
else
Details.Insert(Details.Count, dd);