Scala Seq对变换

时间:2016-04-28 10:59:44

标签: scala

我有以下要求,并希望转换为我想要的形式。根据输入,我想根据每个类的id键对输出做一些调整。

case class Day(id: Int, name: String)
case class Shift(id: Int, dayId: Int)
case class Break(id: Int, shiftId: Int)

val day1 = Day(1, "xx")
val day2 = Day(2, "xx")
val day3 = Day(3, "xx")

val shift1 = Shift(1, 1)
val shift2 = Shift(2, 1)
val shift3 = Shift(3, 2)

val break1 = Break(1, 1)
val break2 = Break(2, 3)

val input = Seq(
  ((day1, Some(shift1)), Some(break1)),
  ((day1, Some(shift2)), None),
  ((day2, Some(shift3)), Some(break2)),
  ((day3, None), None)
)

def convert(input: Seq[((Day, Option[Shift]), Option[Break])]): Seq[(Day, Seq[(Shift, Seq[Break])])] = {
  ???
}

val output = Seq(
  (day1, Seq((shift1, Seq(break1)), (shift2, Seq()))),
  (day2, Seq((shift3, Seq(break2)))),
  (day3, Seq())
)

任何人都有一些想法最好的方法吗?感谢。

3 个答案:

答案 0 :(得分:2)

如果您将输入结构更改为:

  val input = Seq(
    (day1, Some(shift1), Some(break1)),
    (day1, Some(shift2), None),
    (day2, Some(shift3), Some(break2)),
    (day2, None, None)
  )

(你跳过了元组的嵌套)

然后你可以使用下面的转换函数:

def convert(input: Seq[(Day, Option[Shift], Option[Break])]): Seq[(Day, Seq[(Shift, Seq[Break])])] = {
    val a = input.groupBy(_._1).map { case (day, gr) =>
        (day, gr.collect { case (_, Some(shift), breakOpt) =>
                (shift, breakOpt)
            }.groupBy(_._1).toSeq.map { case (shift, sq) => (shift, sq.flatMap(_._2))})
    }
    a.toSeq
}

最重要的是你只需要分组两次。一天一次,另一次轮班。其余的代码只是转换为您想要的输出格式。

转换可以这样做:

def flatten[A, B, C](t: ((A, B), C)) = (t._1._1, t._1._2, t._2)

然后:

convert(input.map(flatten))

答案 1 :(得分:2)

case class Day(id: Int, name: String)
case class Shift(id: Int, dayId: Int)
case class Break(id: Int, shiftId: Int)

val day1 = Day(1, "xx")
val day2 = Day(2, "xx")
val day3 = Day(3, "xx")

val shift1 = Shift(1, 1)
val shift2 = Shift(2, 1)
val shift3 = Shift(3, 2)

val break1 = Break(1, 1)
val break2 = Break(2, 3)

val input = Seq(
  ((day1, Some(shift1)), Some(break1)),
  ((day1, Some(shift2)), None),
  ((day2, Some(shift3)), Some(break2)),
  ((day2, None), None)
)

def convert(input: Seq[((Day, Option[Shift]), Option[Break])]): Seq[(Day, Seq[(Shift, Seq[Break])])] = {
  input.groupBy(_._1._1).toSeq.map(d=>(d._1,d._2.groupBy(_._1._2).filter(_._1.isDefined).toSeq.map(s=>(s._1.get,s._2.flatMap(_._2)))))
}

val output = Seq(
  (day1, Seq((shift1, Seq(break1)), (shift2, Seq()))),
  (day2, Seq((shift3, Seq(break2)))),
  (day3, Seq())
)

答案 2 :(得分:0)

也许是这样的。代码有点长,但它适用于您的特定输入。它也可以重构,因为两个部分函数几乎相同。

case class Day(id: Int, name: String)

case class Shift(id: Int, dayId: Int)

case class Break(id: Int, shiftId: Int)

val day1 = Day(1, "xx")
val day2 = Day(2, "xx")
val day3 = Day(3, "xx")

val shift1 = Shift(1, 1)
val shift2 = Shift(2, 1)
val shift3 = Shift(3, 2)

val break1 = Break(1, 1)
val break2 = Break(2, 3)

val input = Seq(
  ((day1, Some(shift1)), Some(break1)),
  ((day1, Some(shift2)), None),
  ((day2, Some(shift3)), Some(break2)),
  ((day2, None), None)
)

type MyReturnType = (Day, Seq[(Shift, Seq[Break])])

def convert(input: Seq[((Day, Option[Shift]), Option[Break])]): Seq[MyReturnType] = {
  input.foldLeft(Seq[MyReturnType]()) {
    case (acc, ((day, Some(shift)), Some(break))) =>

      val findExistingDay = acc.find { case (d, _) => d == day }
      val seqWithoutDay = acc.filter { case (d, _) => d != day }


      val addNewElementIfDayExists = findExistingDay.map { case (d, seq) =>
        (d, seq :+(shift, Seq(break))) +: seqWithoutDay
      }

      val otherwiseCreateANewOne = addNewElementIfDayExists.getOrElse((day, Seq((shift, Seq(break)))) +: acc)
      otherwiseCreateANewOne

    case (acc, ((day, Some(shift)), None)) =>

      val findExistingDay = acc.find { case (d, _) => d == day }
      val seqWithoutDay = acc.filter { case (d, _) => d != day }


      val addNewElementIfDayExists = findExistingDay.map { case (d, seq) =>
        (d, seq :+(shift, Seq())) +: seqWithoutDay
      }

      val otherwiseCreateANewOne = addNewElementIfDayExists.getOrElse((day, Seq((shift, Seq()))) +: acc)
      otherwiseCreateANewOne

    case (acc, ((day, None), Some(break))) =>
      ??? //I don't know what should I do in this case, because you didn't provide an example
    case (acc, ((day, None), None)) =>
      acc
  }
}
val convertResult: Seq[MyReturnType] = convert(input)

convertResult.foreach(println)
/* result:
(Day(2,xx),List((Shift(3,2),List(Break(2,3)))))
(Day(1,xx),List((Shift(1,1),List(Break(1,1))), (Shift(2,1),List())))
res0: Unit = ()
 */

//if you need it in a specific order:

convertResult.sortBy(_._1.id).foreach(println)
/* result:
(Day(1,xx),List((Shift(1,1),List(Break(1,1))), (Shift(2,1),List())))
(Day(2,xx),List((Shift(3,2),List(Break(2,3)))))
res1: Unit = ()
 */

//if you also need an empty day3, then you have to somehow group the all days. Maybe like this::
val allDays = List(day1, day2, day3)
val withEmptyDays: Seq[MyReturnType] = allDays.map(d => convertResult.find(_._1 == d).getOrElse((d, Seq())))
withEmptyDays.foreach(println)
/*
(Day(1,xx),List((Shift(1,1),List(Break(1,1))), (Shift(2,1),List())))
(Day(2,xx),List((Shift(3,2),List(Break(2,3)))))
(Day(3,xx),List())
res2: Unit = ()
 */


val output = Seq(
  (day1, Seq((shift1, Seq(break1)), (shift2, Seq()))),
  (day2, Seq((shift3, Seq(break2)))),
  (day3, Seq())
)

当然这是SCALA,你可以用简洁的方式写出来:

 def convert(input: Seq[((Day, Option[Shift]), Option[Break])]): Seq[(Day, Seq[(Shift, Seq[Break])])] = {
   def helper(acc:Seq[(Day, Seq[(Shift, Seq[Break])])], day:Day, shift:Shift, breaks:Seq[Break]) =
     acc.find(_._1 == day).map(a =>(a._1, a._2 :+ (shift, breaks)) +: acc.filter(_._1 != day)).getOrElse((day, Seq((shift, breaks))) +: acc)
   input.foldLeft(Seq[(Day, Seq[(Shift, Seq[Break])])]()) {
     case (acc, ((day, Some(shift)), Some(break))) => helper(acc, day, shift, Seq(break))
     case (acc, ((day, Some(shift)), None)) => helper(acc, day, shift, Seq())
     case (acc, ((day, None), Some(break))) =>
       ???
     case (acc, ((day, None), None)) =>
       acc
   }
 }