views:

340

answers:

9

In javascript how to convert sequence of numbers in an array to range of numbers?

eg. [2,3,4,5,10,18,19,20] to [2-5,10,18-20]

A: 

If you simply want a string that represents a range, then you'd find the mid-point of your sequence, and that becomes your middle value (10 in your example). You'd then grab the first item in the sequence, and the item that immediately preceded your mid-point, and build your first-sequence representation. You'd follow the same procedure to get your last item, and the item that immediately follows your mid-point, and build your last-sequence representation.

// Provide initial sequence
var sequence = [1,2,3,4,5,6,7,8,9,10];
// Find midpoint
var midpoint = Math.ceil(sequence.length/2);
// Build first sequence from midpoint
var firstSequence = sequence[0] + "-" + sequence[midpoint-2];
// Build second sequence from midpoint
var lastSequence  = sequence[midpoint] + "-" + sequence[sequence.length-1];
// Place all new in array
var newArray = [firstSequence,midpoint,lastSequence];

alert(newArray.join(",")); // 1-4,5,6-10

Demo Online: http://jsbin.com/uvahi/edit

Jonathan Sampson
Wouldn't the output be 1-10, since the numbers 1-10 appear in sequence with none missing?
Nick Presta
A: 

You could iterate over the numbers and see if the next number is 1 bigger then the current number. So have a:

struct range {
    int start;
    int end;
} range;

where if array[i+1] == array[i]+1; (where i is the currently observed number) then range.end = array[i+1];. Then you progress to the next i; If array[i+1] != array[i]+1; then range.end = array[i];

you could store the ranges in a vector< range > ranges;

printing would be easy:

for(int i = 0; i < ranges.size(); i++) {
    range rng = (range)ranges.at(i);
    printf("[%i-%i]", rng.start, rng.end);
}
ArtemT
A: 
 ; For all cells of the array
    ;if current cell = prev cell + 1 -> range continues
    ;if current cell != prev cell + 1 -> range ended

int[] x  = [2,3,4,5,10,18,19,20]
string output = '['+x[0]
bool range = false; --current range
for (int i = 1; i > x[].length; i++) {
  if (x[i+1] = [x]+1) {
    range = true;
  } else { //not sequential
  if range = true
     output = output || '-' 
  else
     output = output || ','
  output.append(x[i]','||x[i+1])
  range = false;
  } 

}

Something like that.

glasnt
+4  A: 

Here is an algorithm that I made some time ago, originally written for C#, now I ported it to JavaScript:

function getRanges(array) {
  var ranges = [], rstart, rend;
  for (var i = 0; i < array.length; i++) {
    rstart = array[i];
    rend = rstart;
    while (array[i + 1] - array[i] == 1) {
      rend = array[i + 1]; // increment the index if the numbers sequential
      i++;
    }
    ranges.push(rstart == rend ? rstart+'' : rstart + '-' + rend);
  }
  return ranges;
}

getRanges([2,3,4,5,10,18,19,20]);
// returns ["2-5", "10", "18-20"]
getRanges([1,2,3,5,7,9,10,11,12,14 ]);
// returns ["1-3", "5", "7", "9-12", "14"]
getRanges([1,2,3,4,5,6,7,8,9,10])
// returns ["1-10"]
CMS
would suggest sorting the values first, so you can deal with mixed values like: [1,3,2,6,5,7]
Tracker1
+1  A: 

Here is a version for Perl:

use strict;
use warnings;

my @numbers = (0,1,3,3,3,4,4,7,8,9,12, 14, 15, 19, 35, 35, 37, 38, 38, 39);
@numbers =  sort {$a <=> $b} @numbers ; # Make sure array is sorted.

# Add "infinity" to the end of the array.
$numbers[1+$#numbers] = undef ;

my @ranges = () ; # An array where the range strings are stored.

my $start_number = undef ;
my $last_number  = undef ;
foreach my $current_number (@numbers)
{
  if (!defined($start_number))
  {
    $start_number = $current_number ;
    $last_number  = $current_number ;
  }
  else
  {
    if (defined($current_number) && (($last_number + 1) >= $current_number))
    {
      $last_number = $current_number ;
      next ;
    }
    else
    {
      if ($start_number == $last_number)
      {
        push(@ranges, $start_number) ;
      } 
      else
      {
        push(@ranges, "$start_number-$last_number") ;
      }
      $start_number = $current_number ;
      $last_number  = $current_number ;
    }
  }
}

# Print the results
print join(", ", @ranges) . "\n" ; 
# Returns "0-1, 3-4, 7-9, 12, 14-15, 19, 35, 37-39"
rlandster
A: 

Just having fun with solution from CMS :

  function getRanges (array) {
    for (var ranges = [], rend, i = 0; i < array.length;) {
      ranges.push ((rend = array[i]) + ((function (rstart) {
        while (++rend === array[++i]);
        return --rend === rstart;
      })(rend) ? '' : '-' + rend)); 
    }
    return ranges;
  }
Hans B PUFAL
A: 

Here's my take on this...

function getRanges(input) {

  //setup the return value
  var ret = [], ary, first, last;

  //copy and sort
  var ary = input.concat([]);
  ary.sort(function(a,b){
    return Number(a) - Number(b);
  });

  //iterate through the array
  for (var i=0; i<ary.length; i++) {
    //set the first and last value, to the current iteration
    first = last = ary[i];

    //while within the range, increment
    while (ary[i+1] == last+1) {
      last++;
      i++;
    }

    //push the current set into the return value
    ret.push(first == last ? first : first + "-" + last);
  }

  //return the response array.
  return ret;
}
Tracker1
A: 

I was just looking for this exact thing. I needed a PHP version so ported CMS's solution. Here it is, for anyone who stops by this question looking for the same thing:

function getRanges( $nums )
{
    $ranges = array();

    for ( $i = 0, $len = count($nums); $i < $len; $i++ )
    {
        $rStart = $nums[$i];
        $rEnd = $rStart;
        while ( isset($nums[$i+1]) && $nums[$i+1]-$nums[$i] == 1 )
            $rEnd = $nums[++$i];

        $ranges[] = $rStart == $rEnd ? $rStart : $rStart.'-'.$rEnd;
    }

    return $ranges;
}
DisgruntledGoat