比较两个集合的所有元素并生成第三个集合

时间:2017-03-23 19:45:34

标签: scala collections nested-loops scala-collections for-comprehension

我正在寻找一种比较两个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)))
)

我知道上面的代码有漏洞但是没关系。我在寻找的是:

  1. 有什么方法可以避免if(isChild(i1,i2))Some(i1.copy(freeItem = Some(i2.copy()))) else None,结果是.filter(_.isInstanceOf[Some[_]]).map(_.get)?部分功能是一种选择吗?

  2. 我在这里使用嵌套循环,我会在Java中做类似的事情。有没有其他方法可以接近它?起初我认为我zip但显然至少不起作用。

  3. 提前致谢!

3 个答案:

答案 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)

一种稍微不正统的方法,但我相信会更高效,是连接categorycode以形成带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],子匹配被复制到父项中。