Подтвердить что ты не робот

Как преобразовать последовательность чисел в массив в диапазон чисел

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

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

4b9b3361

Ответ 1

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

Ответ 2

Просто весело с решением от 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;
  }

Ответ 3

Я просто искал эту вещь. Мне нужна была версия 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;
}

Ответ 4

Я нашел этот ответ полезным, но вам нужна версия 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]))

Ответ 5

Если вам просто нужна строка, представляющая диапазон, то вы найдете среднюю точку своей последовательности, и это станет вашим средним значением (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

Ответ 6

Вот версия для 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"

Ответ 7

В С#

    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();
    }

Ответ 8

Вот порт кода 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=( "[email protected]" )  # 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; } # }}}

Ответ 9

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

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

Ответ 10

Вы можете перебирать числа и посмотреть, будет ли следующее число больше, чем текущее число. Так что:

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

где if array[i+1] == array[i]+1; (где я - наблюдаемое в настоящее время число) то range.end = array[i+1];. Затем вы переходите к следующему i; Если array[i+1] != array[i]+1;, то range.end = array[i];

вы можете сохранить диапазоны в vector< range > ranges;

печать будет простой:

for(int i = 0; i < ranges.size(); i++) {
    range rng = (range)ranges.at(i);
    printf("[%i-%i]", rng.start, rng.end);
}

Ответ 11

 ; 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;
  } 

}

Что-то вроде этого.

Ответ 12

Вот мой пример...

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;
}

Ответ 13

PHP

function getRanges($nums) {
sort($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;
}


echo print_r(getRanges(array(2,21,3,4,5,10,18,19,20)));
echo print_r(getRanges(array(1,2,3,4,5,6,7,8,9,10)));

Ответ 14

import java.util.ArrayList;
import java.util.Arrays;



public class SequencetoRange {

    /**
     * @param args
     */
    public static void main(String[] args) {
    // TODO Auto-generated method stub

        int num[] = {1,2,3,63,65,66,67,68,69,70,80,90,91,94,95,4,101,102,75,76,71};

        int l = num.length;
        int i;
        System.out.print("Given number : ");
        for (i = 0;i < l;i++ ){
            System.out.print("  " + num[i]);
        }
        System.out.println("\n");
        Arrays.sort(num);

        ArrayList newArray = new ArrayList();
        newArray = getRanges(num);
        System.out.print("Range : ");
        for(int y=0;y<newArray.size();y++)
        {
            System.out.print(" " +newArray.get(y));
        }
    }

    public static ArrayList getRanges(int num[])
    {  
        ArrayList ranges = new ArrayList();
        int rstart, rend;   
        int lastnum = num[num.length-1];
        for (int i = 0; i < num.length-1; i++) 
        {     
            rstart = num[i];     
            rend = rstart;     
            while (num[i + 1] - num[i] == 1) 
            {       
                rend = num[i + 1]; 
                // increment the index if the numbers sequential       
                if(rend>=lastnum)
                {
                    break;
                }
                else
                {
                    i++;
                }  
            }  
            if(rstart==rend)
            {
                ranges.add(rend);
            }
            else
            {
                ranges.add(+rstart+"..."+rend);
            }
        } 
        return ranges; 
    } 
}

Ответ 15

Я написал свой собственный метод, который зависит от Lo-Dash, но не просто возвращает вам массив диапазонов, а скорее возвращает массив групп диапазонов.

[1,2,3,4,6,8,10] становится:

[[1,2,3,4],[6,8,10]]

http://jsfiddle.net/mberkom/ufVey/

Ответ 16

Адаптация CMS javascript solution для Cold Fusion

Сначала он сортирует список, чтобы 1,3,2,4,5,8,9,10 (или аналогичный) правильно преобразовывал в 1-5,8-10.

<cfscript>
    function getRanges(nArr) {
        arguments.nArr = listToArray(listSort(arguments.nArr,"numeric"));
        var ranges = [];
        var rstart = "";
        var rend = "";
        for (local.i = 1; i <= ArrayLen(arguments.nArr); i++) {
            rstart = arguments.nArr[i];
            rend = rstart;
            while (i < ArrayLen(arguments.nArr) and (val(arguments.nArr[i + 1]) - val(arguments.nArr[i])) == 1) {
                rend = val(arguments.nArr[i + 1]); // increment the index if the numbers sequential
                i++;
            }
            ArrayAppend(ranges,rstart == rend ? rstart : rstart & '-' & rend);
        }
        return arraytolist(ranges);
    }
</cfscript>

Ответ 17

Вот что я собрал в Свифт. Он устраняет дубликаты и сортирует массив сначала, и не имеет против, если он дал пустой массив или массив из них.

func intArrayToString(array: [Int]) -> String {
    var intArray = Array(Set(array))
    intArray.sortInPlace()
    if intArray.count == 0 {
        return ""
    }
    var intString = "\(intArray[0])"
    if intArray.count > 1 {
        for j in 1..<intArray.count-1 {
            if intArray[j] == intArray[j-1]+1 {
                if intArray[j] != intArray[j+1]-1 {
                    intString += "-\(intArray[j])"
                }
            } else {
                intString += ",\(intArray[j])"
            }
        }
        if intArray.last! == intArray[intArray.count-2]+1 {
            intString += "-\(intArray.last!)"
        } else {
            intString += ",\(intArray.last!)"
        }
    }
    return intString
}

Ответ 18

Крошечный модуль ES6 для вас, ребята. Он принимает функцию, чтобы определить, когда мы должны разорвать последовательность (параметр breakDetectorFunc - по умолчанию - это простая вещь для ввода целочисленной последовательности). УВЕДОМЛЕНИЕ: поскольку ввод является абстрактным - нет автоматической сортировки перед обработкой, поэтому, если ваша последовательность не отсортирована - сделайте это до вызова этого модуля

function defaultIntDetector(a, b){
    return Math.abs(b - a) > 1;
}

/**
 * @param {Array} valuesArray
 * @param {Boolean} [allArraysResult=false] if true - [1,2,3,7] will return [[1,3], [7,7]]. Otherwise [[1.3], 7]
 * @param {SequenceToIntervalsBreakDetector} [breakDetectorFunc] must return true if value1 and value2 can't be in one sequence (if we need a gap here)
 * @return {Array}
 */
const sequenceToIntervals = function (valuesArray, allArraysResult, breakDetectorFunc) {
    if (!breakDetectorFunc){
        breakDetectorFunc = defaultIntDetector;
    }
    if (typeof(allArraysResult) === 'undefined'){
        allArraysResult = false;
    }

    const intervals = [];
    let from = 0, to;
    if (valuesArray instanceof Array) {
        const cnt = valuesArray.length;
        for (let i = 0; i < cnt; i++) {
            to = i;
            if (i < cnt - 1) { // i is not last (to compare to next)
                if (breakDetectorFunc(valuesArray[i], valuesArray[i + 1])) {
                    // break
                    appendLastResult();
                }
            }
        }
        appendLastResult();
    } else {
        throw new Error("input is not an Array");
    }

    function appendLastResult(){
        if (isFinite(from) && isFinite(to)) {
            const vFrom = valuesArray[from];
            const vTo = valuesArray[to];

            if (from === to) {
                intervals.push(
                    allArraysResult
                        ? [vFrom, vTo] // same values array item
                        : vFrom // just a value, no array
                );
            } else if (Math.abs(from - to) === 1) { // sibling items
                if (allArraysResult) {
                    intervals.push([vFrom, vFrom]);
                    intervals.push([vTo, vTo]);
                } else {
                    intervals.push(vFrom, vTo);
                }
            } else {
                intervals.push([vFrom, vTo]); // true interval
            }
            from = to + 1;
        }
    }

    return intervals;
};

module.exports = sequenceToIntervals;

/** @callback SequenceToIntervalsBreakDetector
 @param value1
 @param value2
 @return bool
 */

Первый аргумент - это отсортированный массив входных последовательностей, второй - логический флаг, управляющий режимом вывода: если true - единый элемент (вне интервалов) будет возвращен как массивы: [1,7], [9,9], [10,10], [12,20], в противном случае отдельные элементы возвращаются, как они появляются во входном массиве

для ввода вашего образца

[2,3,4,5,10,18,19,20]

он вернется:

sequenceToIntervals([2,3,4,5,10,18,19,20], true) // [[2,5], [10,10], [18,20]]
sequenceToIntervals([2,3,4,5,10,18,19,20], false) // [[2,5], 10, [18,20]]
sequenceToIntervals([2,3,4,5,10,18,19,20]) // [[2,5], 10, [18,20]]