我正在寻找一种比较两个Scala集合的元素并根据条件生成第三个集合的好方法。我可以为了性能而牺牲代码外观。
假设如下:
case class Item(name: String, category: String, code: String, price: Int, freeItem: Option[Item])
val parentItems = Seq(
Item("name_1", "category_A", "code_A", 100, None),
Item("name_2", "category_B", "code_B", 100, None),
Item("name_3", "category_C", "code_C", 100, None)
)
val childItems = Seq(
Item("name_4", "category_A", "code_A", 100, None),
Item("name_5", "category_C", "code_C", 100, None)
)
def isChild(i1: Item, i2: Item): Boolean = {
i1.name != i2.name &&
i1.category == i2.category &&
i1.code == i2.code
}
val parentsWithChildren: Seq[Item] = (
for {
i1 <- parentItems;
i2 <- childItems
} yield {
if (isChild(i1, i2)) Some(i1.copy(freeItem = Some(i2.copy())))
else None
}).filter(_.isInstanceOf[Some[_]]).map(_.get)
上面的代码片段会产生以下结果,这是结果:
Seq(
Item("name_1", "category_A", "code_A", 100,
Some(Item("name_4", "category_A", "company_A", 100, None))),
Item("name_3", "category_C", "code_C", 100,
Some(Item("name_5", "category_C", "company_C", 100, None)))
)
我知道上面的代码有漏洞但是没关系。我在寻找的是:
有什么方法可以避免if(isChild(i1,i2))Some(i1.copy(freeItem = Some(i2.copy()))) else None
,结果是.filter(_.isInstanceOf[Some[_]]).map(_.get)
?部分功能是一种选择吗?
我在这里使用嵌套循环,我会在Java中做类似的事情。有没有其他方法可以接近它?起初我认为我zip
但显然至少不起作用。
提前致谢!
答案 0 :(得分:2)
我很想尝试这样的事情:
val parentsWithChildren: Seq[Item] =
for {
parent <- parentItems
child <- childItems if isChild(parent, child)
}
yield parent.copy(freeItem = Some(child))
希望有所帮助!
答案 1 :(得分:0)
关于
p = Paths.get(new URI("file:///a/b/c")).getParent().getParent();
,返回选项似乎是正确的做法,但您可以用展平替换过滤器:
1
至于(for {
i1 <- parentItems;
i2 <- childItems
} yield {
if (isChild(i1, i2)) Some(i1.copy(freeItem = Some(i2.copy())))
else None
}).flatten
,如果你真的需要一个嵌套循环并且你担心性能,那么你的代码看起来不错。也许有人有更好的主意。
答案 2 :(得分:0)
一种稍微不正统的方法,但我相信会更高效,是连接category
和code
以形成带groupBy
的地图的关键:
val childItemMap: Map[String, Iterable[Item]] = childItems
.groupBy(item => s"${item.category}-${item.code}")
然后你只需迭代parentItem
,连接他们的类别和代码,并将地图值复制到父母中:
val parentsWithChildren: Seq[Item] = parentItems
.map { p =>
p.copy(freeItem = childItemMap.get(s"${p.category}-${p.code}").map(_.head))
}
您的数据模型表明只有一个可能匹配,所以我只抓取第一个(可能只有)匹配值。我使用head
而不是headOption
,因为我使用Map
生成了groupBy
,因此永远不会有空值。结果是Seq[Item]
,子匹配被复制到父项中。