Scala根据条件减少List

时间:2015-09-04 18:15:19

标签: list scala reduce

我根据条件有一个我想要减少的特定类型的列表。我有一个类型,其中Interval是一个具有开始和结束的DateTime间隔:

case class MyType(a: Interval, value: Double)

我有一个List [MyType]条目,我希望根据包含相同DateTime和值的MyType将其缩减为List [MyType]。我不想两次查看我已经做过的列表。

说我有:

val a = MyType(interval1, 2)
val b = MyType(interval2, 2)
val c = MyType(interval3, 1)
val d = MyType(interval4, 6)
val e = MyType(interval5, 2)

val original = List(a, b, c, d, e)

我现在必须根据以下条件减少原始列表:

1. interval should be continuous, then take the start of the first entry and the end of the second entry
2. the double value should be the same

因此假设interval1,interval2是连续的,结果应如下所示:

val result = Seq(MyType(new Interval(a.interval.start, b.interval.end),2), c, d, e) 

是否有更优雅的解决方案或想法?

4 个答案:

答案 0 :(得分:4)

在reduce函数中,检查条件是否为真,如果是,则返回当前累加器而不是你计算的值。

这里是你如何只对偶数求和:

Seq(1,4,6,3).foldLeft(0)( (acc, a) =>
    if (a % 2 == 0) acc + a else acc
)
res5: Int = 10

对编辑过的问题的回复:看来你有一些条件必须要遵守这些要素。然后,您可以应用函数.sliding

Seq(a,b,c,d,e).sliding(2).foldLeft(0)(
    case (acc, Seq(MyType(ai, a), MyType(bi, b))) =>
        if (ai.max == bi.min) acc + a else acc
)

Buuut ...... 您可能已经猜到它不会像您希望的那样高效。我希望你没有做任何过早的优化,因为你知道,这是所有邪恶的根源。但是如果你真的需要性能,可以用while循环重写代码(回退到Java)。

答案 1 :(得分:2)

这应该有效:

def reduce(xs: List[MyType]) = {
  xs match {
    case a :: b :: tail =>
      if(a.interval.end == b.interval.start && a.value == b.value)
        reduce(MyType(new Interval(a.interval.start, b.interval.end) a.value) :: tail)
      else
        a :: reduce(b :: tail)
    case _ => xs
  }
}

if条件可能需要稍微调整,具体取决于您的确切需求,但算法应该有效。

  1. 给出一个列表xs

    1. 如果前两项ab可以合并到c,请将它们合并,然后使用xs = c :: tail
    2. 返回第1步
    3. 如果无法合并ab,请尝试减少第一个元素,并将结果附加到a
    4. 否则(列表包含1个元素或为空),返回xs

答案 2 :(得分:0)

请注意,您的任务可能会产生多个不同的解决方案,这些解决方案无法进一步降低。 因此,您将获得一组解决方案:Set[Set[MyType]]

我使用Set[MyType]代替提议的List[MyType]Seq[MyType],因为顺序并不重要,我的答案需要比较不同的解决方案(以避免重复)。

我的回答并不是对项目顺序做出假设,任何顺序都可以。

除此之外为了简化代码,我已将Interval替换为2个字段fromto,这些字段可以轻松转换。

以下是减少代码:

case class MyType(from: Long, to: Long, value: Double)

object MyType {
  //Returns all possible varians of reduced source.
  //If reduction is not possible, returns empty set.
  private def strictReduce(source: Set[MyType]): Set[Set[MyType]] = {
    if (source.size <= 1) {Set.empty} else {
      val active = source.head //get some item
      val otherItems = source.tail //all other items
      val reducedWithActive: Set[Set[MyType]] = otherItems.flatMap {
          case after if active.to == after.from =>
            //we have already found a reduction (active->after),
            //  so further reductions are not strictly required
            reduce(otherItems - after + MyType(active.from, after.to, active.value))
          case before if before.to == active.from =>
            //we have already found a reduction (before->active),
            // so further reductions are not strictly required
            reduce(otherItems - before + MyType(before.from, active.to, active.value))
          case notContinuos => Set.empty[Set[MyType]]
        }
      //check if we can reduce items without active
      val reducedIgnoringActive = strictReduce(otherItems).
        //if so, re-insert active and try to reduce it further, but not strictly anymore
        flatMap (reducedOther => reduce(reducedOther + active))
      reducedWithActive ++ reducedIgnoringActive
    }
  }
  //Returns all possible varians of reduced source.
  //If reduction is not possible, returns source as single result.
  private def reduce(source: Set[MyType]): Set[Set[MyType]] = strictReduce(source) match {
    case empty if empty.isEmpty => Set(source)
    case reduced => reduced
  }
  //Reduces source, which contains items with different values
  def reduceAll(source: Set[MyType]): Set[Set[MyType]] = source.
    groupBy(_.value). //divide by values, because they are not merge-able
    mapValues(reduce). //reduce for every group
    values.reduceLeft((solutionSetForValueA, solutionSetForValueB) =>
    //merge solutions for different groups
    for(subSolutionForValueA <- solutionSetForValueA;
      subSolutionForValueB <- solutionSetForValueB)
      yield (subSolutionForValueA ++ subSolutionForValueB) //merge subSolutions
    )
}

以下是使用它的示例:

object Example extends App {
  val source = Set(
    MyType(0L, 1L, 1.0),
    MyType(1L, 2L, 2.0), //different value
    MyType(1L, 3L, 1.0), //competing with next
    MyType(1L, 4L, 1.0), //competing with prev
    MyType(3L, 5L, 1.0), //joinable with pre-prev
    MyType(2L, 4L, 2.0), //joinable with second
    MyType(0L, 4L, 3.0) //lonely
  )

  val solutions: Set[Set[MyType]] = MyType.reduceAll(source)
  //here you could choose the best solution (for example by size)
  //printing out
  solutions.foreach(solution => println(solution.toList.sortBy(_.from).sortBy(_.value).
      map(item => s"${item.from}->${item.to}(${item.value})").mkString(", ")))
}

我的结果是:

0->5(1.0), 1->4(1.0), 1->4(2.0), 0->4(3.0)
0->4(1.0), 1->5(1.0), 1->4(2.0), 0->4(3.0)

答案 3 :(得分:0)

以下是我提出的建议:

end