Самый простой способ получить «следующий» элемент в последовательности? - PullRequest
3 голосов
/ 21 декабря 2011

У меня есть ICollection<SomeClass>.

public class SomeClass
{
   public string Text { get; set; }
   public bool IsPreferred { get; set; }
}

Предметы там были предварительно заказаны, так что «следующий» что-то значит.

В моем сценарии содержимое последовательности выглядит следующим образом:

[0] - "a", false

[1] - «b», true

[2] - «c», false

Я пытаюсь получить «следующий» элемент после того, который IsPreferred == true. Итак, в приведенном выше примере я хочу получить элемент 2 и очистить другое значение IsPreferred.

Итак, я хочу закончить с этим:

[0] - "a", false

[1] - «b», false

[2] - "c", true

В основном перетасовывает предпочтительный предмет вниз.

Какой лучший способ сделать это? Единственное, о чем я могу подумать, - это создать новый массив, добавить их один за другим, отследить индекс того, который является предпочтительным, а затем перейти к элементу с указанным индексом + 1.

Есть идеи получше?

Ответы [ 5 ]

5 голосов
/ 21 декабря 2011

Я бы использовал перечислитель для итерации по коллекции - это то, что foreach делает за кулисами:

var enumerator = collection.GetEnumerator();

while (enumerator.MoveNext())
{
    if (enumerator.Current.IsPreferred)
    {
        var oldPreferred = enumerator.Current;

        if (enumerator.MoveNext())
        {
            oldPreferred.IsPreferred = false;
            enumerator.Current.IsPreferred = true;
        }

        break;
    }
}

Это предполагает, что вы хотите остановиться после нахождения первого элемента с помощью IsPreferred, и еслиэто последний элемент, все еще очищенный IsPreferred.

Редактировать: Исправлен случай с краем, когда IsPreferred всегда имеет значение false для коллекции одного элемента

4 голосов
/ 21 декабря 2011

Поскольку ICollection<T> не дает индексатора, я бы выбрал более прямое решение, а не полагался на LINQ.

Предполагая, что вы хотите (1) остановиться как можно скореепоскольку условие выполнено, и (2) измените значение только при наличии следующего элемента, это может быть достигнуто следующим образом:

bool isFound = false;
SomeClass targetItem = null;
foreach (var item in list)
{
    if (isFound)
    {
        item.IsPreferred = true;
        targetItem.IsPreferred = false;
        break;
    }
    if (item.IsPreferred)
    {
        targetItem = item;
        isFound = true;
    }
}
3 голосов
/ 21 декабря 2011

Я могу думать только о грязном способе сделать это с LINQ:

var x = collection.SkipWhile(z => !z.IsPreferred);
SomeClass a = x.First();
SomeClass b = x.Skip(1).First();

a.IsPreferred = false;
b.IsPreferred = true;

Это, конечно, исключает проверку ошибок и не очень эффективно.


Другая возможность (использованиеLINQ) будет использовать решение Ахмада Магида (как предлагается в комментариях ниже):

var x = collection.SkipWhile(z => !z.IsPreferred);
SomeClass a = x.FirstOrDefault();
SomeClass b = x.ElementAtOrDefault(1);

if (a != null) a.IsPreferred = false;
if (b != null) b.IsPreferred = true;
0 голосов
/ 21 декабря 2011

Поскольку ваша коллекция заказана, может ли это быть IList вместо ICollection?

Затем вы можете создать метод расширения, который даст вам индексы значений, к которым применяется некоторый предикат:

static IEnumerable<int> IndexWhere<T>(this IList<T> list, 
                                      Func<T, bool> predicate)
{
    for(int i = 0; i < list.Count; i++)
    {
        if(predicate(list[i])) yield return i;
    }
}

Предполагая, что вы ожидаете совпадения только с одним элементом, похоже, что остальные будут выглядеть так:

var preferredIndex = list.IndexWhere(x=>x.IsPreferred).Single();
list[preferredIndex].IsPreferred = false;
list[preferredIndex + 1].IsPreferred = true;
0 голосов
/ 21 декабря 2011

Пара идей

  1. Если вы можете перебрать коллекцию, почему мы не можем установить значение i + 2 в true перед обработкой i + 1? Убедитесь, что я + 2 существует
  2. Другая идея заключается в расширении LinkedList и создании current.next.next = true, если он существует.
...