如何将数组“映射”到一个新对象?

时间:2016-08-07 17:59:16

标签: arrays scala dictionary reduce

我有一个Array [Float],我想使用该数组的元素初始化一个新对象。

以经典,必要的方式,我当然可以做到:

val array: Array[Float] = ...
val rect: Rect = new Rect(Point(array(0), array(1), Size(array(2), array(3))

有没有办法以更实用的方式使用某种类型的“地图”?像这样:

array => new Rect(Point(_(0), _(1)), Size(_(2), _(3)))

我正在尝试解析CSV文件:

string.split("\n") map {
  line => line.split(".") map { 
    value => value.trim.toDouble
  } => new Rect(Point(_(0), _(1)), Size(_(2), _(3))) // something like this?
}

3 个答案:

答案 0 :(得分:2)

阵列的大小是否固定?您可以使用模式匹配:

.map { case Array(x, y, w, h) => new Rect(Point(x, y), Size(w, h)) }

示例:

scala> "1,2,3,4\n5,6,7,8".split("\n").map(_.split(",")).map { case Array(x,y,w,h) => (x,y,w,h) }
res3: Array[(String, String, String, String)] = Array((1,2,3,4), (5,6,7,8))

答案 1 :(得分:2)

不确定是否正常运行,但重复变量名称4次,如array(n)可以通过解构来改善:

val Array(x, y, w, h) = "1,2,3,4".split(",").map(_.trim.toDouble)
new Rect(Point(x, y), Size(w, h))

(类似于Markus1189的答案,只是它不必是匿名函数)

我会说它更多是关于可读性而不是功能

答案 2 :(得分:1)

上述建议很好但容易出错,它使用了一个提取器,例如Scala添加的数组unnapply。如果CSV文件已损坏,我看到的问题是获得MatchError

这种方法只会跳过文件中与条件不符的行,但如果您只想取前4行呢?

csv.lines.map {
  case Array(x, y, w, h) => Some(new Rect(Point(x, y), Size(w, h)))
  case _ => None
} flatten

如果你想简单地提取前四个变量,即使CSV行有超过4个元素,你也可以使用略有不同的语法,这就是说该行应该有“至少4个元素”

csv.lines.map {
   case Array(x, y, w, h, _*) => Some(new Rect(Point(x, y), Size(w, h)))
   case _ => None
} flatten

您甚至可以使用tail @ _*语法获取对“其余元素”的引用。如果您希望对解析进行更细粒度的控制,并且有一些很好的错误报告:

val detailed: Array[Try[Rect]] = csv.lines.map {
  case Array(x, y, w, h, tail @ _: *) => {
    Success(new Rect(Point(x, y), Size(w, h)))
  }
  case arr @ _ => Failure(s"Entry had ${arr.size} elements, expected at least 4. ${arr.mkString(",")}")
}

为了更好地控制流量,我将map逻辑分开为parseRow(input: Array[String]): Try[Rect]

def parseRow(input: Array[String]): Try[Rect] = input match {
  case Array(x, y, w, h, tail @ _: *) => {
    Success(new Rect(Point(x, y), Size(w, h)))
  }
  case arr @ _ => Failure(s"Entry had ${arr.size} elements, expected at least 4. ${arr.mkString(",")}")
}

您甚至可以考虑对您必须执行的任何转换进行更细粒度的处理,从实例从字符串输入转到DoubleRect需要的任何数学类型。

以下是我将使用的完整设置:

  case class Point(x: Double, y: Double)
  case class Size(x: Double, y: Double)
  class Rect(val p: Point, val s: Size)

  def parseDouble(input: String): Try[Double] = Try(input.toDouble)

  def parseRow(input: Array[String]): Try[Rect] = input match {
    case Array(x, y, w, h, tail @ _*) => {
      for {
        pointX <- parseDouble(x)
        pointY <- parseDouble(y)
        pointW <- parseDouble(w)
        pointY <- parseDouble(y)
      } yield {
        new Rect(Point(pointX, pointY), Size(pointW, pointY))
      }
    }
    case arr @ _ => Failure(new Exception(s"Entry had ${arr.length} elements, expected at least 4. ${arr.mkString(",")}"))
  }

你可以很容易地重复使用上述内容:

val rects = csv.lines.map(parseRow)