我有一个自定义逻辑,可以按照第一个字母对名单进行分组,我可以通过以下方式实现:
val names = List("Adam", "Barbara", "Bob", "Charlie", "Damien", "Elaine", "Florence", "Gwen")
names.map{
case x if x.startsWith("A") => (1, x)
case x if x.startsWith("B") => (1, x)
case x if x.startsWith("C") => (2, x)
case x if x.startsWith("D") => (2, x)
case x if x.startsWith("E") => (3, x)
case x if x.startsWith("F") => (3, x)
case default => (0, default)
}.groupBy(_._1)
逻辑可能会改变。例如,下次我可能想要将以A,F和G开头的所有名称分组到组1,或者我可以将所有字母添加到Z.更高级的逻辑是从Ad开始的前两个字母和组名称和Ba到第1组,在这种情况下,亚当和芭芭拉将属于同一组。
我想知道是否有更惯用的方法,我可以写startsWith
次数较少。
从Tanjin's answer开始,我进一步重构了groups
:
val groups = List(
(List("A", "B"),1),
(List("C", "D"),2),
(List("E", "F"),3)
).flatMap{ case (l,i) => l.map((_, i)) }
答案 0 :(得分:2)
我们可以尝试这一点 - 您需要做的就是改变您的groups
:
def categorize(groups: Seq[(String, Int)]) = (s: String) => {
groups.find(category => category._1 == s.head.toString)
.map(c => (c._2, s)).getOrElse((0, s))
}
val groups = Seq(("A", 1), ("B", 1), ("C", 2), ("D", 2), ("E", 3), ("F", 3))
names.map(categorize(groups)).groupBy(_._1)
答案 1 :(得分:1)
您只能使用<div>
1 <div>
2
<div>3</div>
<div>4</div>
<div>5
<div>6</div>
<div>
7
<div>8</div>
<div>9</div>
</div>
</div>
</div>
</div>
<div>
<span id="carot">|</span>10
</div>
groupBy
注意def groupBy[K](f: String => K): scala.collection.immutable.Map[K,List[String]]
将函数作为参数
Scala REPL
groupBy
答案 2 :(得分:1)
def categorize[A](names: List[String], prefixGroups: Seq[(A,Set[String])]): Map[Option[A],List[String]] =
names.groupBy(name => prefixGroups.find{case (group, prefixes) => prefixes.exists(name.startsWith)}.map(_._1))
val result = categorize(List("Anna","Bob","Carla","Debbie","Eddie"), Seq(1 -> Set("A","B"), 2 -> Set("C","D")))
println("all: " + result)
println("only valid: " + result.filterKeys(_.isDefined).map{case (Some(k),v) => k -> v})
all: Map(Some(1) -> List(Anna, Bob), None -> List(Eddie), Some(2) -> List(Carla, Debbie))
only valid: Map(1 -> List(Anna, Bob), 2 -> List(Carla, Debbie))
Seq[(Key,Set[String])]
给出,汇总了同一组的前缀。答案 3 :(得分:1)
我参加派对有点晚了,但如果你愿意将你的群体存储略有不同,我想分享另一个好的解决方案。请注意,此版本仅在您检查第一个字母时才有效,但您也可以轻松更新它以便在其他情况下使用。
&#34;看看地图的力量&#34;
首先,我们将我们的群组作为Int
的地图存储到一系列字符中,这些字符将成为我们的前缀。为清晰起见,我使用多行语法:
val groups = Map(
1 -> Seq('A', 'B'),
2 -> Seq('C', 'D'),
3 -> Seq('E', 'F')
)
现在我们从字典中构建一个inverted index。这意味着我们可以使用地图中的值来交换键。它不完全交换,因为我们需要将每个前缀序列映射到多个元组:
val inverted = groups
.flatMap { case (id, prefixes) => prefixes.map(_ -> id) } // Swap keys w/ values
.withDefault(_ => 0) // Set the default
最后,groupBy
电话非常整洁:
names.groupBy(name => inverted(name(0)))
最后,我要补充说Map
上的操作通常更快。但无论如何,如果你没有真正庞大的名单,你应该没办法。这种方法的另一个好处是你可以重复使用前两个地图:groups
和inverted
。因此,如果您正在检查多个名称列表,那么它反复重新计算的唯一内容是最后一次groupBy
调用。
我希望这很有帮助,尽情享受!