2011-12-21 1 views
3

У меня есть ICollection<SomeClass>.Самый простой способ получить «следующий» элемент в последовательности?

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

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

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

[0] - "а", ложные

[1] - "Ъ", истинный

[2] - "с", ложные

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

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

[0] - "а", ложные

[1] - "Ъ", ложные

[2] - "c", true

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

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

Любые лучшие идеи?

ответ

4

Поскольку 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

я могу думать только грязный способ делать это с помощью 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) будет использовать решение Ахмад Mageed (как предложено в комментариях ниже):

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

это работает, хотя ... вы получаете +1 от меня. Попробуйте @ TheEvilPengiun's one now ... – RPM1984

+2

Мой код, вероятно, будет более эффективным, если у вас много предметов, но с современными ПК лучшие решения, как правило, наиболее читаемы. Если это будет иметь смысл, если вы снова взглянете на код через 6 месяцев, это лучшее решение. – TheEvilPenguin

+1

@Marlon nice LINQ подход, но использование 'Skip' и' First' будет генерировать исключение, если следующий элемент не существует. Лучшим подходом было бы использовать: 'SomeClass b = x.ElementAtOrDefault (1);' тогда, если бы OP хотел изменить значения * только *, если следующий элемент существовал, поместите последние 2 строки назначения внутри этого условия: 'if (b! = null) {...}'. Конечно, вы упомянули об отсутствии проверки ошибок :) –

0

Пара идей

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

Поскольку ваша коллекция заказана, может ли она быть 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; 
+2

'IEnumerable ' и 'ICollection ' не имеют указателей - как это будет работать? – RPM1984

+0

А я получил меня; Я всегда получаю ICollection путать с IList . –

+1

'ICollection ' также не имеет индексатора - попробуйте код, он не будет компилироваться. – RPM1984

5

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

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.

Edit: Фиксированный крайний случай, когда IsPreferred всегда устанавливается в ложь на сбор одного элемента

+0

Мне нравится это решение, потому что a) оно работает, b) оно довольно эффективно, и c) я скрыл его за методом расширения, называемым 'ShuffleDownToNextPreference', поэтому мне не нужно беспокоиться об этом, но когда я читаю код, который использует его, я знаю, что он делает. Принято. – RPM1984

+0

@ RPM1984 и @ TheEvilPenguin: единственный недостаток, который я вижу при таком подходе, заключается в том, что он всегда устанавливает для параметра «IsPreferred» значение false для первого элемента независимо от того, существует или нет следующий элемент.Чтобы избежать этого, см. Мое решение. –

+0

@AhmadMageed - да, это правда. Кромка, но правда, тем не менее. Извините Пенджиун - Ахмад украл ваш принятый ответ. :) +1, чтобы компенсировать это. – RPM1984

Смежные вопросы