根据字段值过滤集合

时间:2014-07-27 18:23:23

标签: scala

我有这个集合

class ConvEntry(designation: String, saeThick: Double, common: Boolean)
val convList = immutable.List(
      new ConvEntry("16 Gauge", 0.0598, true),
      new ConvEntry("1/16th Inch", 0.0625, true),
      new ConvEntry("15 Gauge", 0.0673, false),
      new ConvEntry("14 Gauge", 0.0747, false),
      new ConvEntry("13 Gauge", 0.0897, false),
      new ConvEntry("12 Gauge", 0.1046, true),
      new ConvEntry("11 Gauge", 0.1196, false),
      new ConvEntry("1/8th Inch", 0.1250, true),
      new ConvEntry("10 Gauge", 0.1345, false),
      new ConvEntry("0.160 Inch", 0.1600, false),
      new ConvEntry("8 Gauge", 0.1644, false),
      new ConvEntry("3/16th Inch", 0.1875, true),
      new ConvEntry("0.190 Inch", 0.1900, false),
      new ConvEntry("0.204 Inch", 0.2040, false),
      new ConvEntry("1/4 Inch", 0.2500, true),
      new ConvEntry("5/16th Inch", 0.3125, true),
      new ConvEntry("3/8th Inch", 0.3750, true),
      new ConvEntry("7/16th Inch", 0.4375, true),
      new ConvEntry("1/2 Inch", 0.5000, true),
      new ConvEntry("9/16th Inch", 0.5625, true),
      new ConvEntry("5/8th Inch", 0.6250, true),
      new ConvEntry("11/16th Inch", 0.6875, true),
      new ConvEntry("3/4th Inch", 0.7500, true),
      new ConvEntry("13/16th Inch", 0.8125, true),
      new ConvEntry("7/8 Inch", 0.8750, true),
      new ConvEntry("1 Inch", 1.0000, true),
      new ConvEntry("1 1/4 Inch", 1.2500, true),
      new ConvEntry("1 1/2 Inch", 1.5000, true),
      new ConvEntry("1 3/4 Inch", 1.7500, true),
      new ConvEntry("2 Inch", 2.0000, true),
      new ConvEntry("2 1/2 Inch", 2.5000, true)
)

我试图弄清楚如何做的是根据字段中的各种值过滤集合。我需要一个基于真值列表的算法,一个错误值列表,我需要在给定数字的正上方和下方找到该条目。

这可能与集合或我需要做旧式强力循环方法。

4 个答案:

答案 0 :(得分:2)

收集真或假值:

Collection[ConvEntry]上有一个过滤函数,它从ConvEntryBoolean取一个函数,过滤函数的输出将是Collection[ConvEntry],包含那些项目你的函数返回true。

例如,对于真正的条目:

val filteredList = convList.filter(_.common)

和虚假条目:

val filteredList = convList.filterNot(_.common)

对于项目之前和之后的元素,可以使用indexWhere和一个检查值的函数,然后使用index - 1和index + 1来获取上一个和下一个项目。这是假设按前一个和下一个你的意思是根据列表已经在的顺序。尽管使用Boris建议的二进制搜索,评论中的蜘蛛会更高效。

请注意,如果您按索引访问项目,则使用Vector可能更合适。有关各种集合类型的性能特征的说明,请参阅here

答案 1 :(得分:1)

使用分区,对于此用例,它优于filter

val (commonConvs, uncommonConvs) = convList.partition(_.common)

以下函数在面值上有点复杂,但意味着您只需要遍历列表一次(如果使用indexWhere,则需要遍历3次)。所以它是O(N)

val init = (Option.empty[ConvEntry], Option.empty[ConvEntry], 
  Option.empty[ConvEntry])

val (prevOpt, elemOpt, nextOpt) = convList.foldLeft(init) {
  case ((prevOpt, None, _), cur) if cur.designation == "blar blar" => 
    (prevOpt, Some(cur), None)
  case ((_, None, _), cur) => (Some(cur), None, None)
  case ((prevOpt, Some(elem), None), cur) => (prevOpt, Some(elem), Some(cur))
  case ((prevOpt, Some(elem), Some(next)), _) => (prevOpt, Some(elem), Some(next))
}

请注意,3个结果属于Option[ConvEntry]类型,因为不能保证任何3个值实际存在。

这是最好的情况O(N),因为它使用foldLeft,但如果你使用while循环,你可以得到最好的情况O(1)。你也可以使用尾递归,但是循环通常要快一点。

答案 2 :(得分:0)

对于true和false值,您可以使用partition

val(trueValues,falseValues)= convList.partition(_。common)

对于某些值附近的值,您可能希望将序列转换为TreeSet。此外,将您的班级视为案例类更为方便。

import scala.collection.immutable.TreeSet
case class ConvEntry(designation: String, saeThick: Double, common: Boolean)

val convList = List(
      ConvEntry("16 Gauge", 0.0598, true),
        // the rest of your list goes here ...
      ConvEntry("2 1/2 Inch", 2.5000, true)
)

val myOrdering = Ordering.fromLessThan[ConvEntry]{(x,y) => x.saeThick < y.saeThick}
val ts = TreeSet.empty(myOrdering) ++ convList

def beforeAndAfter(saeThick:Double, ts:TreeSet[ConvEntry]) = {
    (for (item<-ts.find(_.saeThick==saeThick)) yield {
      (ts.to(item).take(1) ++ ts.from(item).take(2)).toList
    }).getOrElse(List[ConvEntry]())
}

scala> beforeAndAfter(0.204, ts)
res12: List[ConvEntry]] = List(ConvEntry(16 Gauge,0.0598,true), ConvEntry(0.204 Inch,0.204,false), ConvEntry(1/4 Inch,0.25,true))

scala> beforeAndAfter(0.625, ts)
List[ConvEntry] = List(ConvEntry(16 Gauge,0.0598,true), ConvEntry(5/8th Inch,0.625,true), ConvEntry(11/16th Inch,0.6875,true))

答案 3 :(得分:0)

谢谢大家。我拿走了每个人的一些建议。这就是我最终的结果,仍在编写单元测试以确保其正常工作

trait Converter {

  val pointTwo = (ret: Double) => BigDecimal(ret).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
  val pointFour = (ret: Double) => BigDecimal(ret).setScale(4, BigDecimal.RoundingMode.HALF_UP).toDouble

  val convList = immutable.List[ConvEntry](
    new ConvEntry("16 Gauge", 0.0598, true),
    new ConvEntry("1/16th Inch", 0.0625, true),
    new ConvEntry("15 Gauge", 0.0673, false),
    new ConvEntry("14 Gauge", 0.0747, false),
    new ConvEntry("13 Gauge", 0.0897, false),
    new ConvEntry("12 Gauge", 0.1046, true),
    new ConvEntry("11 Gauge", 0.1196, false),
    new ConvEntry("1/8th Inch", 0.1250, true),
    new ConvEntry("10 Gauge", 0.1345, false),
    new ConvEntry("0.160 Inch", 0.1600, false),
    new ConvEntry("8 Gauge", 0.1644, false),
    new ConvEntry("3/16th Inch", 0.1875, true),
    new ConvEntry("0.190 Inch", 0.1900, false),
    new ConvEntry("0.204 Inch", 0.2040, false),
    new ConvEntry("1/4 Inch", 0.2500, true),
    new ConvEntry("5/16th Inch", 0.3125, true),
    new ConvEntry("3/8th Inch", 0.3750, true),
    new ConvEntry("7/16th Inch", 0.4375, true),
    new ConvEntry("1/2 Inch", 0.5000, true),
    new ConvEntry("9/16th Inch", 0.5625, true),
    new ConvEntry("5/8th Inch", 0.6250, true),
    new ConvEntry("11/16th Inch", 0.6875, true),
    new ConvEntry("3/4th Inch", 0.7500, true),
    new ConvEntry("13/16th Inch", 0.8125, true),
    new ConvEntry("7/8 Inch", 0.8750, true),
    new ConvEntry("1 Inch", 1.0000, true),
    new ConvEntry("1 1/4 Inch", 1.2500, true),
    new ConvEntry("1 1/2 Inch", 1.5000, true),
    new ConvEntry("1 3/4 Inch", 1.7500, true),
    new ConvEntry("2 Inch", 2.0000, true),
    new ConvEntry("2 1/2 Inch", 2.5000, true)
  )

  def resolveDesignation(intake: Double, commonParam: Boolean, round: Rounder): ConvEntry = {
    val tpl = loopSearch(convList.filter {_.common == commonParam }, intake)
    matchRound(intake, tpl, round)
  }

  def matchRound(target: Double, x: (ConvEntry, ConvEntry), round: Rounder): ConvEntry = round match {
    case Rounder.Up => x._2
    case Rounder.Down => x._1
    case Rounder.Closest if (target - x._1.saeThick) < (x._2.saeThick - target ) =>  x._1
    case Rounder.Closest if (target - x._1.saeThick) >= (x._2.saeThick - target ) =>  x._2
  }

  def loopSearch(list: List[ConvEntry], target: Double): (ConvEntry, ConvEntry) = {
    var prev: ConvEntry = null

    list.foreach { x =>
      if (x.saeThick == target) return (null, x)
      else if (x.saeThick > target) return (prev, x)
      else prev = x
    }
    (null, null)
  }
}