Отдельный диапазон чисел, если последовательно, а затем дефис, и если происходит разрыв последовательности, то запятый символ

У меня есть строка, обозначающая номера страниц, такие как 1,2,3,4,8,9,10,15.

Я хочу, чтобы это отображалось как 1-4,8-10,15 i.e числа в последовательности разделены дефисом, заключенным наименьшим и наибольшим числом в последовательности.

Если разрыв последовательности, диапазон должен быть разделен запятой.

string pageNos = "5,6,7,9,10,11,12,15,16";
string result=string.Empty;
string[] arr1 = pageNos.Split(',');
int[] arr = new int[arr1.Length];

for (int x = 0; x < arr1.Length; x++) // Convert string array to integer array
{
    arr[x] = Convert.ToInt32(arr1[x].ToString());
}

for (int i = 0; i < arr.Length;i++)
{
    for (int j = i + 1; ; j++)
        if (arr[i] == (arr[j] - 1))
            result += arr[i].ToString() + "-" + arr[j].ToString();
        else
            result += arr[i].ToString() + ",";
}

Console.WriteLine(result);

Ответ 1

Я думаю, что loop-in-loop делает вещи более запутанными. Попробуйте использовать только один цикл, потому что вам нужно только перебирать весь список один раз.

int start,end;  // track start and end
end = start = arr[0];
for (int i = 1; i < arr.Length; i++)
{
    // as long as entries are consecutive, move end forward
    if (arr[i] == (arr[i - 1] + 1))
    {
        end = arr[i];
    }
    else
    {
        // when no longer consecutive, add group to result
        // depending on whether start=end (single item) or not
        if (start == end)
            result += start + ",";
        else
            result += start + "-" + end + ",";

        start = end = arr[i];
    }
}

// handle the final group
if (start == end)
    result += start;
else
    result += start + "-" + end;

Демо: http://ideone.com/7HdpS7

Ответ 2

Немного LINQ будет убирать это:

static IEnumerable<Tuple<int, int>> GetRanges(IEnumerable<int> source)
{
   bool started = false;
   int rangeStart = 0, lastItem = 0;

   foreach (int item in source)
   {
      if (!started)
      {
         rangeStart = lastItem = item;
         started = true;
      }
      else if (item == lastItem + 1)
      {
         lastItem = item;
      }
      else
      {
         yield return new Tuple<int, int>(rangeStart, lastItem);
         rangeStart = lastItem = item;
      }
   }

   if (started)
   {
      yield return new Tuple<int, int>(rangeStart, lastItem);
   }
}

static string FormatRange(Tuple<int, int> range)
{
   string format = (range.Item1 == range.Item2) ? "{0:D}" : "{0:D}-{1:D}";
   return string.Format(format, range.Item1, range.Item2);
}

string pageNos = "5,6,7,9,10,11,12,15,16";
int[] pageNumbers = Array.ConvertAll(pageNos.Split(','), Convert.ToInt32);
string result = string.Join(",", GetRanges(pageNumbers).Select(FormatRange));

Ответ 3

string pageNos = "5,6,7,9,10,11,12,15,16";
string[] arr1 = pageNos.Split(',');
int[] arr = new int[arr1.Length];

for (int x = 0; x < arr1.Length; x++) // Convert string array to integer array
{
    arr[x] = Convert.ToInt32(arr1[x]);
}

StringBuilder sb = new StringBuilder();
bool hyphenOpen = false;
for (int i = 0; i < arr.Length - 1; i++)
{
    if (arr[i] + 1 == arr[i+1])
    {
        if (!hyphenOpen)
        {
            hyphenOpen = true;
            sb.Append(arr[i] + "-");
        }
    }
    else
    {
        hyphenOpen = false;
        sb.Append(arr[i] + ",");
    }
}
sb.Append(arr[arr.Length-1]);
Console.WriteLine(sb.ToString());

Это длинный и неуклюжий, но он работает.

P.S. - Я оставил исходную строку OP- > int как есть, см. Комментарий JonB по вопросу для более чистого метода.

Ответ 4

Вы можете использовать этот метод для получения смежных групп чисел, где каждая группа представлена ​​пользовательским Range -class:

class Range
{
    public int? Start { get; set; }
    public int? End { get; set; }
}

private static IEnumerable<Range> getAdjacentRanges(IEnumerable<int> nums)
{
    var ranges = new List<Range>();
    if (!nums.Any())
        return ranges;

    var ordered = nums.OrderBy(i => i);
    int lowest = ordered.First();
    int last = lowest;
    ranges.Add(new Range { Start = lowest });

    foreach (int current in ordered)
    {
        lastRange = ranges[ranges.Count - 1];
        if (current > last + 1)
        {
            lastRange.End = last;
            ranges.Add(new Range { Start = current });
        }
        last = current;
    }

    return ranges;
}

Остальное легко:

var arr = new[] { 1, 2, 3, 4, 8, 9, 10, 15 };
var ranges = getAdjacentRanges(arr)
    .Select(r => r.End.HasValue ? string.Format("{0}-{1}", r.Start, r.End) : r.Start.ToString());
Console.Write(string.Join(",", ranges));

вывод: 1-4,8-10,15

DEMO

Ответ 5

Следующий код JS также поможет

  1. Удалить дубликаты
  2. Сортировать массив
  3. Используйте 2 указателя
  4. Начать с того же места
  5. Продвигайте второй указатель, пока его следующий элемент не станет преемником
  6. Выведите элемент в я и j с пробелами и запятой
  7. Не печатать элемент в j, если индекс совпадают
  8. Удалить запятую

const givenArray = [1, 6, 6, 8, 44, 45, 47, 55, 9, 11, 12, 1, 6, 88, 13, 14, 2, 3, 5, 22, 33, 57, 88];

const input = [...new Set(givenArray)].sort((a, b) => a - b);
let i = 0;
let j = 0;
let output = '';

while (i < input.length) {
  while (j < input.length && (input[j] + 1) === input[j + 1]) {
    j++;
  }
  output += '${input[i]}';
  if (i !== j) {
    output += ' - ${input[j]}, ';
  } else {
    output += ', ';
  }
  i = j + 1;
  j = i;
}
console.log(output.substring(0, output.lastIndexOf(",")));

Ответ 6

Я не человек С#, но я думаю, у вас есть проблема:

 if (arr[i] == (arr[j] - 1))
            result += arr[i].ToString() + "-" + arr[j].ToString();

вы не должны добавлять это в свой результат. но установите флаг (возможно, булев), чтобы указать, что теперь я начинаю считать.

если флаг == ture и число больше не является непрерывным, то есть время добавить к вашему результату, конечно, с помощью "-".

Ответ 7

Здесь другое решение, которое создает List<Tuple<int, int>> с каждым не последовательным значением и количеством последовательных значений, которые следуют за ним. Затем он преобразуется в строку с помощью string.Join.

string pageNos = "1,2,3,4,8,9,10,15";

// Get list of numbers as ints
var list = pageNos.Split(',').Select(i => Convert.ToInt32(i)).ToList();

// Get a list of numbers and ranges of consecutive numbers
var ranges = new List<Tuple<int, int>>();
int start = 0;

for (int i = 0; i < list.Count; i++)
{
    // First item always starts a new range
    if (i == 0)
    {
        start = list[i];
    }

    // Last item always ends the current range
    if (i == list.Count - 1)
    {
        if (list[i] == list[i - 1] + 1)
        {
            ranges.Add(new Tuple<int, int>(start, list[i] - start));
        }
        else
        {
            ranges.Add(new Tuple<int, int>(start, list[i - 1] - start));
            ranges.Add(new Tuple<int, int>(list[i], 0));
        }
    }

    // End the current range if nonsequential
    if (i > 0 && i < list.Count - 1 && list[i] != list[i - 1] + 1)
    {
        ranges.Add(new Tuple<int, int>(start, list[i - 1] - start));
        start = list[i];
    }
}

// Craete the result string
var result = string.Join(", ", ranges.Select(r => r.Item2 == 0 ? r.Item1.ToString() : string.Format("{0}-{1}", r.Item1, r.Item1 + r.Item2)));

Ответ 8

public static string HyphenateRanges(this string input)
{
    if (string.IsNullOrEmpty(input))
    {
        return "";
    }

    var orderedDistinct = input.Split(',')
                                .Select(Int32.Parse)
                                .Distinct()
                                .OrderBy(x => x)
                                .ToArray();

    Func<int, int, string> replaceRangeValuesWithDash =
        (x, i) =>
        i == 0 || // first
        i == orderedDistinct.Length - 1 || // last
        orderedDistinct[i + 1] - orderedDistinct[i - 1] != 2 // not in a range
            ? x.ToString()
            : "-";

    var rangeValuesDashed = orderedDistinct
        .Select(replaceRangeValuesWithDash)
        .ToList();

    var extraDashesRemoved = rangeValuesDashed
        .Where((x, i) => i == 0 || rangeValuesDashed[i - 1] != x)
        .ToArray();

    var formattedString = String.Join(",", extraDashesRemoved)
                                .Replace(",-,", "-");

    return formattedString;
}

Ответ 9

Используйте этот вспомогательный класс для конвертирования между списками номеров и строк диапазона.

Это копирует реализацию ConvertRangeStringToNumberList() из здесь и ConvertNumberListToRangeString() из здесь с небольшими улучшениями.

using System;
using System.Collections.Generic;
using System.Linq;

public static class NumberRangeHelper
{
    /// <summary>
    /// Converts a string of comma separated list of numbers and ranges to the list of individual numbers it represents.
    /// </summary>
    /// <param name="numbers">Range in form of <c>"2,4-8,11,15-22,39"</c></param>
    /// <returns>A list of numbers</returns>
    public static List<int> ConvertRangeStringToNumberList(string numbers)
    {
        var numbersSplit = numbers.Split(',');
        var convertedNumbers = new SortedSet<int>();
        foreach (var strNumber in numbersSplit)
        {
            int number;
            if (int.TryParse(strNumber, out number))
            {
                convertedNumbers.Add(number);
            }
            else
            {
                // try and delimited by range
                if (strNumber.Contains('-'))
                {
                    var splitRange = strNumber.Split('-');
                    if (splitRange.Length == 2)
                    {
                        int firstNumber;
                        int secondNumber;

                        if (Int32.TryParse(splitRange[0], out firstNumber) &&
                            Int32.TryParse(splitRange[1], out secondNumber))
                        {
                            for (var i = firstNumber; i <= secondNumber; ++i)
                            {
                                convertedNumbers.Add(i);
                            }
                        }
                    }
                }
            }
        }
        return convertedNumbers.ToList();
    }

    /// <summary>
    /// Converts a list of numbers to their concise range representation.
    /// </summary>
    /// <param name="numbers">A list of numbers such as <c>new[] { 1, 2, 3, 4, 5, 12, 13, 14, 19 }</c></param>
    /// <returns>A string like <c>"1-5, 12-14, 19"</c></returns>
    public static string ConvertNumberListToRangeString(IEnumerable<int> numbers)
    {
        var items = new SortedSet<int>(numbers)
            .Select((n, i) => new { number = n, group = n - i })
            .GroupBy(n => n.group)
            .Select(g => (g.Count() >= 3)
                    ? g.First().number + "-" + g.Last().number
                    : String.Join(", ", g.Select(x => x.number))
            )
            .ToList();

        return String.Join(", ", items);
    }

}

Тест:

Action<IEnumerable<int>> DumpList = l => Console.WriteLine("\t[{0}]", String.Join(", ", l));
Action<string> DumpRange = s => Console.WriteLine("\t\"{0}\"", s);

var numbers = new[] { 1, 1, 2, 3, 4, 5, 12, 13, 19, 19, 6, 7 };
DumpList(numbers);
var str = ConvertNumberListToRangeString(numbers);
DumpRange(str);
var list = ConvertRangeStringToNumberList(str);
DumpList(list);

Console.WriteLine();    

str = "1-5, 12, 13, 19, 20, 21, 2-7";
DumpRange(str);
list = ConvertRangeStringToNumberList(str);
DumpList(list);
str = ConvertNumberListToRangeString(list);
DumpRange(str);

Вывод:

[1, 1, 2, 3, 4, 5, 12, 13, 19, 19, 6, 7]
"1-7, 12, 13, 19"
[1, 2, 3, 4, 5, 6, 7, 12, 13, 19]

"1-5, 12, 13, 19, 20, 21, 2-7"
[1, 2, 3, 4, 5, 6, 7, 12, 13, 19, 20, 21]
"1-7, 12, 13, 19-21"