Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert list to number range string

Tags:

string

c#

parsing

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,12:

I will ouput:

1,3,5-10,12

Here is my first attempt. It seems kind of hacky and is probably the worst code I ever wrote. Can you suggest an imporovement\better way to do it?

static string numListToRangeStr(List<int> numList)
{
    StringBuilder retString = new StringBuilder();
    numList.Sort();

    bool inRangeFind = false;
    int firstInRange = numList[0];
    int lastNumber = firstInRange;
    bool first = true;

    for (int i = 1; i < numList.Count; i++)
    {
        if (numList[i] == (lastNumber + 1))
        {
            inRangeFind = true;
        }
        else
        {             
            if (inRangeFind)
            {
                if (!first)
                {
                    retString.Append(",");
                }
                retString.Append(firstInRange);
                retString.Append("-");
            }
            else
            {
               if (!first)
                {
                    retString.Append(",");
                }
            }

            retString.Append(lastNumber);

            firstInRange = numList[i];
            inRangeFind = false;
            first = false;
        }

        lastNumber = numList[i];
    }


    if (inRangeFind)
    {
        if (!first)
        {
            retString.Append(",");
        }
        retString.Append(firstInRange);
        retString.Append("-");
    }
    else
    {
        if (!first)
        {
            retString.Append(",");
        }
    }
    retString.Append(lastNumber);

    return retString.ToString();
}
like image 444
Paligulus Avatar asked Oct 07 '11 14:10

Paligulus


People also ask

How do I convert a string to a number list?

Create an empty list with ints = [] . Iterate over each string element using a for loop such as for element in list . Convert the string to an integer using int(element) and append it to the new integer list using the list. append() method.

How do I convert a list of numbers to a string in Python?

To convert a list to a string, use Python List Comprehension and the join() function. The list comprehension will traverse the elements one by one, and the join() method will concatenate the list's elements into a new string and return it as output.

How do you convert a list into an integer in Python?

Approach #3 : Using map() Another approach to convert a list of multiple integers into a single integer is to use map() function of Python with str function to convert the Integer list to string list. After this, join them on the empty string and then cast back to integer.

Can a string be convert to list Python?

Python String is a sequence of characters. We can convert it to the list of characters using list() built-in function. When converting a string to list of characters, whitespaces are also treated as characters.


2 Answers

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();
    }
  }
}
like image 163
Corey Kosak Avatar answered Sep 30 '22 10:09

Corey Kosak


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
like image 45
Joshua Honig Avatar answered Sep 30 '22 11:09

Joshua Honig