我当前有一个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
是否有两个元素,以防止以后调用head
和tail.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
和带有行的迭代器(,并在每次迭代时:
- 如果迭代器中仍有元素,它会获取下一个项,标识其索引并返回:
- 作为下一个项目,
Iterator
具有空行,最新行号后接实际行- 例如,当达到5时,1和4之间的空行被发射,以5开头的行终止
- 作为下一个状态,发出的项中最后一个之后的行的索引和迭代器本身(它是有状态的,在每次迭代中被对
unfold
的重复调用所消耗(- 例如,在处理5之后,下一个状态是6,迭代器
- 作为下一个项目,
- 如果迭代器中不再有元素,但尚未达到
to
索引,它将发出另一个Iterator
,其中包含要打印的剩余项(在您的示例中,是84之后的项( - 如果两个条件都为假,我们就不需要再发射任何东西;展开";集合,通过返回
None
而不是Some[(Item, State)]
来发出信号
这返回一个Iterator[Iterator[String]]
,其中每个嵌套迭代器都是从一行到下一行的值范围,默认空行为"0";夹在中间的";介于两者之间。对flatten
的调用将其转换为所需的结果。
我使用了Iterator
来确保在任何时候,只有在实际使用时,才在内存中保留基本状态。