Search code examples
scalacollectionsnested-loopsscala-collectionsfor-comprehension

Compare all elements of two collections and produce a third one


I am looking for a nice way to compare the elements of two Scala collections and produce a third collection based on a condition. I can sacrifice code appearance for performance.

Assuming the following:

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)

The above snippet will produce the following result, which is the excpected:

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

I know that the above code has loopholes but that's fine. What I am looking for is:

  1. Is there any way I can avoid the if (isChild(i1, i2)) Some(i1.copy(freeItem = Some(i2.copy()))) else None and as a result the .filter(_.isInstanceOf[Some[_]]).map(_.get)? Would Partial Function be an option?

  2. I am using nested loop here, I would do something like that in Java. Is there any other way to approach it? I thought I zip at first but obviously doesn't work, by itself at least.

Thanks in advance!


Solution

  • I would be tempted to try something like this:

    val parentsWithChildren: Seq[Item] =
      for {
        parent <- parentItems
        child <- childItems if isChild(parent, child)
      }
        yield parent.copy(freeItem = Some(child))
    

    Hope that helps!