1

Suppose I have some sorted lists of integers and I want to convert them to their respective regex digit ranges, like so:

  1. [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] => [0-9]
  2. [0, 1, 2, 3, 4, 6, 7, 8, 9] => [0-46-9]
  3. [0, 1, 3, 4, 5, 8, 9] => [013-589]
  4. [0, 2, 4, 6, 8] => [02468]

I am not trying to regex match anything here. I am trying to generate a regex range from a set of digits.

I am really just looking to see if there is already some de facto algorithm for doing something like this.

Edit: Based on @Jerry_Coffin's answer, a Java-based algorithm:

List<Integer> digits = Arrays.asList(0, 1, 3, 4, 5, 8, 9);
StringBuilder digitRange = new StringBuilder().append('[');
int consecutive = 0;
for (int i = 0; i < digits.size(); i++) {
  if (i == digits.size() - 1 || digits.get(i) + 1 != digits.get(i + 1)) {
    if (consecutive > 1) {
        digitRange.append('-');
    }
    digitRange.append(digits.get(i));
    consecutive = 0;
  } else {
    if (consecutive == 0) {
      digitRange.append(digits.get(i));
    }
    consecutive++;
  }
}
digitRange.append(']');
System.out.println(digitRange.toString());

Output: [013-589]

Feel free to find improvements or problems.

Random Human
  • 946
  • 1
  • 14
  • 31

2 Answers2

3

Presumably you're starting from sorted input (if not, you almost certainly want to start by sorting the input).

From there, start from the first (unprocessed) item, write it out. Walk through the numbers as long as they're consecutive. Assuming you get more than two consecutive, write out a dash then the last of the consecutive numbers. If you got two or fewer consecutive, just write them to output as-is.

Repeat until you reach the end of the input.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

I can propose a different approach.

Iterate through the list identifying intervals. We keep two variables left and right (interval bounds) and each time we have two not consecutive values we write the interval to a StringBuilder.

int[] list = new[] { 0, 1, 3, 4, 5, 8, 9 };
int left = 0;
int right = 0;
for (int i = 0; i < list.Length; i++)
{
    if (i == 0) // first case
    {
        left = right = list[i];
        continue;
    }
    if (list[i] - list[i - 1] > 1) // not consecutive
    {
        builder.AppendFormat(Write(left, right));
        left = list[i];
    }
    right = list[i];
}
builder.AppendFormat(Write(left, right));// last case
builder.Append("]");

The write method:

private static string Write(int left, int right)
{
    return
        left == right
            ? left.ToString()
        : right - left == 1
            ? string.Format("{0}{1}", left, right)
            : string.Format("{0}-{1}", left, right);
}
polkduran
  • 2,533
  • 24
  • 34