Группировка чисел на C#


При перечислении номеров страниц в индексах документов или для печати обычно группируются смежные страницы. В этой заметке описывается метод, который создает такие группировки из простого набора целых чисел.

Номера страниц

Если у вас есть большой набор чисел, которые используются в качестве ссылок, может быть полезно сгруппировать их в ряд меньших диапазонов, каждый из которых содержит смежные числа. Этот подход обычно используется в индексах документов. Он также используется в программном обеспечении для редактирования документов при указании, что вы хотите распечатать определенный набор страниц.

Например, у вас может быть следующий набор чисел:

1, 2, 4, 6, 7, 8, 9, 10, 12, 17, 19, 20, 21, 24, 25, 30
Приведенный выше список может быть выражен в виде ряда групп и отдельных чисел, как показано ниже:

1-2, 4, 6-10, 12, 17, 19-21, 24-25, 30
В этой статье мы создадим метод, который преобразует массив целочисленных значений в массив строк, где каждый элемент возвращаемого массива представляет либо одно число, либо группу смежных значений.

Реализация

Прежде чем мы создадим метод, который группирует значения, давайте создадим программу, которую можно использовать для вызова метода и просмотра результатов. Создайте новое консольное приложение и замените пустой метод Main следующим кодом:

static void Main(string[] args)
{
    var pages = new int[] { 1, 2, 4, 6, 7, 8, 9, 10, 12, 17, 19, 20, 21, 24, 25, 30 };
 
    string[] groups = GroupPageNumbers(pages);
 
    Console.WriteLine("Original: {0}", BuildList(pages.Select(p => p.ToString())));
    Console.WriteLine("Grouped:  {0}", BuildList(groups));
}
 
static string BuildList(IEnumerable<string> items)
{
    return string.Join(", ", items.ToArray());
}
Этот код создает массив дискретных целых чисел, которые мы хотим сгруппировать. Он вызывает метод GroupPageNumbers для создания массива строк, а затем выводит исходный и форматированный массивы для сравнения.

Создание метода

Теперь мы можем построить ключевой метод. Мы ожидаем, что вызывающий объект передаст отсортированный массив целых чисел и вернет массив строк, поэтому создайте метод GroupPageNumbers(int[] pages). Можно добавить сортировку в метод, но я не сделал для упрощения примера кода.

static string[] GroupPageNumbers(int[] pages)
{
}
Метод создаст список отдельных чисел или групп смежных значений в общем списке строк, чтобы мы могли автоматически увеличить размер коллекции. Это будет преобразовано в массив до его возврата, как только мы узнаем окончательное количество групп.

Добавьте следующую строку в метод для инициализации списка:

var groups = new List<string>();
Основная часть метода - это цикл, который обрабатывает весь входной массив. Мы будем использовать цикл for, а не цикл foreach, поскольку он позволяет нам изменять переменную управления циклом в блоке кода команды, а также в конце каждой итерации.

for (int i = 0; i < pages.Length; i++)
{
}
В теле цикла мы определим отдельные значения и смежные группы и вручную продвинем переменную управления циклом. Это будет означать, что каждый раз при запуске цикла значение текущего индекса будет начальным значением для новой группы. К концу итерации цикла мы определим конечное значение для группы. Если эти два значения совпадают, мы определили одно значение.

Чтобы сохранить значение текущей позиции в качестве начала диапазона, добавьте следующую команду в блок кода цикла:

int start = pages[i];
С записанным начальным значением нам нужно искать соседние целые числа. Для достижения этой цели мы можем использовать цикл while, который продолжает цикл до тех пор, пока значение следующей позиции в цикле for не будет точно на единицу выше текущей позиции, или пока массив не будет исчерпан. Как только текущее и следующее значения не будут смежными, или мы пробежимся по всему массиву, значение в текущей позиции должно быть концом группы. Начальное и конечное значения затем можно объединить в строку и добавить в массив групп.

Добавьте следующий цикл while в цикл for. Обратите внимание, что группа отформатирована с использованием другого метода. Мы добавим это в ближайшее время.

while (i < pages.Length - 1 && pages[i + 1] == pages[i] + 1)
{
    i++;
}
groups.Add(FormatGroup(start, pages[i]));
После завершения цикла переменная groups содержит конечный результат. Верните это, используя следующий код, добавленный после закрывающей скобки цикла for:

return groups.ToArray();
Последним шагом является добавление метода, который форматирует диапазон. Это проверяет, совпадают ли начальное и конечное значения с помощью условного оператора. Если они это сделают, он вернет единственное значение в виде строки. Если они отличаются, то два значения объединяются в одну строку.

Добавьте новый метод:

static string FormatGroup(int start, int end)
{
    return start == end ? start.ToString() : string.Format("{0}-{1}", start, end);
}
Тестирование программы

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

Оригинал: 1, 2, 4, 6, 7, 8, 9, 10, 12, 17, 19, 20, 21, 24, 25, 30
Сгруппированные: 1-2, 4, 6-10, 12, 17, 19-21, 24-25, 30
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегизаметки, си шарп, алгоритмы




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.



Урок 19. Простые функции форматирования строк C#
Урок 4. Экземпляры и статические члены Java
Сколько будет стоить и где можно заказать программу