2015-09-04 82 views
1

我有一定類型的列表,我想基於一個條件,以減少列表。我有一個類型,其中間隔與開始和結束日期時間間隔:斯卡拉減少基於條件

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

我已經得到了我想要減少到列表[MyType的]基礎上的MyType包含列表[MyType的]條目相同的DateTime和值。我不想兩次列出我已經做過的列表。

說我有:

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 

所以假設區間1,時間間隔2是連續的,結果應該像:

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

有沒有更優雅的解決方案或想法?

+1

「包含相同的DateTime和值」是什麼意思? 「什麼」是什麼?你能提供一個輸入和輸出的樣本嗎? – dcastro

+0

這並沒有什麼幫助。你沒有提供輸出的樣本,我不知道'interval1','interval2'等是什麼。你又說'雙重價值應該是一樣的',但是又一次 - 和什麼一樣?預定義的值?您可以從幫助中心查看此頁面:[MCVE](http://stackoverflow.com/help/mcve) – dcastro

+0

我添加了我期望的結果! – sparkr

回答

4

在減少功能,檢查條件爲真,如果是,返回當前蓄電池,而不是你會怎樣,否則計算。

這裏是你將如何總結只有偶數:

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

響應編輯的問題:看來你有一些條件必須持有有關consecuitve元素。然後您可以應用功能.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)。

+0

不知道爲什麼downvotes。根據問題中的有限信息,這是一個有效的解決方案。 – Gangstead

+0

我修改了我的問題!對不起,我第一次嘗試就不明確! – sparkr

+0

可能是downvotes出現的原因:1它不起作用(如果'a'和'c'是連續的,'b'是其他地方的。2不清楚,'acc'是什麼(我沒有downvoted :) ) –

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. 如果ab不能到步驟1合併,嘗試減少除第一個以外的所有元素,並將結果追加到a
    3. 否則(list has 1元或爲空),返回xs
+0

它不適用於未排序和重疊間隔。想象一下'1st'和'3rd'項目是可以合併的,但是'2nd'在別的地方或者具有不同的價值。 –

+0

@OlegRudenko這是一個需求?如果是這樣,這個問題就沒有提到(這和模糊可以從頭開始)。 – dcastro

0

收費attantion,您的任務可能會導致多種不同的解決方案,不能被進一步降低。 因此,作爲結果,你會得到一套解決方案:Set[Set[MyType]]

我用Set[MyType],而不是提出List[MyType]Seq[MyType]因爲順序並不重要,我的答案可能需要比較不同的解決方案(以避免重複)。

我的答案不會對物品的順序作出任何假設,任何訂單都可以。

除此之外,爲了簡化代碼,我用兩個字段fromto替換了Interval,它們可以很容易地轉換。

這裏是減少代碼:

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

這裏是我的想出了:

def reduce(accumulator: Seq[MyType], original: Seq[MyType]): Seq[MyType] = original match { 

    case Nil => accumulator 
    case head :: xs => { 
     val found = xs.find(_.timeSpan.getStart().equals(head.timeSpan.getEnd)) 
     if (found.isDefined && found.get.value == head.value) { 
     reduce(
      accumulator :+ (MyType(new Interval(head.timeSpan.getStart, found.get.timeSpan.getEnd), head.value)), 
      original.diff(Seq(found.get, head)) 
     ) 
     } 
     else 
     reduce(
      accumulator :+ head, 
      xs 
     ) 
    } 
    }