if (listofelements.Contains(valueFieldValue.ToString()))
{
listofelements[listofelements.IndexOf(valueFieldValue.ToString())] = value.ToString();
}
Я замінив, як вище. Чи є інший найкращий спосіб порівняння, крім цього?
Відповіді:
Використовуйте лямбду, щоб знайти індекс у списку, і використовуйте цей індекс для заміни елемента списку.
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використовується лише в тому випадку, якщо об'єкт зберігається у наборі (fe 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("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 Завдяки спостереженню Майка , я додав метод 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);