badjr13
badjr13

Reputation: 25

How to transform a list of numbers into a list of ranges of consecutive numbers

I'm generating a list of numbers:

[1,2,3,4,6,7,8,9,11,12,13,14,16,17,18,19]

Note how some numbers are missing (in this case, every 5th number). I want to transform consecutive numbers into ranges, delimited by a dash.

In the above case, I'd like the output to be

"1-4,6-9,11-14,16-20"

How can I go about solving this problem?

Upvotes: 1

Views: 1105

Answers (2)

charlietfl
charlietfl

Reputation: 171669

Solution returns completed string and handles isolated numbers that aren't part of a range

function convert(input) {
    var res = [],
        arr = typeof input == 'string' ? input.split(',').map(Number) : input;
    while (arr.length) {
        var curr = arr.shift(),
            lastIncIdx = null;

        if (arr.length && curr == arr[0] - 1) {
            var next = arr.length ? arr.reduce(function (last, curr, idx, arr) {
                if (curr == last + 1) {
                    lastIncIdx = idx;
                    return curr;
                } else {
                    return last;
                }
            }) : curr;

            if (next != curr) {
                arr.splice(0, lastIncIdx + 1);
                res.push(curr + '-' + next)
            }
        } else {
            res.push(curr)
        }
    }
    return res.join();
}

DEMO

Upvotes: 1

royhowie
royhowie

Reputation: 11171

var convertToRanges = function (str) {
    // split the string at the commas and map it to an array of ints
    // NOTE: if you are passing an array, skip this step
    var pieces = str.split(",").map(Number)
    // ranges will be an array of arrays
    // each inner array will have 2 dimensions, representing the start/end
    // of a range
    // we want to initialize our first range to pieces[0], pieces[0],
    // or (only the first element)
      , ranges = [[pieces[0], pieces[0]]]
    // last index we accessed (so we know which range to update)
      , lastIndex = 0;

    for (var i = 1; i < pieces.length; i++) {
        // if the current element is 1 away from the end of whichever range
        // we're currently in
        if (pieces[i] - ranges[lastIndex][1] === 1) {
            // update the end of that range to be this number
            ranges[lastIndex][1] = pieces[i];
        } else {
            // otherwise, add a new range to ranges
            ranges[++lastIndex] = [pieces[i], pieces[i]];
        }
    }
    return ranges;
}

This will return an array of arrays:

console.log(convertToRanges("1,2,3,4,6,7,8,9,11,12,13,14,16,17,18,19"));
// -> [ [1, 4], [6, 9], [11, 14], [16, 19] ]

I'll leave it to you to figure out how to transform this to look like "1-4,6-9,11-14,16-20"

Hint: use Array.prototype.map and Array.prototype.join

Upvotes: 7

Related Questions