折叠连续数字到范围的运行

时间:2013-06-04 07:08:44

标签: r

请考虑以下以逗号分隔的数字字符串:

s <- "1,2,3,4,8,9,14,15,16,19"
s
# [1] "1,2,3,4,8,9,14,15,16,19"

是否可以将连续数字的运行折叠到其相应的范围,例如上面的运行1,2,3,4将折叠到范围1-4。所需的结果如下所示:

s
# [1] "1-4,8,9,14-16,19"

4 个答案:

答案 0 :(得分:4)

我从this question的答案中汲取了一些重要的灵感。

findIntRuns <- function(run){
  rundiff <- c(1, diff(run))
  difflist <- split(run, cumsum(rundiff!=1))
  unlist(lapply(difflist, function(x){
    if(length(x) %in% 1:2) as.character(x) else paste0(x[1], "-", x[length(x)])
  }), use.names=FALSE)
}

s <- "1,2,3,4,8,9,14,15,16,19"
s2 <- as.numeric(unlist(strsplit(s, ",")))

paste0(findIntRuns(s2), collapse=",")
[1] "1-4,8,9,14-16,19"

编辑:多种解决方案:基准测试时间!

Unit: microseconds
   expr     min      lq   median       uq      max neval
 spee() 277.708 295.517 301.5540 311.5150 1612.207  1000
  seb() 294.611 313.025 321.1750 332.6450 1709.103  1000
 marc() 672.835 707.549 722.0375 744.5255 2154.942  1000

@ speendo的解决方案目前是最快的,但这些解决方案尚未进行优化。

答案 1 :(得分:2)

我太慢了......但这是另一种解决方案。

它使用较少的R特定功能,因此它可以移植到其他语言(另一方面也许它不那么优雅)

s <- "1,2,3,4,8,9,14,15,16,19"

collapseConsecutive <- function(s){
  x <- as.numeric(unlist(strsplit(s, ",")))

  x_0 <- x[1]
  out <- toString(x[1])
  hasDash <- FALSE

  for(i in 2:length(x)) {
    x_1 <- x[i]
    x_2 <- x[i+1]

    if((x_0 + 1) == x_1 && !is.na(x_2) && (x_1 + 1) == x_2) {
      if(!hasDash) {
        out <- c(out, "-")
        hasDash <- TRUE
      }
    } else {
      if(hasDash) {
        hasDash <- FALSE
      } else {
        out <- c(out, ",")
      }
      out <- c(out, x_1)
      hasDash <- FALSE
    }
    x_0 <- x_1
  }
  outString <- paste(out, collapse="")
  outString
}

collapseConsecutive(s)
# [1] "1-4,8,9,14-16,19"

答案 2 :(得分:0)

这是一个应该做你想做的功能:

conseq <- function(s){
s <- as.numeric(unlist(strsplit(s, ",")))
dif <- s[seq(length(s))][-1] - s[seq(length(s)-1)]
new <- !c(0, dif == 1)
cs <- cumsum(new)
res <- vector(mode="list", max(cs))
for(i in seq(res)){
    s.i <- s[which(cs == i)]    
    if(length(s.i) > 2){
        res[[i]] <- paste(min(s.i), max(s.i), sep="-")
    } else {
        res[[i]] <- as.character(s.i)
    }
}  
paste(unlist(res), collapse=",")
}

实施例

> s <- "1,2,3,4,8,9,14,15,16,19"
> conseq(s)
[1] "1-4,8,9,14-16,19"

答案 3 :(得分:0)

另一个相当紧凑的选择

{"Date":1531043635316,"Daily bonus":2,"sound enabled":true,"total coins":2099300,"high score":0,"Btns Set":1,"leftBtnX":46,"leftBtnY":18,"leftBtnsize":1,"upBtnX":105,"upBtnY":18,"upBtnsize":1,"rightBtnX":164,"rightBtnY":18,"rightBtnsize":1,"guardBtnX":288,"guardBtnY":18,"guardBtnsize":1,"chargeBtnX":363,"chargeBtnY":18,"chargeBtnsize":1,"attackBtnX":438,"attackBtnY":18,"attackBtnsize":1,"superBtnX":438,"superBtnY":58,"superBtnsize":1,"ultimateBtnX":363,"ultimateBtnY":58,"ultimateBtnsize":1,"dpadX":80,"dpadY":40,"dpadSize":1,"gpadX":405,"gpadY":59,"gpadSize":1}

我还编写了一个钟声版本,可以处理数字和字符串输入,包括命名对象,降序序列和替代标点符号,以及执行错误检查和报告。如果有人感兴趣,可以将其添加到我的答案中。