所以我想说我有一个这样的清单:
List(Person(List(Name("Frank"), Age(50))),Person(List(Name("Peter"), Age(40))),Person(List(Name("Frank"), Age(20))))
如果所有名称都是唯一的,我如何编写一个返回true的函数,如果有重复的名称,则返回false。例如,这将返回true:
List(Person(List(Name("Frank"), Age(50))),Person(List(Name("Peter"), Age(40))),Person(List(Name("Bob"), Age(20))))
上面的示例List将返回false。
我试过这个:
sealed abstract class PersonFeatures
case class Person(list: List[PersonFeatures]) extends PersonFeatures
case class Age(num: Int) extends PersonFeatures
case class Name(value: String) extends PersonFeatures
val datlist = List(Person(List(Name("Frank"), Age(50))),Person(List(Name("Peter"), Age(40))),Person(List(Name("Frank"), Age(20))))
def findDoubles(checklist: List[Person]): List[Person] = {
checklist.foldLeft(List[Person]()) {
case (uniquePersons, Person(List(name, age))) if uniquePersons.contains(Person(List(name, _))) => {
throw new IllegalArgumentException("Double name found");
}
case (uniquePersons, person) => uniquePersons :+ person
}
}
val result = findDoubles(datlist)
println(result)
但它引发了这个错误:
type mismatch;
found : List[Any]
required: Playground.this.PersonFeatures
答案 0 :(得分:3)
您可以使用以下代码修改代码以使其编译:
def findDoubles(checklist: List[Person]): List[String] = {
checklist.foldLeft(List[String]()) {
case (uniquePersons, Person(List(Name(name), _))) if uniquePersons.contains(name) =>
throw new IllegalArgumentException("Double name found");
case (uniquePersons, Person(List(Name(name), _))) => name :: uniquePersons
}
}
但对你的要求似乎相当复杂。
这是另一种选择:
case class Name(name: String)
case class Age(age: Int)
case class Person(smthg: List[Any])
val list = List(
Person(List(Name("Frank"), Age(50))),
Person(List(Name("Peter"), Age(40))),
Person(List(Name("Frank"), Age(20))))
val names = list.flatMap {
case Person(smthg) => smthg.collect { case Name(name) => name }
}
println(names)
> List(Frank, Peter, Frank)
println(names.distinct.length == result.length)
> false
首先,我们从所有元素中提取名称:
val names = list.flatMap {
case Person(smthg) => smthg.collect { case Name(name) => name }
}
smthg.collect
已应用于List(Name("Frank"), Age(50))
。它过滤Name类型的元素(为了过滤Age元素)并从Age(年龄)中提取实际年龄。
由于smthg.collect
输出了一个列表,我们flatten
它(list.flatMap {...}
)。
因此我们得到此列表:List(Frank, Peter, Frank)
然后,为了找出列表是否有重复,一个简单的方法是使用distinct
转换列表,只保留每个元素的一个实例并将其与之前生成的列表进行比较:
names.distinct.length == result.length
答案 1 :(得分:1)
首先,从查看代码开始,我必须指出在同一个列表中包含不同类型的非常不良做法。 PersonFeatures
特质对此没有任何帮助。我建议你制作Person
案例类,而不是List
两种完全不同的类型(Name
和Age
)。除此之外,这将改善数据结构并简化解决方案。 (如果你必须采用这种方式,那么支持异构列表的 Shapeless 等库比使用List[List[Any]]
要好得多。 )
所以,这是我如何接受这个:
import scala.annotation.tailrec
final case class Person(name: String, age: Int)
val datlist = List(Person("Frank", 50), Person("Peter", 40), Person("Frank", 20))
// Determine if people names are unique.
def haveUniqueNames(pl: List[Person]): Boolean = {
// Helper function.
@tailrec
def headUnique(rem: List[Person], seen: Set[String]): Boolean = {
// If we've reached the end of the list, return true; we didn't find a duplicate.
if(rem.isEmpty) true
// Otherwise, if the person at the head of the list has a name we've already seen,
// return false.
else if(seen.contains(rem.head.name)) false
// Otherwise, add the head person's name to the set of names we've seen,
// and perform another iteration starting with the next person.
else headUnique(rem.tail, seen + rem.head.name)
}
// Start off with the full list and an empty set.
headUnique(pl, Set.empty)
}
// Check if names are unique.
haveUniqueNames(datlist)
或者,如果效率不如简洁那么重要:
datlist.map(_.name).distinct.size == datlist.size
答案 2 :(得分:0)
没人知道,人和年龄是什么。 :)
您可以将此示例转移到您的案例中吗?
val la = List (List (1, 2), List (3, 2), List (1, 4))
// la: List[List[Int]] = List(List(1, 2), List(3, 2), List(1, 4))
val lb = List (List (1, 2), List (3, 2), List (4, 1))
// lb: List[List[Int]] = List(List(1, 2), List(3, 2), List(4, 1))
la.groupBy (_(0)).size == la.size
// res229: Boolean = false
lb.groupBy (_(0)).size == lb.size
// res230: Boolean = true
确定。这可以抽象出来。有一些东西,根据这些东西的谓词,我们希望找到唯一性。
def unique [T, A] (l: List[T], f: T => A): Boolean = {
l.groupBy (element => f(element)).size == l.size
}
对于我的例子,T是整数列表(内部列表),A只是T的属性,由T到A的函数选择。如果我们对该函数的结果进行分组,如果属性是唯一的,则大小应保持不变。
unique (lb, (l:List[Int]) => l(0))
这里的函数是索引到内部的int列表。既然我们知道人/年龄/姓名定义,我们也可以测试它:
unique (datlist, (p:Person) => p.list(0))
// res254: Boolean = false
unique (datlist, (p:Person) => p.list(1))
// res255: Boolean = true
我有点不高兴,解决方案没有透露,名称不是唯一的,而年龄是 - 不是结果是假的,而是我们用列表索引访问它,而不是属性名称。但是也许有一个很好的理由,就像那样写,这就是主题。但据我所知,具有以不同顺序初始化的属性的Person会使此方法失败。所以我们真正需要的是一个从Person到Name的方法,而不是从Person到Person.list中的第一个PersonAttribute。
这可以通过模式匹配来尝试,但我一般不相信Person设计。难道没有名字的人真的可能吗?有两个名字的人?或者如何从您的设计中防止这种情况?
然而,忽略对设计的批评,我们可以实现一个冗长的解决方案,它处理重新排序的功能(我称之为属性):
unique (datlist, (p:Person) => p.list.filter (pf=> isFeatureByName (pf, "Name")))
//res259: Boolean = false1def isFeatureByName (pf: PersonFeatures, featurename: String) = (pf, featurename) match {
case (Age (_), "Age") => true
case (Name (_), "Name") => true
case (Person (_), "Person") => true
case _ => false
}
unique (datlist, (p:Person) => p.list.filter (pf=> isFeatureByName (pf, "Age")))
// res258: Boolean = true
unique (datlist, (p:Person) => p.list.filter (pf=> isFeatureByName (pf, "Name")))
// res259: Boolean = false
但是如何使用缺少的功能,没有名字或年龄的人,或者两个名字,两个年龄?我想很明显,这个设计需要重新思考。
答案 3 :(得分:0)
您可以使用此类地图删除重复键的功能,将您的列表转换为人名的地图。
我同意Mike Allen的观点,你不应该使用不同类型的列表,而应该使用案例类。然后,您可以按如下方式编写函数:
final case class Person(name: String, age: Int)
val datlist = List(Person("Frank", 50), Person("Peter", 40), Person("Frank", 20))
// Determine if people names are unique.
def haveUniqueNames(personList: List[Person]): Boolean = {
val personMap = personList.map(person => person.name -> person).toMap
(personMap.size == personList.size)
}