Как преобразовать последовательность чисел в массиве в диапазон чисел - PullRequest
24 голосов
/ 16 февраля 2010

В javascript, как преобразовать последовательность чисел в массиве в диапазон чисел?

например. [2,3,4,5,10,18,19,20] до [2-5,10,18-20]

Ответы [ 21 ]

27 голосов
/ 16 февраля 2010

Вот алгоритм, который я сделал некоторое время назад , изначально написанный для C #, теперь я перенес его на 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"]
5 голосов
/ 16 февраля 2010

Просто весело с решением от 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;
  }
5 голосов
/ 30 марта 2010

Я просто искал именно эту вещь. Мне нужна была версия PHP, поэтому портировал решение CMS. Вот тот, кто останавливается на этом вопросе и ищет то же самое:

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;
}
5 голосов
/ 04 ноября 2010

Я нашел этот ответ полезным, но мне нужна версия Python:

def GroupRanges(items):
  """Yields 2-tuples of (start, end) ranges from a sequence of numbers.

  Args:
    items: an iterable of numbers, sorted ascendingly and without duplicates.

  Yields:
    2-tuples of (start, end) ranges.  start and end will be the same
    for ranges of 1 number
  """
  myiter = iter(items)
  start = myiter.next()
  end = start
  for num in myiter:
    if num == end + 1:
      end = num
    else:
      yield (start, end)
      start = num
      end = num
  yield (start, end)


numbers = [1, 2, 3, 5, 6, 7, 8, 9, 10, 20]
assert [(1, 3), (5, 10), (20, 20)] == list(GroupRanges(numbers))
assert [(1, 1)] == list(GroupRanges([1]))
assert [(1, 10)] == list(GroupRanges([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
3 голосов
/ 29 марта 2015

Очень хороший вопрос: вот моя попытка:

function ranges(numbers){
    var sorted = numbers.sort(function(a,b){return a-b;});
    var first = sorted.shift();
    return sorted.reduce(function(ranges, num){
        if(num - ranges[0][1] <= 1){
            ranges[0][1] = num;        
        } else {
            ranges.unshift([num,num]);
        }
        return ranges;
    },[[first,first]]).map(function(ranges){
        return ranges[0] === ranges[1] ? 
            ranges[0].toString() : ranges.join('-');
    }).reverse();
}

Демонстрация на JSFiddler

1 голос
/ 16 февраля 2010

Вот мой взгляд на это ...

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;
}
1 голос
/ 16 февраля 2010

Вот версия для 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"
1 голос
/ 16 февраля 2010

Если вам просто нужна строка, представляющая диапазон, тогда вы найдете среднюю точку вашей последовательности, и это станет вашим средним значением (10 в вашем примере). Затем вы бы захватили первый элемент в последовательности и элемент, который непосредственно предшествовал вашей средней точке, и построили ваше представление первой последовательности. Вы будете следовать той же процедуре, чтобы получить свой последний элемент и элемент, который следует сразу за вашей средней точкой, и построить представление последней последовательности.

// 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

Демо Онлайн: http://jsbin.com/uvahi/edit

1 голос
/ 12 января 2014

Вот порт кода CMS для BASH:

#!/usr/bin/env bash
# vim: set ts=3 sts=48 sw=3 cc=76 et fdm=marker: # **** IGNORE ******
get_range() { RANGE= # <-- OUTPUT                  **** THIS   ******
   local rstart rend i arr=( "$@" )  # ported from **** JUNK   ******
   for (( i=0 ; i < $# ; i++ )); do  # http://stackoverflow.com
      (( rstart = arr[i] ))          # /a/2270987/912236
      rend=$rstart; while (( arr[i+1] - arr[i] == 1 )); do
      (( rend = arr[++i] )); done; (( rstart == rend )) &&
   RANGE+=" $rstart" || RANGE+=" $rstart-$rend"; done; } # }}}
1 голос
/ 13 июля 2012

In C #

    public string compressNumberRange(string inputSeq)
    {
        //Convert String array to long List and removing the duplicates
        List<long> longList = inputSeq.Split(',').ToList().ConvertAll<long>(s => Convert.ToInt64(s)).Distinct().ToList();

        //Sort the array
        longList.Sort();

        StringBuilder builder = new StringBuilder();


        for (int itr = 0; itr < longList.Count(); itr++)
        {
            long first = longList[itr];
            long end = first;

            while (longList[itr + 1] - longList[itr] == 1) //Seq check 
            {
                end = longList[itr + 1];
                itr++;
                if (itr == longList.Count() - 1)
                    break;
            }
            if (first == end) //not seq
                builder.Append(first.ToString() + ",");
            else //seq
                builder.Append(first.ToString() + "-" + end.ToString() + ",");
        }

        return builder.ToString();
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...