使用Kotlin将数字集合转换为范围格式的字符串

时间:2019-07-10 23:19:51

标签: kotlin collections numbers format tostring

我有很多数字。我需要将此数字列表作为URL查询参数传递。由于这些列表可能太大,因此有可能导致请求URL超过the allowed length of a URL;而且,调试一串连续数字(例如1,2,3,..,500,782)有点困难。为了解决这些问题,我想将序列号列表字符串转换为使用范围符号(例如-5..-3,1..500,782)格式化的字符串。如何使用Kotlin创建此范围符号字符串,以及如何也使用Kotlin将字符串解析回数字集合?

1 个答案:

答案 0 :(得分:0)

这会将Collection<Int>转换为使用指定的“范围符号”的字符串:

fun Collection<Int>.toRangesString(): String {
  if (this.isEmpty()) {
    return ""
  }


  if (this.size <= 2) {
    return this.toSortedSet().joinToString(",")
  }


  val rangeStrings = mutableListOf<String>()

  var start: Int? = null
  var prev: Int? = null

  for (num in this.toSortedSet()) {
    if (prev == null) {
      start = num
      prev = num
      continue
    }

    if (num != (prev + 1)) {
      _addRangeString(rangeStrings, start!!, prev)
      start = num
      prev = num
      continue
    }

    prev = num
  }

  if (start != null) {
    _addRangeString(rangeStrings, start, prev!!)
  }

  return rangeStrings.joinToString(",")
}


private fun _addRangeString(rangeStrings: MutableList<String>, start: Int, prev: Int) {
  rangeStrings.add(
    when {
      (start == prev) -> start.toString()
      ((start + 1) == prev) -> "${start},${prev}"
      else -> "${start}..${prev}"
    }
  )
}

...,这会将这些范围标记的字符串解析为Set<Int>

fun parseRangesString(str: String): Set<Int> {
  if (str.isBlank()) {
    return setOf()
  }


  val ranges = str.trim().split(",")
  val numbers = mutableListOf<Int>()

  for (range in ranges) {
    if (range.contains("..")) {
      val (start, end) = range.split("..")
      numbers.addAll(start.toInt()..end.toInt())
      continue
    }

    numbers.add(range.toInt())
  }

  return numbers.toSet()
}

...,最后,比使用大量数字更好的是,您可以使用Kotlin的IntRange(或LongRange)类:

fun toIntRanges(str: String): Collection<IntRange> = _toRanges(str, ::_createIntRange)
fun toLongRanges(str: String): Collection<LongRange> = _toRanges(str, ::_createLongRange)

private fun <T : ClosedRange<*>> _toRanges(str: String, createRange: (start: String, end: String) -> T): Collection<T> {
  if (str.isBlank()) {
    return listOf()
  }

  val rangeStrs = str.trim().split(",")
  val ranges = mutableListOf<T>()

  for (rangeStr in rangeStrs) {
    if (rangeStr.contains("..")) {
      val (start, end) = rangeStr.split("..")
      ranges.add(createRange(start, end))
      continue
    }

    ranges.add(createRange(rangeStr, rangeStr))
  }

  return ranges.toList()
}


private fun _createIntRange(start: String, end: String) = IntRange(start.toInt(), end.toInt())
private fun _createLongRange(start: String, end: String) = LongRange(start.toLong(), end.toLong())