在Scala中填充所需的行

  • 本文关键字:填充 Scala scala
  • 更新时间 :
  • 英文 :


我当前有一个result值,它是一个表示图中循环的字符串

> scala result
String =
0:0->52->22;
5:5->70->77;
8:8->66->24;8->42->32;
. //
. // trimmed to get by point across
. //
71:71->40->45;
77:77->34->28;77->5->70;
84:84->22->29

然而,我希望输出中包含介于两者之间的数字,并包含一定的值。示例代码的值为90

0:0->52->22;
1:
2:
3:
4:
5:5->70->77;
6:
7:
8:8->66->24;8->42->32;
. //
. // trimmed
. //
83:
84:84->22->29;
85:
86:
87:
88:
89:
90:

如果它有帮助或产生任何差异,则将此值更改为一个列表以供以后使用,例如

list_result = result.split("n").toList
List[String] = List(0:0->52->22;, 5:5->70->77;, 8:8->66->24;8->42->32;, 11:11->26->66;11->17->66;

我最初的想法是将缺失的数字插入列表中,然后进行排序,但我在排序时遇到了问题,所以我在这里寻找更好的方法。

list_result转换为具有默认值的Map。然后遍历所需的数字范围,将每个数字交换为其Map值。

val map_result: Map[String,List[String]] =
list_result.groupBy("\d+:".r.findFirstIn(_).getOrElse("bad"))
.withDefault(List(_))
val full_result: String =
(0 to 90).flatMap(n => map_result(s"$n:")).mkString("n")

这里有一个Scastie会议,看看它的行动。

一种选择是使用Map作为中间数据结构:


val l: List[String] = List("0:0->52->22;", "5:5->70->77;", "8:8->66->24;8->42->32;", "11:11->26->66;11->17->66;")
val byKey: List[Array[String]] = l.map(_.split(":"))
val stop = 90
val mapOfValues = (1 to stop).map(_->"").toMap
val output = byKey.foldLeft(mapOfValues)((acc, nxt) => acc + (nxt.head.toInt -> nxt.tail.head))
output.toList.sorted.map {case (key, value) => println(s"$key, $value")}

这将为您提供您想要的输出。它将输入字符串分解为伪键值对,创建一个保存结果的映射,将byKey的元素插入到映射中,然后返回结果的排序列表。

注意:如果您在类似于生产代码的任何代码中使用此项,则需要正确检查byKey中的每个Array是否有两个元素,以防止以后调用headtail.head时出现任何nullPointerExceptions

提供的解决方案很好,但我想推荐一种可以延迟处理数据并且不需要一次将所有数据保存在内存中的解决方案。

它使用了一个名为unfold的漂亮函数;展开";从开始状态到您认为集合已结束(docs(的集合。

它不是完美的抛光,但我希望它能有所帮助:

def readLines(s: String): Iterator[String] =
util.Using.resource(io.Source.fromString(s))(_.getLines)
def emptyLines(from: Int, until: Int): Iterator[(String)] =
Iterator.range(from, until).map(n => s"$n:")
def indexOf(line: String): Int =
Integer.parseInt(line.substring(0, line.indexOf(':')))
def withDefaults(from: Int, to: Int, it: Iterator[String]): Iterator[String] = {
Iterator.unfold((from, it)) { case (n, lines) =>
if (lines.hasNext) {
val next = lines.next()
val i = indexOf(next)
Some((emptyLines(n, i) ++ Iterator.single(next), (i + 1, lines)))
} else if (n < to) {
Some((emptyLines(n, to + 1), (to, lines)))
} else {
None
}
}.flatten
}

你可以在Scastie上看到这一点。

unfold所做的是从一个状态开始(在这种情况下,是行号from和带有行的迭代器(,并在每次迭代时:

  1. 如果迭代器中仍有元素,它会获取下一个项,标识其索引并返回:
    • 作为下一个项目,Iterator具有空行,最新行号后接实际行
      • 例如,当达到5时,1和4之间的空行被发射,以5开头的行终止
    • 作为下一个状态,发出的项中最后一个之后的行的索引和迭代器本身(它是有状态的,在每次迭代中被对unfold的重复调用所消耗(
      • 例如,在处理5之后,下一个状态是6,迭代器
  2. 如果迭代器中不再有元素,但尚未达到to索引,它将发出另一个Iterator,其中包含要打印的剩余项(在您的示例中,是84之后的项(
  3. 如果两个条件都为假,我们就不需要再发射任何东西;展开";集合,通过返回None而不是Some[(Item, State)]来发出信号

这返回一个Iterator[Iterator[String]],其中每个嵌套迭代器都是从一行到下一行的值范围,默认空行为"0";夹在中间的";介于两者之间。对flatten的调用将其转换为所需的结果。

我使用了Iterator来确保在任何时候,只有在实际使用时,才在内存中保留基本状态。

最新更新