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

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

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

Ответы [ 21 ]

0 голосов
/ 07 ноября 2015

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

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

}

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

0 голосов
/ 29 марта 2015

Адаптация решения javascript для CMS для 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>
0 голосов
/ 03 октября 2012

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

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

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

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

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

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

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

где, если array[i+1] == array[i]+1; (где i - наблюдаемое в настоящее время число) тогда 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);
}
0 голосов
/ 28 октября 2018

Мне нужна версия PHP, которая также поддерживает нисходящие диапазоны (например, [10,9,8] преобразуется в [10-8]).Поэтому я изменил версию DisgruntledGoat, которая портировала решение CMS.Он также правильно обрабатывает строки во входных данных.

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

    for ($i = 0; $i < count($nums); $i++) {
        if (!is_numeric($nums[$i]) || !isset($nums[$i+1]) || !is_numeric($nums[$i+1])) {
            $ranges[] = $nums[$i];
            continue;
        }
        $rStart = $nums[$i];
        $rEnd = $rStart;
        $rDiff = $nums[$i+1] > $nums[$i] ? 1 : -1;
        while (isset($nums[$i+1]) && is_numeric($nums[$i+1]) && $nums[$i+1]-$nums[$i] == $rDiff)
            $rEnd = $nums[++$i];
        $ranges[] = $rStart == $rEnd ? $rStart : $rStart.'-'.$rEnd;
    }

    return $ranges;
}

Пример:

getRanges([2,3,4,5,10,18,19,20,"downwards",10,9,8,7,6,5])
// Returns [2-5,10,18-20,"downwards",10-5]
0 голосов
/ 05 декабря 2017

Tiny 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]]
0 голосов
/ 18 апреля 2018

Используя ES6, решение:

function display ( vector ) { // assume vector sorted in increasing order
    // display e.g.vector [ 2,4,5,6,9,11,12,13,15 ] as "2;4-6;9;11-13;15"
    const l = vector.length - 1; // last valid index of vector array
    // map [ 2,4,5,6,9,11,12,13,15 ] into array of strings (quote ommitted)
    // --> [ "2;", "4-", "-", "6;", "9;", "11-", "-", "13;", "15;" ]
    vector = vector.map ( ( n, i, v ) => // n is current number at index i of vector v
        i < l && v [ i + 1 ] - n === 1 ? // next number is adjacent ? 
            `${ i > 0 && n - v [ i - 1 ] === 1 ? "" : n }-` :
            `${ n };`
        );
    return vector.join ( "" ).  // concatenate all strings in vector array
        replace ( /-+/g, "-" ). // replace multiple dashes by single dash
        slice ( 0, -1 );        // remove trailing ;
    }

Если вы хотите добавить дополнительные пробелы для удобства чтения, просто добавьте дополнительные вызовы к string.prototype.replace().

Если входной вектор не отсортирован, вы можете добавить следующую строку сразу после открывающей фигурной скобки функции display():

vector.sort ( ( a, b ) => a - b ); // sort vector in place, in increasing order.

Обратите внимание, что это можно улучшить, чтобы избежать двойного тестирования на целочисленную смежность (смежность? Я не являюсь носителем английского языка; -).

И, конечно, если вы не хотите выводить одну строку, разбейте ее на ";".

0 голосов
/ 18 августа 2011

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)));
0 голосов
/ 03 марта 2019

Мне понадобился код TypeScript для решения этой самой проблемы - спустя много лет после ОП - и я решил попробовать версию, написанную в стиле, более функциональном, чем другие ответы здесь. Конечно, только аннотации параметров и возвращаемых типов отличают этот код от стандартного JavaScript ES6.

  function toRanges(values: number[],
                    separator = '\u2013'): string[] {
    return values
      .slice()
      .sort((p, q) => p - q)
      .reduce((acc, cur, idx, src) => {
          if ((idx > 0) && ((cur - src[idx - 1]) === 1))
            acc[acc.length - 1][1] = cur;
          else acc.push([cur]);
          return acc;
        }, [])
      .map(range => range.join(separator));
  }

Обратите внимание, что slice необходимо, потому что sort сортируется на месте, и мы не можем изменить исходный массив.

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