在Scala中检查映射中的值

3

好的,我不知道这是否可能,但是假设我们有以下列表:

List(1, 2, 3, 1)

如果我想对这个进行映射,有没有办法检查我之前是否已经有过一个值,例如在第四个值(第二个1)时,它会说已经遇到了1,然后抛出错误或其他什么。

我假设你不想在映射之前简单地测试List的唯一性,这其中一定有原因。 - jwvh
为什么不使用 Set[A] 来保证集合中元素的唯一性呢? - Yuval Itzchakov
@jwvh 实际上没有,这确实是一个更快的解决方案。 - GSerum_
@YuvalItzchakov 因为列表是通过生成而不是输入产生的,因此可能出现重复值,应该抛出一个错误。 - GSerum_
3个回答

4
这将是一个 `foldLeft` 阶段的角色:
List(1, 2, 3, 1).foldLeft(List[Int]()) {
  // The item has already been encountered:
  case (uniqueItems, b) if uniqueItems.contains(b) => {
    // If as stated, you want to throw an exception, that's where you could do it
    uniqueItems
  }
  // New item not seen yet:
  case (uniqueItems, b) => uniqueItems :+ b
}

foldLeft在遍历序列时,会在处理每个新元素时工作,并且基于先前的结果得出一个结果。

对于每个元素,模式匹配(uniqueItems, b)应该这样理解:uniqueItems是“累加器”(它初始化为List[Int]()),并将针对列表的每个项目进行更新(或不更新)。而b则是当前正在处理的列表中的新项目。

顺便说一下,这个例子是一个(非高效的)列表上的distinct


好的,感谢您的回答。如果我们有一个包含姓名和年龄的人员列表:List(Person("Frank", 30), Person("Eva", 24), Person("Frank", 23)...)那么这个方法是否也适用呢?如果出现相同的姓名,是否会抛出错误? - GSerum_
是的,foldLeft适用于任何类型的序列。List(Person("Frank", 30), Person("Eva", 24), Person("Frank", 23)...).foldLeft(List[Person]()) { case (uniqueItems, person) if uniqueItems.contains(person) => throw SomeException; case (uniqueItems, person) => uniqueItems :+ person } - Xavier Guihot
这仅仅是通过检查名称来实现的,因此Person("Frank", 30)和Person("Frank", 23)被视为相等。 - GSerum_
我的前一个评论是针对情况类的,其中两个元素相等当且仅当名称和年龄都相同。如果您只想考虑名称,则第一个情况可能是:case (uniqueItems, Person(name, age)) if uniqueItems.exists(_.name == name) => throw SomeException。但我们有点偏离了最初的问题。 - Xavier Guihot

2
List(1, 2, 3, 1).distinct.map (n => n*n) 
// res163: List[Int] = List(1, 4, 9)

这段代码会去除重复项,然后以自我描述、简洁的方式执行映射。


虽然这段代码片段可能解决了问题,但包括解释真的有助于提高您的帖子质量。请记住,您正在为未来的读者回答问题,而这些人可能不知道您的代码建议原因。同时,请尽量不要在代码中添加过多的解释性注释,因为这会降低代码和解释的可读性! - Blue
@FrankerZ:非常感谢您提供的文本模板。您真的认为需要解释吗?您可以更精确一些吗?您不理解什么?让我猜猜——是审查队列,给出了那个建议。 - user unknown
如果有解释的话,它根本不会进入审核队列:我的意思是,看看其他答案就知道了。它们阐明了代码的作用,并详细说明了代码的工作原理。虽然你的代码片段可能有效/更短,但与一个答案相去甚远。 - Blue
是的,因为他们的代码过于复杂。你没有 Scala 的声誉,所以也许不应该审查这样简单的一行代码,而是专注于自己擅长的语言。在审查页面上有一个“筛选”按钮/链接,可以减少外来标签的干扰。 - user unknown

1

fold 可能是最好的选择。问题在于每次迭代都必须携带先前元素的内存以及正在构建的 map() 结果。

List(1, 2, 3, 11).foldRight((Set[Int](),List[String]())) {case (i, (st, lst)) =>
  if (st(i)) throw new Error        //duplicate encountered
  else (st + i, i.toString :: lst)  //add to memory and map result
}._2                                //pull the map result from the tuple

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接