比较两个集合的所有元素并生成第三个集合
Compare all elements of two collections and produce a third one
我正在寻找一种比较两个 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
但显然是行不通的,至少它自己行不通。
提前致谢!
关于 1
,返回选项似乎是正确的做法,但您可以用展平替换过滤器:
(for {
i1 <- parentItems;
i2 <- childItems
} yield {
if (isChild(i1, i2)) Some(i1.copy(freeItem = Some(i2.copy())))
else None
}).flatten
至于2
,如果你真的需要一个嵌套循环并且你担心性能,你的代码对我来说看起来不错。也许有人有更好的主意。
我很想尝试这样的事情:
val parentsWithChildren: Seq[Item] =
for {
parent <- parentItems
child <- childItems if isChild(parent, child)
}
yield parent.copy(freeItem = Some(child))
希望对您有所帮助!
一种略微非正统的方法,但我认为这种方法性能更高,将连接 category
和 code
以形成具有 groupBy
的映射的键:
val childItemMap: Map[String, Iterable[Item]] = childItems
.groupBy(item => s"${item.category}-${item.code}")
然后您只需遍历 parentItem
s,连接 它们的 类别和代码,并将映射值复制到父项中:
val parentsWithChildren: Seq[Item] = parentItems
.map { p =>
p.copy(freeItem = childItemMap.get(s"${p.category}-${p.code}").map(_.head))
}
您的数据模型表明只有一个可能的匹配项,因此我只获取第一个(并且可能是唯一的)匹配值。我使用的是 head
而不是 headOption
,因为我用 groupBy
生成了 Map
,因此永远不会有空值。结果是 Seq[Item]
,其中子项匹配复制到父项中。
我正在寻找一种比较两个 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
但显然是行不通的,至少它自己行不通。
提前致谢!
关于 1
,返回选项似乎是正确的做法,但您可以用展平替换过滤器:
(for {
i1 <- parentItems;
i2 <- childItems
} yield {
if (isChild(i1, i2)) Some(i1.copy(freeItem = Some(i2.copy())))
else None
}).flatten
至于2
,如果你真的需要一个嵌套循环并且你担心性能,你的代码对我来说看起来不错。也许有人有更好的主意。
我很想尝试这样的事情:
val parentsWithChildren: Seq[Item] =
for {
parent <- parentItems
child <- childItems if isChild(parent, child)
}
yield parent.copy(freeItem = Some(child))
希望对您有所帮助!
一种略微非正统的方法,但我认为这种方法性能更高,将连接 category
和 code
以形成具有 groupBy
的映射的键:
val childItemMap: Map[String, Iterable[Item]] = childItems
.groupBy(item => s"${item.category}-${item.code}")
然后您只需遍历 parentItem
s,连接 它们的 类别和代码,并将映射值复制到父项中:
val parentsWithChildren: Seq[Item] = parentItems
.map { p =>
p.copy(freeItem = childItemMap.get(s"${p.category}-${p.code}").map(_.head))
}
您的数据模型表明只有一个可能的匹配项,因此我只获取第一个(并且可能是唯一的)匹配值。我使用的是 head
而不是 headOption
,因为我用 groupBy
生成了 Map
,因此永远不会有空值。结果是 Seq[Item]
,其中子项匹配复制到父项中。