如何部分减少scala中的列表
How to partially reduce list in scala
我有一个整数列表(代表手牌价值)
val values = List(1, 200, 3, 4, 45, 45, 7, 8)
以及此列表中的索引列表(代表参与底池的牌)
val indexes = List(0, 4, 5, 7)
我需要过滤索引,以便只保留手牌价值最高的索引,相同的最高手牌价值将全部包括在内。
在这种情况下,输出将是 List(4, 5)
,因为 4 和 5 的最高手牌值都是 45。
我会分几个步骤进行:
val tuples = indexes.map(index => index -> values(index)) // mapping index - value
val maxVal = tuples.map(_._2).max // get max value
val maxIndices = tuples.filter(t => t._2 == maxVal).map(_._1) // filter & map tuples
为简洁起见,我对元组使用了位置表示法(_1
,_2
),这当然可以使用模式匹配来编写,使其更像 scala。
P.S.1:感谢@Joe K 简化聚合。
P.S.2:@Luis Miguel Mejía Suárez 注意到,最后一行可以压缩成一个 collect
模式匹配方法:
tuples.collect { case (i, v) if (v == maxVal) => i }
val indexedValues = values.view.zipWithIndex
val maxValue = indexedValues.toList.filter{case (v,i) => indexes.contains(i)}.maxBy(_._1)._1
indexedValues.filter{ case (v,i) => v==maxValue }.map(_._2).toList
在 Scala REPL 中:
Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_231).
Type in expressions for evaluation. Or try :help.
scala> val values = List(1, 200, 3, 4, 45, 45, 7, 8)
values: List[Int] = List(1, 200, 3, 4, 45, 45, 7, 8)
scala> val indexes = List(0, 4, 5, 7)
indexes: List[Int] = List(0, 4, 5, 7)
scala> val indexedValues = values.view.zipWithIndex
indexedValues: scala.collection.View[(Int, Int)] = View(<not computed>)
scala> val maxValue = indexedValues.toList.filter{case (v,i) => indexes.contains(i)}.maxBy(_._1)._1
maxValue: Int = 45
scala> indexedValues.filter{ case (v,i) => v==maxValue }.map(_._2).toList
res0: List[Int] = List(4, 5)
恕我直言,以自定义方式处理列表的最佳方法是编写尾递归算法。
def filterHighestIndexes(indexes: Set[Int])(data: List[Int]): List[Int] = {
@annotation.tailrec
def loop(remaining: List[Int], acc: List[Int], currentMax: Int, currentIdx: Int): List[Int] =
remaining match {
case x :: xs =>
val (newAcc, newMax) =
if (!indexes.contains(currentIdx) || x < currentMax)
acc -> currentMax
else if(x == currentMax)
(currentIdx :: acc) -> currentMax
else
List(currentIdx) -> x
loop(
remaining = xs,
newAcc,
newMax,
currentIdx + 1
)
case Nil =>
acc.reverse
}
data match {
case x :: xs =>
loop(
remaining = xs,
acc = List(0),
currentMax = x,
currentIdx = 1
)
case Nil =>
List.empty
}
}
你可以这样测试
val values = List(1, 200, 3, 4, 45, 45, 7, 8)
val indexes = Set(0, 4, 5, 7)
filterHighestIndexes(indexes)(values)
// res: List[Int] = List(4, 5)
请注意,我使用 Set 而不是 List 作为索引只是为了使 contains
更有效率。如果你不能改变它,可以只使用 List.
我觉得这个就这么简单:
scala> indexes.groupBy(values(_)).maxBy(_._1)._2
res6: List[Int] = List(4, 5)
我有一个整数列表(代表手牌价值)
val values = List(1, 200, 3, 4, 45, 45, 7, 8)
以及此列表中的索引列表(代表参与底池的牌)
val indexes = List(0, 4, 5, 7)
我需要过滤索引,以便只保留手牌价值最高的索引,相同的最高手牌价值将全部包括在内。
在这种情况下,输出将是 List(4, 5)
,因为 4 和 5 的最高手牌值都是 45。
我会分几个步骤进行:
val tuples = indexes.map(index => index -> values(index)) // mapping index - value
val maxVal = tuples.map(_._2).max // get max value
val maxIndices = tuples.filter(t => t._2 == maxVal).map(_._1) // filter & map tuples
为简洁起见,我对元组使用了位置表示法(_1
,_2
),这当然可以使用模式匹配来编写,使其更像 scala。
P.S.1:感谢@Joe K 简化聚合。
P.S.2:@Luis Miguel Mejía Suárez 注意到,最后一行可以压缩成一个 collect
模式匹配方法:
tuples.collect { case (i, v) if (v == maxVal) => i }
val indexedValues = values.view.zipWithIndex
val maxValue = indexedValues.toList.filter{case (v,i) => indexes.contains(i)}.maxBy(_._1)._1
indexedValues.filter{ case (v,i) => v==maxValue }.map(_._2).toList
在 Scala REPL 中:
Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_231).
Type in expressions for evaluation. Or try :help.
scala> val values = List(1, 200, 3, 4, 45, 45, 7, 8)
values: List[Int] = List(1, 200, 3, 4, 45, 45, 7, 8)
scala> val indexes = List(0, 4, 5, 7)
indexes: List[Int] = List(0, 4, 5, 7)
scala> val indexedValues = values.view.zipWithIndex
indexedValues: scala.collection.View[(Int, Int)] = View(<not computed>)
scala> val maxValue = indexedValues.toList.filter{case (v,i) => indexes.contains(i)}.maxBy(_._1)._1
maxValue: Int = 45
scala> indexedValues.filter{ case (v,i) => v==maxValue }.map(_._2).toList
res0: List[Int] = List(4, 5)
恕我直言,以自定义方式处理列表的最佳方法是编写尾递归算法。
def filterHighestIndexes(indexes: Set[Int])(data: List[Int]): List[Int] = {
@annotation.tailrec
def loop(remaining: List[Int], acc: List[Int], currentMax: Int, currentIdx: Int): List[Int] =
remaining match {
case x :: xs =>
val (newAcc, newMax) =
if (!indexes.contains(currentIdx) || x < currentMax)
acc -> currentMax
else if(x == currentMax)
(currentIdx :: acc) -> currentMax
else
List(currentIdx) -> x
loop(
remaining = xs,
newAcc,
newMax,
currentIdx + 1
)
case Nil =>
acc.reverse
}
data match {
case x :: xs =>
loop(
remaining = xs,
acc = List(0),
currentMax = x,
currentIdx = 1
)
case Nil =>
List.empty
}
}
你可以这样测试
val values = List(1, 200, 3, 4, 45, 45, 7, 8)
val indexes = Set(0, 4, 5, 7)
filterHighestIndexes(indexes)(values)
// res: List[Int] = List(4, 5)
请注意,我使用 Set 而不是 List 作为索引只是为了使 contains
更有效率。如果你不能改变它,可以只使用 List.
我觉得这个就这么简单:
scala> indexes.groupBy(values(_)).maxBy(_._1)._2
res6: List[Int] = List(4, 5)