Как получить данные, которые идут подряд

Есть List<DB>

class DB
{
    int id { get; set; }
    decimal win { get; set; }
    decimal credit { get; set; }
}
id     win     credit
1      10      100
2      12      105 
3      9       109
4      11      109
5      5       109
6      4       109
7      3       95
8      2       115

Как узнать с какого по какой индекс идут строки, с одинаковым credit. Допустим будет переменная n-количество совпадений идущие больше 4 раз подряд(>=). Вот как найти эти индексы?

В моем примере выше это с 3 по 6 строки


Ответы (1 шт):

Автор решения: EvgeniyZ

Как один из вариантов, который написан на коленке:

int startIdex = -1;
var result = new List<(decimal Credit, Range Range)>();

for (int i = 0; i < data.Count; i++)
{
    var current = data[i];
    var next = i + 1 < data.Count ? data[i+1] : null;

    if (current.Credit == next?.Credit)
    {
        if (startIdex == -1)
            startIdex = i;
    }
    else
    {
        if (startIdex == -1)
        {
            result.Add((current.Credit, new Range(i, i+1)));
        }
        else
        {
            result.Add((current.Credit, new Range(startIdex, i+1)));
            startIdex = -1;
        }
    }
}

Поясняю что тут да как:

  • new List<(decimal Credit, Range Range)> - тут все зависит от того, как вид вы хотите. Можно сделать класс, содержащий в себе эти данные, можно сделать Dictionary, у которого ключом будет Range, мне лично так было удобней)
  • for (int i = 0; i < data.Count; i++) - простой цикл, который даст текущий индекс (что нам и нужно).
  • var current = data[i]; - текущий объект.
  • var next = i + 1 < data.Count ? data[i+1] : null; - следующий объект в коллекции. Если индекс за пределами, то возвращаем NULL.
  • if (current.Credit == next?.Credit) - Если текущее значение и следующее равны, то...
  • if (startIdex == -1) - дабы нам не менять каждую иттерацию стартовый индекс, есть эта проверка, которая меняет его значение лишь в тот момент, когда первый раз было найдено повторяющееся значение.
  • блок else - тут думаю все понятно, если у нас текущее значение не повторяется со следующим, то мы проверяем изменение startIndex, если оно со стандартным значением -1, то просто добавляем новый объект, где Range будет от i до i+1, если же стартовый индекс был изменен, то добавляем уже нужный нам Range от startIndex до i+1, ну и сбрасываем этот индекс.

То есть, наша логика такая:

  • Проверяем текущее и следующее значение
    • Если они равны, то записываем стартовый индекс.
    • Если они не равны, то:
      • Если индекс был изменен - добавляем в результат значение с Range(стартовыйИндекс, текущий+1).
      • Если индекс не изменен - добавляем в результат значение с Range(текущий, текущий+1).

В итоге мы получим примерно следующее:

(100, 0..1)
(105, 1..2)
(109, 2..6)
(95, 6..7)
(115, 7..8)

Сами данные:

0. 100
1. 105
2. 109
3. 109
4. 109
5. 109
6. 95
7. 115

И видим, что 109-е значение идет с 2 и до 6 индека (помним, что подсчет индексов в C# идет с 0!).

Тут можно провести еще различные улучшения, но саму суть думаю вы теперь поняли. Также выводимый результат смотрите сами какой вам нужен, к примеру, можете брать не индекс (i), а id из класса db, думаю труда вам это не составит.

А дальше уже поступайте с этими данными так, как вашей душе будет угодно, например:

var range = ranges.FirstOrDefault(x => x.Credit == 109);
var items = data[range.Range];

P.S. Range были добавлены в C#8. Если по каким-то причинам вам это не подходит, то уж извиняйте, придется писать свои костыли.

Пример онлайн

→ Ссылка