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

14ifxucb  于 2023-06-06  发布在  Scala
关注(0)|答案(4)|浏览(181)

Scala学习曲线的延续
我有两个对象列表。我需要将这些列表合并为一个列表,同时应用具有匹配pares的逻辑。
例如,这里有两个列表:

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

一定有更好的办法!
谢谢

dluptydi

dluptydi1#

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

以下是我的方法:
1.将“旧”应用于所有list1条目
1.为list2创建一个Map,以便有效地检查(在下一个方法中)是否有重复的值来自list2。(breakOut在这里指示编译器使用最合适的工厂来构建它。更多信息,请访问https://stackoverflow.com/a/7404582/4402547

  1. applyLogic决定如何称呼一个非旧测试(“新”或“更新”)
    1.将它们放在一起,索引上的groupByapplyLogic和排序(可选)。
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)
iih3973s

iih3973s2#

我不知道这个解决方案是否是“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)
4uqofj5v

4uqofj5v3#

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

map更新新值,groupBy更新distinct

gcuhipw9

gcuhipw94#

我建议使用 Set 而不是List,并像下面的代码一样覆盖你的case类equals函数:

val obj1 = test(1, "one")
val obj2 = test(2, "two")
val set1 = Set(obj1, obj2)

val obj3 = test(2, "Another two")
val obj4 = test(4, "four")
val set2 = Set(obj3, obj4)

println((set2 ++ set1))

case class test(i: Int, str: String) {
    override def equals(obj: Any): Boolean = this.i == obj.asInstanceOf[test].i
}

要知道在组合中使用集合的顺序很重要,并且会给予不同的结果。
输出是这样的:Set(test(2,Another two),test(4,four),test(1,one))
你也可以保留你的列表,做三个额外的转换,像这样:

println((list2.toSet ++ list1.toSet).toList)

相关问题