0

2つのScalaコレクションの要素を比較し、条件に基づいて3つ目のコレクションを生成する素晴らしい方法を探しています。パフォーマンスのためにコードの外観を犠牲にすることができます。2つのコレクションのすべての要素を比較し、3つ目のコレクションを生成します

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) 

上記のスニペットは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))) 
) 

を以下のように仮定し

。私は何を探していますは、次のとおりです。

  1. は、私がもし(isChild(I1、I2))Some(i1.copy(freeItem = Some(i2.copy()))) else Noneと結果.filter(_.isInstanceOf[Some[_]]).map(_.get)などを避けることができる方法はありますか?部分機能はオプションになりますか?

  2. 私はここでネストされたループを使用しています。私はJavaでそのようなことをします。それにアプローチする他の方法はありますか?私は最初にzipだと思ったが、明らかに少なくともそれ自体は機能しない。

ありがとうございます!

答えて

2

私はこのような何かを試すように誘惑されるだろう:

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

希望に役立つこと!

0

1については、オプションを返すことは行うには正しいことのように思えるが、あなたはフラット化してフィルタを交換することができます:2については

(for { 
    i1 <- parentItems; 
    i2 <- childItems 
    } yield { 
    if (isChild(i1, i2)) Some(i1.copy(freeItem = Some(i2.copy()))) 
    else None 
    }).flatten 

を、あなたが本当にネストされたループを必要とし、あなたが心配している場合パフォーマンス、あなたのコードは私によく見えます。たぶん誰かが良いアイデアを持っているかもしれない。

+0

質問に答える時間をとってくれてありがとう。 'flatten'はうまく動作し、期待される結果が得られます。しかし、@Martinはさらにコンパクトにしました。私は両方の実装が多かれ少なかれ同じ性能を発揮すべきだと思います。 – Angelos

+0

@Angelosええ、彼の解決策はより良い、より可読です – nmat

0

Aやや異例のアプローチが、私は、よりパフォーマンスの高いだろうと信じて1、groupByでマップのキーを形成するためにcategorycodeを連結するために、次のようになります。

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

あなたのデータモデルは可能な一致が1つしかないことを示唆しているので、私は最初に(そしてたぶん唯一の)一致する値を取得しています。私はheadOptionではなく、MapgroupByで生成したので、headを使用しています。したがって、空の値は決してありません。結果はSeq[Item]で、子の一致が親にコピーされます。

+0

この回答を提供していただきありがとうございます。それは私のケースでは完璧に機能しますが、少し複雑なモデルがあり、他の人がそれに従うのが難しくなります。私はそのパフォーマンスを測定しませんでしたが、あなたはそれについて正しいことができます:) – Angelos

+0

これは問題ありませんが、文字通り私が記述したものではないとしても、あなたは 'Map'ベースのアプローチを検討すべきと思います。 – Vidya

+0

あなたはなぜ私に説明できますか、ありがとう? – Angelos

関連する問題