0

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

위의 조각은 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이라고 생각했지만 적어도 분명히 작동하지 않습니다.

미리 감사드립니다. 1에 관한

답변

2

나는 이런 식으로 뭔가를 시도 유혹 될 것이다 :

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

희망하는 데 도움이! 그런 다음 당신은 그냥 parentItem 반복

val childItemMap: Map[String, Iterable[Item]] = childItems 
    .groupBy(item => s"${item.category}-${item.code}") 

: 좀 더 성능이 좋은 것 생각

0

는 옵션을 반환하는 것은 옳은 일처럼 보인다, 그러나 당신은 펼치기로 필터 교체 할 수 있습니다 : 당신이 정말로 중첩 루프를 필요로하는 경우

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

약간 정통 접근하지만, 하나는 groupBy와지도의 핵심을 형성 categorycode을 연결하는 것입니다 S, 자신의 범주와 코드을 연결하고, 부모에 맵 값을 복사

val parentsWithChildren: Seq[Item] = parentItems 
    .map { p => 
     p.copy(freeItem = childItemMap.get(s"${p.category}-${p.code}").map(_.head)) 
    } 

귀하의 데이터 모델은 하나의 가능한 일치가 있음을 시사하므로 첫 번째 일치 (아마도 유일한) 일치 값만 잡습니다. 나는 headOption이 아닌 head을 사용하고 있습니다. 왜냐하면 groupBy으로 Map을 생성했기 때문에 결코 빈 값이 없기 때문입니다. 결과는 Seq[Item]이며 일치하는 내용은 부모와 일치합니다.

+0

답을 제공해 주셔서 감사합니다.필자의 경우 완벽하게 작동하지만 약간 더 복잡한 모델이 있으며 다른 사람들이 따라하기가 어려워졌습니다. 나는 그 성능을 측정하지는 못했지만 그것에 대해 제대로 할 수는있다. :) – Angelos

+0

괜찮 았지 만, 여전히 내가 설명하는 것이 아니더라도'Map' 기반 접근법을 사용해야한다고 생각한다. – Vidya

+0

이유에 대해 설명해 주시겠습니까? 감사합니다. – Angelos