单独的数字范围,如果按顺序,然后是连字符,如果顺序中断,则发生逗号字符

时间:2012-11-29 14:38:41

标签: c# algorithm

我有一个表示页面nos的字符串,如1,2,3,4,8,9,10,15

我希望将它显示为1-4,8-10,15,即序列中的数字用连字符分隔,连字符由最小和最大数字依次包围。

如果按顺序中断,范围将用逗号分隔。

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);

9 个答案:

答案 0 :(得分:8)

我认为循环内循环让事情变得更加混乱。尝试只使用一个循环,因为您只需要遍历整个列表一次。

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

答案 1 :(得分:4)

一点点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));

答案 2 :(得分: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的原始字符串 - &gt; int,请参阅JonB关于更清晰方法的评论。

答案 3 :(得分:3)

您可以使用此方法获取相邻的数字组,其中每个组由自定义Range - 类表示:

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

答案 4 :(得分:2)

以下JS代码也有帮助

  1. 删除重复项
  2. 排序数组
  3. 使用2个指针
  4. 从同一地点开始
  5. 前进第二个指针,直到其下一个元素是后继元素
  6. 在i和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(",")));

答案 5 :(得分:1)

我不是C#的人,但我猜这里有问题:

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

你不应该在结果中添加它。但设置一个标志(可能是布尔值),表示现在我开始计数了。

如果标志== ture且数字不再连续,那么就是添加到结果中的时间,当然是&#34; - &#34;。

答案 6 :(得分:1)

这是一个不同的解决方案,它创建一个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)));

答案 7 :(得分:1)

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;
}

答案 8 :(得分:1)

使用此助手类在数字列表和范围字符串之间来回转换。

这会复制来自hereConvertNumberListToRangeString()和来自hereusing 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"

输出:

x