9

Given an array of int values, how could one parse the series into counting sequence notation?

Examples:

{1, 2, 3, 4, 5, 9, 13, 14, 15} -> "1-5,9,13-15"
{4, 6, 8, 10, 11, 12, 15, 17}  -> "4,6,8,10-12,15,17"

I'm looking for a method that would produce these results. This is what I have so far, but I'm very much stumped at this point:

Test Code:

import java.util.Arrays;
public class TestSequencing {
    public static void main(String[] args) {
        int[] numbers1 = {1, 2, 3, 4, 5, 9, 13, 14, 15};
        String numbers1s = "1-5,9,13-15";
        System.out.println(Arrays.toString(numbers1));
        System.out.println("Expected:\t" + numbers1s);
        System.out.println("Produced:\t" + sequenceNums(numbers1) + "\n");

        int[] numbers2 = {3, 5, 6, 9, 12};
        String numbers2s = "3,5-6,9,12";
        System.out.println(Arrays.toString(numbers2));
        System.out.println("Expected:\t" + numbers2s);
        System.out.println("Produced:\t" + sequenceNums(numbers2) + "\n");

        int[] numbers3 = {1, 2, 3, 4, 5, 6, 7};
        String numbers3s = "1-7";
        System.out.println(Arrays.toString(numbers3));
        System.out.println("Expected:\t" + numbers3s);
        System.out.println("Produced:\t" + sequenceNums(numbers3) + "\n");
    }

    public static String sequenceNums(int[] nums) {
        StringBuilder sb = new StringBuilder();
        int rangeStart = nums[0];
        int previous = nums[0];
        int current;
        int expected = previous + 1;

        for (int i = 1 ; i < nums.length ; i++) {
            current = nums[i];
            expected = previous + 1;               
            if (current != expected || i == (nums.length - 1)) {
                if (current == rangeStart) {
                    sb.append(previous + ",");
                } else {
                    sb.append(rangeStart + "-" + previous + ",");
                }                
                rangeStart = current;
            }              
            previous = current;
        }
        if (sb.charAt(sb.length() - 1) == ',') {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }
}

Output:

[1, 2, 3, 4, 5, 9, 13, 14, 15]
Expected:   1-5,9,13-15
Produced:   1-5,9-9,13-14

[3, 5, 6, 9, 12]
Expected:   3,5-6,9,12
Produced:   3-3,5-6,9-9

[1, 2, 3, 4, 5, 6, 7]
Expected:   1-7
Produced:   1-6
  • 4
    @SotiriosDelimanolis Please be constructive with your comments. My intended result is clearly stated, and the sample I provided exemplifies that my code does not produce the results that I am seeking. –  Jul 07 '15 at 01:49
  • I'm asking you why it doesn't do what you wanted. Have you debugged it? What did you find? – Sotirios Delimanolis Jul 07 '15 at 01:51
  • @SotiriosDelimanolis I assume the problem has something to do with missing the last number in the array. I have tried different ideas using other control flow structures, but I am out of ideas as to how it can be done. The sample I have provided is the closest I have come to solving my problem, however, so I posted it here in hope of new ideas from new minds. –  Jul 07 '15 at 01:57
  • I found it a better programming use case. Might be an interview question! – JavaHopper Jul 07 '15 at 02:50
  • @N99x Fixed your code. Let me know if any case fails – JavaHopper Jul 07 '15 at 03:16

5 Answers5

4

Try this:

private static void appendRange(StringBuilder sb, int begin, int end) {
    sb.append(",").append(begin);
    if (end != begin)
        sb.append("-").append(end);
}

public static String sequenceNums(int[] nums) {
    StringBuilder sb = new StringBuilder();
    if (nums.length == 0) return sb.toString();
    int begin = nums[0], end = nums[0];
    for (int cur : nums)
        if (cur - end <= 1)
            end = cur;
        else {
            appendRange(sb, begin, end);
            begin = end = cur;
        }
    appendRange(sb, begin, end);
    return sb.substring(1);
}

@Test
public void testSequenceNums() {
    assertEquals("1-5,9,13-15", sequenceNums(new int[] {1, 2, 3, 4, 5, 9, 13, 14, 15}));
    assertEquals("4,6,8,10-12,15,17", sequenceNums(new int[] {4, 6, 8, 10, 11, 12, 15, 17}));
    assertEquals("1-7", sequenceNums(new int[] {1, 2, 3, 4, 5, 6, 7}));
    assertEquals("", sequenceNums(new int[] {}));
}
1

In the for loop you have two issues:

1) The second if should be if (previous == rangeStart) {

2) You're not dealing with the last number in the loop (where i == (nums.length - 1)).

I would do this with the following code:

public static String sequenceNums(int[] nums) {
    StringBuilder sb = new StringBuilder();

    int rangeStart = nums[0];
    int previous = nums[0];
    int current;
    int expected = previous + 1;
    int size = nums.length;

    for (int i = 1 ; i < size ; i++) {
        current = nums[i];
        expected = previous + 1;

        if (current != expected) {
            addRange(sb, rangeStart, previous);
            rangeStart = current;
        }

        previous = current;
    }
    addRange(sb, rangeStart, nums[size - 1]);

    return sb.toString();
}

private void addRange(StringBuilder sb, int from, int to) {
    if (sb.length() > 0) {
        sb.append(",");
    }
    if (from == to) {
        sb.append(from);
    } else {
        sb.append(from + "-" + to);
    }
}
DuncanKinnear
  • 4,563
  • 2
  • 34
  • 65
1

Here is your fixed code.

public class TestSequencing {

    public static void main(String[] args) {
        int[] numbers1 = {1, 2, 3, 4, 5, 9, 13, 14, 15};
        String numbers1s = "1-5,9,13-15";
        System.out.println(Arrays.toString(numbers1));
        System.out.println("Expected:\t" + numbers1s);
        System.out.println("Produced:\t" + sequenceNums(numbers1) + "\n");

        int[] numbers2 = {3, 5, 6, 9, 12};
        String numbers2s = "3,5-6,9,12";
        System.out.println(Arrays.toString(numbers2));
        System.out.println("Expected:\t" + numbers2s);
        System.out.println("Produced:\t" + sequenceNums(numbers2) + "\n");

        int[] numbers3 = {1, 2, 3, 4, 5, 6, 7};
        String numbers3s = "1-7";
        System.out.println(Arrays.toString(numbers3));
        System.out.println("Expected:\t" + numbers3s);
        System.out.println("Produced:\t" + sequenceNums(numbers3) + "\n");
    }

    public static String sequenceNums(int[] nums) {
        StringBuilder sb = new StringBuilder();
        int rangeStart = nums[0];
        int previous = nums[0];
        int current;
        int expected = previous + 1;

        for (int i = 1 ; i < nums.length ; i++) {
            current = nums[i];
            expected = previous + 1;               
            if (current != expected || i == (nums.length - 1)) {
                if (current == rangeStart) {
                    sb.append(previous + ",");
                } else {
                    if(rangeStart != previous) {
                        if(i == nums.length - 1)
                            sb.append(rangeStart + "-" + current);
                        else
                            sb.append(rangeStart + "-" + previous + ",");
                    } else {
                        if(i == nums.length - 1)
                            sb.append(rangeStart + "," + current);
                        else
                            sb.append(rangeStart + ",");
                    }
                }                
                rangeStart = current;
            }              
            previous = current;
        }
        if (sb.charAt(sb.length() - 1) == ',') {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

}

Problem was, if current is not same as range starting value, you need to check for two cases

i) if range was starting with the same previous value. If so, no need to have same number separated by range (ex: 9-9 doesn't make sense. only 9 does). Another case to be handled is end of array reached. In case end of array is reached, it should just be added at the end even thought it does not fall in any range

ii) other wise, range starts and ends with previous value if end of array is not reached. If end of array is reached that would be end value of range

JavaHopper
  • 5,567
  • 1
  • 19
  • 27
  • Thank you for the explanation! I marked the answer by @saka1029 as accepted because I liked how succinct and efficient his code was, but your answer was much more explanatory and educational. –  Jul 07 '15 at 18:21
1

I took the below approach to represent integer array in ranges.

Note: The numbers should be pre-sorted in ascending order.

We will have two variables start & current, which we will use in the iterations to identify the range. index will be the current index of the array.

Once a range is found, we will keep pushing it into the StringBuilder.

This is the Code:

// We will take this set of integers 
int[] temp = new int[] { 0, 1, 4, 5, 8, 9, 11, 12, 13 };

// Helper variables
Integer start = null, current = null;

// The found range(s) will be stored in this variable.
StringBuilder rangeBuilder = new StringBuilder();

// The current index of the array in iteration
int index = 0;

do {
    // During the first iteration both start & current will be null. So setting the current index value to them.
    if (start == null) {
        start = current = temp[index];
    } else {
        // Checking if the index value is the next number of current.
        if (temp[index] == (current + 1)) {
            current = temp[index];
        } else {
            if (start == current) {
                rangeBuilder.append(start + ",");
            } else {
                rangeBuilder.append(start + "-" + current + ",");

            }
            start = current = temp[index];
        }

    }

    // Checking if we have reached the end of the array.
    if (index + 1 == temp.length) {
        if (start == current) {
            rangeBuilder.append(start);
        } else {
            rangeBuilder.append(start + "-" + current);
        }
    }

} while (index++ < temp.length - 1);

// Printing the range.
System.out.println("Range: " + rangeBuilder.toString());

Explanation:

We are taking this example:

{ 0, 1, 4, 5, 8, 9, 11, 12, 13 }

Note: We will represent start -> s & current -> c

Iteration-1:

Entry: start = null, current = null, rangeBuilder = ""

sc
0,  1,  4,  5,  8,  9,  11,  12, 13
^

Is the index value the next number of current ? No

Has the index reached the last element ? No

Exit: start = 0, current = 0, rangeBuilder = ""

Iteration-2:

Entry: start = 0, current = 0, rangeBuilder = ""

s   c
0,  1,  4,  5,  8,  9,  11,  12, 13
    ^

Is the index value the next number of current ? Yes. So we move c from 0 -> 1.

Has the index reached the last element ? No

Exit: start = 0, current = 1, rangeBuilder = ""

Iteration-3:

Entry: start = 0, current = 1, rangeBuilder = ""

        sc
0,  1,  4,  5,  8,  9,  11,  12, 13
        ^

Is the index value the next number of current ? No. So we push s and c into the String Builder (rangeBuilder). While pushing, we check if s and c are same to avoid duplicates. Then we move s & c to the index value.

Has the index reached the last element ? No

Exit: start = 4, current = 4, rangeBuilder = "0-1,"

Iteration-4:

Entry: start = 4, current = 4, rangeBuilder = "0-1,"

        s   c
0,  1,  4,  5,  8,  9,  11,  12, 13
            ^

Is the index value the next number of current ? Yes. So we move c from 4 -> 5.

Has the index reached the last element ? No

Exit: start = 4, current = 5, rangeBuilder = "0-1,"

Iteration-5:

Entry: start = 4, current = 5, rangeBuilder = "0-1,"

                sc
0,  1,  4,  5,  8,  9,  11,  12, 13
                ^

Is the index value the next number of current ? No. So we push s and c into the String Builder (rangeBuilder). While pushing, we check if s and c are same to avoid duplicates. Then we move s & c to the index value.

Has the index reached the last element ? No

Exit: start = 8, current = 8, rangeBuilder = "0-1,4-5,"

Iteration-6:

Entry: start = 8, current = 8, rangeBuilder = "0-1,4-5,"

                s   c
0,  1,  4,  5,  8,  9,  11,  12, 13
                    ^

Is the index value the next number of current ? Yes. So we move c from 8 -> 9.

Has the index reached the last element ? No

Exit: start = 8, current = 9, rangeBuilder = "0-1,4-5,"

Iteration-7:

Entry: start = 8, current = 9, rangeBuilder = "0-1,4-5,"

                        sc
0,  1,  4,  5,  8,  9,  11,  12, 13
                        ^

Is the index value the next number of current ? No. So we push s and c into the String Builder (rangeBuilder). While pushing, we check if s and c are same to avoid duplicates. Then we move s & c to the index value.

Has the index reached the last element ? No

Exit: start = 11, current = 11, rangeBuilder = "0-1,4-5,8-9,"

Iteration-8:

Entry: start = 11, current = 11, rangeBuilder = "0-1,4-5,8-9,"

                        s    c
0,  1,  4,  5,  8,  9,  11,  12, 13
                             ^

Is the index value the next number of current ? Yes. So we move c from 11 -> 12.

Has the index reached the last element ? No

Exit: start = 11, current = 12, rangeBuilder = "0-1,4-5,8-9,"

Iteration-9:

Entry: start = 11, current = 12, rangeBuilder = "0-1,4-5,8-9,"

                        s        c
0,  1,  4,  5,  8,  9,  11,  12, 13
                                 ^

Is the index value the next number of current ? Yes. So we move c from 12 -> 13.

Has the index reached the last element ? Yes. So we push s and c into the String Builder (rangeBuilder). While pushing, we check if s and c are same to avoid duplicates.

End of iteration. The String Builder (rangerBuilder) will have this value: 0-1,4-5,8-9,11-13

Feel free to improve this code :)

Sathieswar
  • 11
  • 2
0

I was able to solve your problem by introducing a boolean flag and reworking the method to test for consecutive numbers. If the current and next numbers are consecutive, inRangeFlag is triggered for the next iteration. See the code comments below for further breakdown:

   public static String sequenceNums(int[] nums) {

    StringBuilder sb = new StringBuilder();
    int current;
    int next;
    boolean inRangeFlag = false;

    for (int i = 0; i < nums.length; i++) {

        current = nums[i];

        // TRUE: if element is not last element, because last number is
        // always appended.
        if (i < nums.length - 1) {

            next = nums[i + 1];

            // TRUE: if current element and next are consecutive
            if (next - current == 1) {

                // If rangeflag is false, the current number is the start
                // of a range. Append the number with hyphen.
                if (!inRangeFlag) {
                    sb.append(current + "-");
                }

                // Trigger inRange Flag for next iteration.
                inRangeFlag = true;

            } else {
                sb.append(current + ",");
                inRangeFlag = false; // Turn flag false because not inRange.
            }
        } else {
            sb.append(current);
        }
    }
    return sb.toString();
}
AngelTrs
  • 236
  • 1
  • 8