【问题标题】:zipping lists with an optional list to construct a list of object in Scala使用可选列表压缩列表以在 Scala 中构造对象列表
【发布时间】:2020-04-07 09:49:08
【问题描述】:

我有一个这样的案例类:

case class Metric(name: String, value: Double, timeStamp: Int)

我收到单独的组件以在单独的列表中构建指标并将它们压缩以创建指标对象列表。

def buildMetric(names: Seq[String], values: Seq[Double], ts: Seq[Int]): Seq[Metric] = {
    (names, values, ts).zipped.toList map {
         case (name, value, time) => Metric(name, value, time)
    }
}

现在我需要为 buildMetric 函数和 Metric 类添加一个可选参数。

case class Metric(name: String, value: Double, timeStamp: Int, type: Option[Type])

&

def buildMetric(names: Seq[String], values: Seq[Double], ts: Seq[Int], types: Option[Seq[Type]]): Seq[Metric]

这个想法是我们有时会收到一个类型的序列,如果存在的话,它与名称和值列表的长度相匹配。我不确定如何修改 buildMetric 函数的主体以惯用地创建具有类型信息的 Metric 对象。我可以想到几种方法。

  1. types.isDefined 上执行 if-else,然后使用 types.get 压缩类型,在一种情况下使用另一个列表,在另一种情况下保持如上。这让我写了两次相同的代码。

  2. 另一种选择是简单地使用 while 循环并创建一个 Metric 对象,types.map(_(i)) 传递了最后一个参数。

到目前为止,我使用的是第二个选项,但我想知道是否有更实用的方法来处理这个问题。

【问题讨论】:

    标签: scala functional-programming


    【解决方案1】:

    你可以在types上使用模式匹配:

    def buildMetric(names: Seq[String], values: Seq[Double], ts: Seq[Int], types: Option[Seq[Type]]): Seq[Metric] = {
    
      types match {
        case Some(types) => names.zip(values).zip(ts).zip(types).map {
            case (((name, value), ts,), t) => Metric(name, value, ts, Some(t))
        }
        case None => (names, values, ts).zipped.map(Metric(_, _, _, None))
      }
    }
    

    【讨论】:

      【解决方案2】:

      第一个选项无法完成,因为zipped 仅适用于 3 个或更少元素的元组。 第二个版本可能如下所示:

      def buildMetric(names: Seq[String], values: Seq[Double], ts: Seq[Int], types: Option[Seq[Type]]): Seq[Metric] =
        for {
          (name, i) <- names.zipWithIndex
          value <- values.lift(i)
          time <- ts.lift(i)
          optType = types.flatMap(_.lift(i))
        } yield {
          Metric(name, value, time, optType)
        }
      

      【讨论】:

        【解决方案3】:

        从我的角度来看,还有一个选择,如果您想保留这种 zipped 方法 - 将 typesOption[Seq[Type]] 转换为 Seq[Option[Type]],长度与 names 相同,并在其中填充 None 值如果types 也是None 的情况:

        val optionTypes: Seq[Option[Type]] = types.fold(Seq.fill(names.length)(None: Option[Type]))(_.map(Some(_)))
        // Sorry, Did not find `zipped` for Tuple4 case
        names.zip(values).zip(ts).zip(optionTypes).toList.map {
          case (((name, value), time), optionType) => Metric(name, value, time, optionType)
        }
        

        希望这会有所帮助!

        【讨论】:

        • 谢谢,我试过了。这是一个很好的方法,但我惊讶地发现 Seq.fill 操作似乎非常占用 CPU。对此进行测试时,我的集群中的 CPU 利用率从 4-5% 上升到 50-70%!
        猜你喜欢
        • 2014-05-23
        • 1970-01-01
        • 2011-10-19
        • 1970-01-01
        • 1970-01-01
        • 2013-11-09
        • 1970-01-01
        • 2010-11-07
        • 2012-01-07
        相关资源
        最近更新 更多