基于Scala的业务逻辑将两个对象列表合并为一个

时间:2016-02-18 21:25:11

标签: list scala collections

  

继续Scala学习曲线

我有两个对象列表。我需要将这些列表合并到一个列表中,同时应用具有匹配削减的逻辑。

因此,例如,以下是两个列表:

case class test(int: Int, str: String)

val obj1 = test(1, "one")
val obj2 = test(2, "two")
val list1 = List(obj1, obj2)


val obj3 = test(2, "Another two")
val obj4 = test(4, "four")
val list2 = List(obj1, obj2)

我需要的是:

List(test(1, "one old"), test(2, "Another two updated"), test(4, "four new"))

粗略地说,我可以用老式的方式迭代所有元素,并在那里进行所有转换,但这不是" Scala方式" (我猜)。

我尝试用foldLeft接近它,但卡住了。以下是我的工作:

list1.foldLeft(list2) { (a:test, b:test) =>
    b.int match {
        case a.int => {
           //Apply logic and create new object
        }
    }
}

更新 现在我分两步完成:

var tasks : Seq[ChecklistSchema.Task] = left.tasks.map((task:ChecklistSchema.Task) =>
            right.tasks.find(t => t.groupId == task.groupId) match {
                case Some(t: ChecklistSchema.Task) => t
                case _ => {
                    task.status match {
                        case TaskAndValueStatus.Active => task.copy(status = TaskAndValueStatus.Hidden)
                        case _ => task
                    }
                }
            }
        )

        tasks = tasks ++ right.tasks.filter((t:ChecklistSchema.Task) => !tasks.contains(t))

必须有更好的方法!

谢谢,

3 个答案:

答案 0 :(得分:2)

*假设val list2 = List(obj3, obj4)

以下是我的方法:

  1. 申请" old"所有list1条目
  2. 为list2创建一个映射,以便有效地检查(在下一个方法中)重复值是否来自list2。 (breakOut此处指示编译器使用最合适的工厂构建它。更多信息https://stackoverflow.com/a/7404582/4402547
  3. applyLogic决定将什么称为不久的测试("新"或"更新")
  4. 将它们放在一起,groupBy放在索引上,applyLogic和排序(可选)。
  5. def merge(left: List[Test], right: List[Test]) = {
      val old = list1.map(t => Test(t.int, t.str+" old"))
      val l2Map = list2.map(t => (t.int -> t)) (breakOut): Map[Int, Test]
    
      def applyLogic(idx: Int, tests: List[Test]): Test = {
        tests.size match {
          case 1 => {
            val test = tests.head
            if(l2Map.contains(test.int)) Test(test.int, test.str + " new") else test
          }
          case 2 => {
            val updated = tests(1)
            Test(updated.int, updated.str+" updated")
          }
        }
      }
    
      (old ++ list2).groupBy(t => t.int).map(f => applyLogic(f._1, f._2)).toList.sortBy((t => t.int))
    }
    
    
    val left = List(Test(1, "one"), Test(2, "two"))
    val right = List(Test(2, "Another two"), Test(4, "four"))
    val result = List(Test(1, "one old"), Test(2, "Another two updated"), Test(4, "four new"))
    assert(merge(left, right) == result)
    

答案 1 :(得分:1)

我不知道这个解决方案是否是“Scala方式”,但它正在使用foldLeft

  case class Test(a: Int, b: String) {
    def labeled(label: String) = copy(b = b + " " + label)
  }

  def merge(left: List[Test], right: List[Test]) = {
    val (list, updated) = left.foldLeft((List[Test](), Set[Int]())) { case ((acc, founded), value) =>
      right.find(_.a == value.a) match {
        case Some(newValue) => (newValue.labeled("updated") :: acc, founded + value.a)
        case None => (value.labeled("old") :: acc, founded)
      }
    }

    list.reverse ::: right.filterNot(test => updated(test.a)).map(_.labeled("new"))
  }

  val left = List(Test(1, "one"), Test(2, "two"))
  val right = List(Test(2, "Another two"), Test(4, "four"))
  val result = List(Test(1, "one old"), Test(2, "Another two updated"), Test(4, "four new"))
  assert(merge(left, right) == result)

答案 2 :(得分:0)

(list1 ++ (list2.map(l => l.copy(str = l.str + " new")))).groupBy(_.int).map(
  l =>
    if (l._2.size >= 2) {
      test(l._2(0).int, "Another two updated")
    } else l._2(0)
)

地图更新新值并使用 groupBy 更新不同