Scala:调用函数检查值是否存在,如果不存在,创建并添加到列表中一次

Scala:调用函数检查值是否存在,如果不存在,创建并添加到列表中一次,scala,for-loop,break,for-comprehension,Scala,For Loop,Break,For Comprehension,假设我有以下类:Foo(id:String)和Bar(fooId:String) 我有一个Foo对象列表,我想根据Foo对象中的id创建一个Bar对象列表。如果列表中存在条,我不希望再次创建并添加到列表中。我可以用Set来解决这个问题,但我不喜欢这样。这是不对的 def createBars() = { var barList = List[Bar]() for { f ← fooList bar = findOrCreateBar(f, barList) bar

假设我有以下类:
Foo(id:String)
Bar(fooId:String)

我有一个
Foo
对象列表,我想根据
Foo
对象中的id创建一个
Bar
对象列表。如果列表中存在条,我不希望再次创建并添加到列表中。我可以用Set来解决这个问题,但我不喜欢这样。这是不对的

def createBars() = {
  var barList = List[Bar]()
  for {
    f ← fooList
    bar = findOrCreateBar(f, barList)
    barList = barList:+bar  //error

  } yield bar
}

def findOrCreateBar(f: Foo, barList: List[Bar]): Bar = {
  barList match {
    case Nil ⇒
      Bar(f.id)
    case _ ⇒
      val bars = for {
        bar ← barList
        if f.id == bar.id
      } yield bar

      if (bars.isEmpty) {
        Bar(f.id)
      } else {
        bars.head
      }
  }
}
我有几个问题:

first上面的代码不起作用,因为我在
createbar
中的
barList:+bar
中遇到编译错误。无法解析
:+
!编译器还说,用于声明
barList
var
可以是val!因此,编译器似乎没有将
barList=barList:+bar
中的barList视为列表

second如果我将barList的var替换为val,则无法将更改后的列表重新分配给barList

third我在
createBars
中用于理解,因为我认为我没有其他选择返回barList。在常规的foor循环中使用注释之类的东西只是为了休息,这不是一个好主意,imho


请帮忙

您的代码有几个缺陷

首先,尽可能避免可变变量

然后,在
列表
中搜索现有元素是没有效率的,因为单次搜索需要O(n)个时间,并且使算法在二次时间中运行

最后,在
列表的末尾添加元素也需要O(n)时间。如果无法避免迭代构建列表,请使用一些可变的工具,如
list.newBuilder

回到您的问题,我将使用递归函数解决它,该函数接受并返回不可变值。(不要一开始就看丑陋的黑客,这只是为了演示)

结果是:

 List(Bar#1(foo1), Bar#2(foo2), Bar#3(foo3), Bar#1(foo1))
UPD正如我承诺的那样,添加了新的方法

您可以使用
foldLeft
,这种方法基本上与上述方法相同,但较短,因此更受欢迎

  val barList2 = fooList.foldLeft((List.empty[Bar], Map.empty[String, Bar])) {
    (accum, foo) =>
      val (resultListReversed, alreadyCreatedBars) = accum
      val newBar = alreadyCreatedBars.getOrElse(foo.id, new Bar(foo.id))
      (newBar :: resultListReversed, alreadyCreatedBars + (foo.id -> newBar))
  }._1.reverse
最后的方法。这个函数非常短,但您最好避免使用它,因为不建议在
map
中使用可变变量

  import scala.collection.mutable

  val barList3 = {
    val createdBars = mutable.HashMap[String, Bar]()
    fooList.map(foo => createdBars.getOrElseUpdate(foo.id, new Bar(foo.id)))
  }

您的代码有几个缺陷

首先,尽可能避免可变变量

然后,在
列表
中搜索现有元素是没有效率的,因为单次搜索需要O(n)个时间,并且使算法在二次时间中运行

最后,在
列表的末尾添加元素也需要O(n)时间。如果无法避免迭代构建列表,请使用一些可变的工具,如
list.newBuilder

回到您的问题,我将使用递归函数解决它,该函数接受并返回不可变值。(不要一开始就看丑陋的黑客,这只是为了演示)

结果是:

 List(Bar#1(foo1), Bar#2(foo2), Bar#3(foo3), Bar#1(foo1))
UPD正如我承诺的那样,添加了新的方法

您可以使用
foldLeft
,这种方法基本上与上述方法相同,但较短,因此更受欢迎

  val barList2 = fooList.foldLeft((List.empty[Bar], Map.empty[String, Bar])) {
    (accum, foo) =>
      val (resultListReversed, alreadyCreatedBars) = accum
      val newBar = alreadyCreatedBars.getOrElse(foo.id, new Bar(foo.id))
      (newBar :: resultListReversed, alreadyCreatedBars + (foo.id -> newBar))
  }._1.reverse
最后的方法。这个函数非常短,但您最好避免使用它,因为不建议在
map
中使用可变变量

  import scala.collection.mutable

  val barList3 = {
    val createdBars = mutable.HashMap[String, Bar]()
    fooList.map(foo => createdBars.getOrElseUpdate(foo.id, new Bar(foo.id)))
  }

真的,用一套更好。它会更快更清晰

fooList.map(foo => new Bar(foo.id)).toSet
但是,无论如何,这里有一个更清晰的版本来描述您正在尝试做的事情(即,仍然使用循环和
List
var
等):

def createBars2(傻瓜:列表[Foo])={
var barList=向量[Bar]()

对于(foo真的,使用一个集合会更好。它会更快更清晰

fooList.map(foo => new Bar(foo.id)).toSet
但是,无论如何,这里有一个更清晰的版本来描述您正在尝试做的事情(即,仍然使用循环和
List
var
等):

def createBars2(傻瓜:列表[Foo])={
var barList=向量[Bar]()
对于(foo什么

fooList.distinct.map(foo=>Bar(foo.id))
它简单、高效,并且保持了原来的列表顺序。

怎么样

fooList.distinct.map(foo=>Bar(foo.id))

它简单、高效,并保持原始列表顺序。

您反对使用集合吗?@dhg基本上,我认为在这种情况下使用集合将有助于管理症状,但不会解决手头的问题,即避免重复创建和添加同一个对象。我真的希望我编写的代码能够使用using list。您反对使用Set吗?@dhg基本上,我认为在这种情况下使用Set会有助于管理症状,但不会解决手头的问题,即避免重复创建和添加同一个对象。我真的想让我编写的代码使用list工作。谢谢您的回答。但我得到了以下错误:java.lang.NoSuchMethodError:scala.collection.immutable.$colon$colon.hd$1()Ljava/lang/Object@mnish这很奇怪。检查你是否有所有必要的导入。我会在回家后(几个小时内)更新我的答案,添加几个其他较短的方法来完成你的任务@mnish按照承诺添加了两种新方法。请检查它们。感谢您的回答。但我发现了以下错误:java.lang.NoSuchMethodError:scala.collection.immutable。$colon$colon.hd$1()Ljava/lang/Object@mnish这很奇怪。检查你是否有所有必要的导入。我会在回家后(几小时内)更新我的答案,添加其他两种较短的方法来完成你的任务。@mnish按照承诺添加了两种新方法。检查它们。问题是关于重用具有相同id的元素。@Aivean-问题非常简单:“检查值是否存在,如果不存在,则创建并添加到列表中一次”。@mnish特别指出,他不想使用set(如其他人所建议的),因为这将多次创建(Bar)对象。请注意,
findOrCreateBar
函数正是尝试这样做的。问题在于重用元素