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
函数正是尝试这样做的。问题在于重用元素