在javascript中如何将数组中的数字序列转换为数字范围?
例如。 [2,3,4,5,10,18,19,20]
至[2-5,10,18-20]
答案 0 :(得分:27)
这是我创建的some time ago算法,最初是为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"]
答案 1 :(得分:5)
从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;
}
答案 2 :(得分:5)
我只是在寻找这个确切的事情。我需要一个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;
}
答案 3 :(得分:5)
我发现这个答案很有用,但需要一个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]))
答案 4 :(得分:3)
非常好的问题:这是我的尝试:
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();
}
答案 5 :(得分:1)
如果您只是想要一个表示范围的字符串,那么您将找到序列的中点,这将成为您的中间值(在您的示例中为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
答案 6 :(得分:1)
这是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 :(得分:1)
这是我对此的看法......
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;
}
答案 8 :(得分:1)
在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();
}
答案 9 :(得分:1)
以下是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; } # }}}
答案 10 :(得分:0)
大致流程如下:
ranges
的空数组value
ranges
为空,则插入项目 {min: value, max: value}
max
中最后一项的 ranges
和当前 value
是连续的,则设置 max
中最后一项的 ranges
= {{1} }value
{min: value, max: value}
数组,例如通过组合 ranges
和 min
(如果相同)以下代码使用max
,结合步骤2.1和2.3简化逻辑。
Array.reduce
答案 11 :(得分:0)
这是Coffeescript中的一个版本
getRanges = (array) ->
ranges = []
rstart
rend
i = 0
while i < array.length
rstart = array[i]
rend = rstart
while array[i + 1] - array[i] is 1
rend = array[i + 1] # increment the index if the numbers sequential
i = i + 1
if rstart == rend
ranges.push rstart + ''
else
ranges.push rstart + '-' + rend
i = i + 1
return ranges
答案 12 :(得分:0)
我今天需要TypeScript代码来解决这个问题-在OP之后很多年-并决定尝试以一种比此处其他答案更实用的方式编写的版本。当然,只有参数和返回类型注释可将此代码与标准ES6 JavaScript区别开。
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
会在适当的位置排序,我们无法更改原始数组。
答案 13 :(得分:0)
我需要一个同时支持向下范围的PHP版本(例如[10,9,8]
转换为[10-8]
)。因此,我修改了移植了CMS解决方案的DisgruntledGoat的版本。它还可以正确处理输入中的字符串。
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]
答案 14 :(得分:0)
使用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
。
请注意,这可以改进,以避免测试两次整数相邻性(邻近?我不是英语母语者; - )。
当然,如果您不想将单个字符串作为输出,请将其拆分为“;”。
答案 15 :(得分:0)
Tiny ES6模块适合你们。它接受一个函数来确定何时必须中断序列(breakDetectorFunc param - 默认是整数序列输入的简单事物)。 注意:由于输入是抽象的 - 在处理之前没有自动排序,因此如果您的序列没有排序 - 在调用此模块之前执行此操作
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]]
答案 16 :(得分:0)
这是我在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
}
答案 17 :(得分:0)
CMS's 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>
答案 18 :(得分:0)
我编写了自己的方法,它依赖于Lo-Dash,但不只是返回一个范围数组,而是只返回一个范围组数组。
[1,2,3,4,6,8,10]成为:
[[1,2,3,4],[6,8,10]]
答案 19 :(得分:0)
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;
}
}
答案 20 :(得分:0)
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)));
答案 21 :(得分:0)
; 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;
}
}
类似的东西。
答案 22 :(得分:0)
您可以迭代数字,看看下一个数字是否比当前数字大1。所以有一个:
struct range {
int start;
int end;
} range;
其中if 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);
}