Convert list to number range string

后端 未结 6 581
你的背包
你的背包 2020-12-05 19:45

This question is pretty much the opposite of this question: Does C# have built-in support for parsing page-number strings?

So given

1,3,5,6,7,8,9,10         


        
相关标签:
6条回答
  • 2020-12-05 20:32

    This is an old thread, but here's a new answer. I've constructed it as an extension method. This returns the array of ranges, where each 'range' is either a single number ('13') or a pair of numbers ('5-12'):

    public static class EnumExt {
        public static string[] ToRanges(this List<int> ints) {
            if (ints.Count < 1) return new string[] { };
            ints.Sort();
            var lng = ints.Count;
            var fromnums = new List<int>();
            var tonums = new List<int>();
            for (var i = 0; i < lng - 1; i++) {
                if (i == 0)
                    fromnums.Add(ints[0]);
                if (ints[i + 1] > ints[i] + 1) {
                    tonums.Add(ints[i]);
                    fromnums.Add(ints[i + 1]);
                }
            }
            tonums.Add(ints[lng - 1]);
            return Enumerable.Range(0, tonums.Count).Select(
                i => fromnums[i].ToString() +
                    (tonums[i] == fromnums[i] ? "" : "-" + tonums[i].ToString())
            ).ToArray();
        }
    }
    

    If you wish to join them, just use built-in string.Join:

    var intlist = new List<int>() { 1, 2, 3, 6, 7, 8, 9, 10, 14 };
    Console.WriteLine(string.Join(", ", intlist.ToRanges()));
    // Prints: 1-3, 6-10, 14
    
    0 讨论(0)
  • 2020-12-05 20:34

    Had to solve same problem. Was finding alternatives to my solution which I think looks more logical. Therefore sharing it. Set second parameter to true if you want to sort an unsorted list.

    public string ToRangeString(List<int> list, bool withSort) {
      list = list.Distinct().ToList();
      if(withSort) list.Sort();
    
      StringBuilder result = new StringBuilder();
      int temp;
    
      for (int i=0; i<list.Count(); i++) {
        temp = list[i];
    
        //add a number
        result.Append(list[i]);
    
        //skip number(s) between a range
        while(i<list.Count()-1 && list[i+1] == list[i]+1)
          i++;
    
        //add the range
        if(temp != list[i])
          result.Append("-").Append(list[i]);
    
        //add comma
        if(i != list.Count()-1)
          result.Append(", ");
    
      }
      return result.ToString();
    }
    
    0 讨论(0)
  • 2020-12-05 20:39

    Here a slightly modifed Version of RedFilter's version.

    It returns a String instead of an Array of Strings, it Removes 0 ,if in the list, it avoids the Exception if only one Value is in the List.

     public static string ToRanges(this List<int> ints)
        {
            ints.Remove(0); // Note: Remove this if you like to include the Value 0
            if (ints.Count < 1) return "";
            ints.Sort();
            var lng = ints.Count;
            if (lng == 1)
                return ints[0].ToString();
    
            var fromnums = new List<int>();
            var tonums = new List<int>();
            for (var i = 0 ; i < lng - 1 ; i++)
            {
                if (i == 0)
                    fromnums.Add(ints[0]);
                if (ints[i + 1] > ints[i] + 1)
                {
                    tonums.Add(ints[i]);
                    fromnums.Add(ints[i + 1]);
                }
            }
            tonums.Add(ints[lng - 1]);
    
    
            string[] ranges = Enumerable.Range(0, tonums.Count).Select(
                i => fromnums[i].ToString() +
                    (tonums[i] == fromnums[i] ? "" : "-" + tonums[i].ToString())
            ).ToArray();
    
            if (ranges.Length == 1)
                return ranges[0];
            else
                return String.Join(",", ranges);
        }
    
    0 讨论(0)
  • 2020-12-05 20:43

    This should work pretty well, not tested for all cases though.

            string s = "1,2,3,4,5,7,8,9,10,11,12,13";
            string[] ints = s.Split(',');
            StringBuilder result = new StringBuilder();
    
            int num, last = -1;
            bool dash = false;
    
            for (int ii = 0; ii < ints.Length; ii++)
            {
                num = Int32.Parse(ints[ii]);
    
                if (num - last > 1)
                {
                    if (dash)
                    {
                        result.Append(last);
                        dash = false;
                    }
                    if (result.Length > 0)
                    {
                        result.Append(",");
                    }
                    result.Append(num);                    
                }
                else
                {
                    if (dash == false)
                    {
                        result.Append("-");
                        dash = true;
                    }
                }
    
                last = num;
    
                if (dash && ii == ints.Length - 1)
                {
                    result.Append(num);
                }
            }
    
            Console.WriteLine(result);
    
    0 讨论(0)
  • 2020-12-05 20:49

    When something has several moving parts like this, I think it helps to decompose it into little logical units and then combine them together. The little logical units might even be usable separately. The code below breaks the problem down into:

    • turning the heterogeneous set of sequential and nonsequential numbers into a homogenous set of ranges (possibly including "degenerate" ranges which start and end at the same number)
    • a way to "pretty-print" such ranges: (x,y) prints as "x-y"; (x,x) prints as "x"
    • a way to interperse a separator between elements of an enumerable, and convert the result into a string.

    The program is:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication37 {
      public static class Program {
        static void Main(string[] args) {
          var numList=new[] {1, 3, 5, 6, 7, 8, 9, 10, 12};
          Console.WriteLine(numListToPossiblyDegenerateRanges(numList).Select(r => PrettyRange(r)).Intersperse(","));
        }
    
        /// <summary>
        /// e.g. 1,3,5,6,7,8,9,10,12
        /// becomes
        /// (1,1),(3,3),(5,10),(12,12)
        /// </summary>
        public static IEnumerable<Tuple<int,int>> numListToPossiblyDegenerateRanges(IEnumerable<int> numList) {
          Tuple<int, int> currentRange=null;
          foreach(var num in numList) {
            if(currentRange==null) {
              currentRange=Tuple.Create(num, num);
            } else if(currentRange.Item2==num-1) {
              currentRange=Tuple.Create(currentRange.Item1, num);
            } else {
              yield return currentRange;
              currentRange=Tuple.Create(num, num);
            }
          }
          if(currentRange!=null) {
            yield return currentRange;
          }
        }
    
        /// <summary>
        /// e.g. (1,1) becomes "1"
        /// (1,3) becomes "1-3"
        /// </summary>
        /// <param name="range"></param>
        /// <returns></returns>
        public static string PrettyRange(Tuple<int,int> range) {
          if(range.Item1==range.Item2) {
            return range.Item1.ToString();
          }
          return string.Format("{0}-{1}", range.Item1, range.Item2);
        }
    
        public static string Intersperse(this IEnumerable<string> items, string interspersand) {
          var currentInterspersand="";
          var result=new StringBuilder();
          foreach(var item in items) {
            result.Append(currentInterspersand);
            result.Append(item);
            currentInterspersand=interspersand;
          }
          return result.ToString();
        }
      }
    }
    
    0 讨论(0)
  • 2020-12-05 20:50

    I know this is an old thread but thought I'd share my approach. This generates a list of ranges which can be easily converted to a single string.

    var numbers = new List<int>() { 1, 3, 5, 6, 7, 8, 9, 10, 12 };
    var ranges = new List<string>();
    
    if (numbers.Count == 0)
        return ranges;
    
    numbers = numbers.Distinct().ToList();
    numbers.Sort();
    
    int start = numbers[0];
    string range = start.ToString();
    
    for (int i = 1; i <= numbers.Count; i++)
    {
        if (i < numbers.Count && numbers[i] == numbers[i - 1] + 1)
        {
            range = $"{start} - {numbers[i]}";
            continue;
        }
    
        ranges.Add(range);
    
        if (i < numbers.Count)
        {
            start = numbers[i];
            range = start.ToString();
        }
    }
    
    var rangeString = string.Join(", ", ranges);  // Outputs: "1, 3, 5 - 10, 12"
    
    0 讨论(0)
提交回复
热议问题