Scala技术问询:Map元素分组及列表特定后缀元素链式分组实现
Hey there! Let's break down your two Scala questions with clear examples and solutions.
Scala's Map comes with a built-in groupBy method that makes grouping elements straightforward. The method takes a function that maps each key-value pair to a "group key", then returns a new Map where each key is your group identifier, and the value is a sub-Map containing all original entries that belong to that group.
Examples:
- Group by value:
val fruitCounts = Map("apple" -> 2, "banana" -> 3, "orange" -> 2, "grape" -> 1) val groupedByCount = fruitCounts.groupBy(_._2) // Result: Map(2 -> Map("apple"->2, "orange"->2), 3->Map("banana"->3), 1->Map("grape"->1)) - Group by a key attribute:
If you want to group by a specific part of the key (like the first character):val cityPopulations = Map("Paris" -> 2.1, "London" -> 9.0, "Berlin" -> 3.7, "Prague" -> 1.3) val groupedByFirstChar = cityPopulations.groupBy(_._1.head) // Result: Map('P' -> Map("Paris"->2.1, "Prague"->1.3), 'L'->Map("London"->9.0), 'B'->Map("Berlin"->3.7)) - Convert groups to Lists:
If you prefer Lists instead of sub-Maps for your groups, usemapValuesto convert:val groupsAsLists = fruitCounts.groupBy(_._2).mapValues(_.toList) // Result: Map(2 -> List(("apple",2), ("orange",2)), 3->List(("banana",3)), 1->List(("grape",1)))
For this specific requirement—grouping an element with the next one only if it ends with "chain"—we can use recursive pattern matching or a foldLeft approach, depending on your preference.
Option 1: Recursive Pattern Matching (Readable for Simple Cases)
This method walks through the list, checking each element and grouping as needed. Adjust the logic if you want to handle unpaired "chain" elements differently (e.g., exclude them instead of keeping them as single-element lists):
def groupChainPairs(list: List[String]): List[List[String]] = list match { // Match an element ending with "chain" plus the next element case chainElem :: nextElem :: tail if chainElem.endsWith("chain") => List(chainElem, nextElem) :: groupChainPairs(tail) // Match any other element (keep as single-element list) case singleElem :: tail => List(singleElem) :: groupChainPairs(tail) // Empty list base case case Nil => Nil } // Test it out val inputList = List("firstchain", "partner1", "random", "secondchain", "partner2", "lastchain") val output = groupChainPairs(inputList) // Result: List(List("firstchain", "partner1"), List("random"), List("secondchain", "partner2"), List("lastchain"))
Option 2: FoldLeft (Functional, Stateful Tracking)
If you prefer a more functional approach without recursion, use foldLeft to track whether we're waiting for a partner element after a "chain" entry:
def groupWithFold(list: List[String]): List[(String, String)] = { list.foldLeft((List.empty[(String, String)], Option.empty[String])) { // No pending "chain" element: check if current is a "chain" entry case ((result, None), current) => if (current.endsWith("chain")) (result, Some(current)) else (result, None) // Pending "chain" element: pair it with current and add to result case ((result, Some(prevChain)), current) => (result :+ (prevChain, current), None) }._1 // Extract the final result list } // Test val input = List("foochain", "bar", "baz", "quxchain", "quux") val result = groupWithFold(input) // Result: List(("foochain", "bar"), ("quxchain", "quux"))
If your expected output differs (e.g., you want to ignore unpaired "chain" elements or handle edge cases), you can tweak these examples to fit exactly what you need!
内容的提问来源于stack exchange,提问作者kaileena

