Получить каждое поле как опцию необязательного объекта - PullRequest
0 голосов
/ 09 января 2019

у меня кейс-класс выглядит так

case class EmotionData(
      fearful: Double,
      angry: Double,
      sad: Double,
      neutral: Double,
      disgusted: Double,
      surprised: Double,
      happy: Double
    )

Я получаю Option[EmotionData], и мне нужны данные о каждой эмоции как опция [Double].

То, что я сделал, было:

val (fearful, angry, sad, neutral, disgusted, surprised, happy) = videoResult.emotion match {
        case Some(e) => (Some(e.fearful), Some(e.angry), Some(e.sad), Some(e.neutral), Some(e.disgusted), Some(e.surprised), Some(e.happy))
        case None => (None, None, None, None, None, None, None)
      }

Таким образом, каждое поле имеет значение Option [Double].

Но разве нет способа сделать это в Scala, где я могу перебирать все поля объекта и извлекать их, не переписывая каждое поле?

Ответы [ 4 ]

0 голосов
/ 09 января 2019

Да, есть способ перебирать поля объекта с помощью productIterator. Это будет выглядеть примерно так:

val List(fearful, angry, sad, neutral, disgusted, surprised, happy) =
  videoResult.emotion.map(_.productIterator.map(f => Some(f.asInstanceOf[Double])).toList)
    .getOrElse(List.fill(7)(None))

Как видите, это не намного лучше, чем у вас уже есть, и более подвержено ошибкам. Проблема в том, что количество и порядок полей указаны в указанном вами результате, поэтому существуют ограничения на степень автоматизации. И это работает только потому, что тип всех полей одинаков.

Лично я бы сохранял значение как Option[EmotionData] как можно дольше и выбирал отдельные значения по мере необходимости, например:

val opt = videoResult.emotion

val fearful = opt.map(_.fearful) // Option[Double]
val angry = opt.map(_.angry) // Option[Double]
val sad = opt.map(_.sad) // Option[Double]

val happy = opt.fold(0)(_.happy) // Double, default is 0 if opt is None

val ok = opt.forall(e => e.happy > e.sad) // True if emotion not set or more happy than sad

val disgusted = opt.exists(_.disgusted > 1.0) // True if emotion is set and disgusted value is large
0 голосов
/ 09 января 2019

Может быть, это?

case class EmotionData(
                        fearful: Double,
                        angry: Double,
                        sad: Double,
                        neutral: Double,
                        disgusted: Double,
                        surprised: Double,
                        happy: Double
                      )

val s = Some(EmotionData(1,2,3,4,5,6,7))
val n:Option[EmotionData] = None

val emotionsOpt = s.map { x =>
  x.productIterator.toVector.map(x => Some(x.asInstanceOf[Double]))
}.getOrElse(List.fill(7)(None))

// Or if you want an iterator:
val emotionsOptItr = n.map { x =>
  x.productIterator.map(x => Some(x.asInstanceOf[Double]))
}.getOrElse(List.fill(7)(None))


println(emotionsOpt)
println(emotionsOptItr)

Что приводит к:

Vector(Some(1), Some(2), Some(3), Some(4), Some(5), Some(6), Some(7))
List(None, None, None, None, None, None, None)
0 голосов
/ 09 января 2019

Вот немного другой подход, который может быть, возможно, немного более приемлемым.

val vidEmo :Option[EmotionData] = videoResult.emotion

val (fearful, angry, sad, neutral, disgusted, surprised, happy) =
  (vidEmo.map(_.fearful)
  ,vidEmo.map(_.angry)
  ,vidEmo.map(_.sad)
  ,vidEmo.map(_.neutral)
  ,vidEmo.map(_.disgusted)
  ,vidEmo.map(_.surprised)
  ,vidEmo.map(_.happy))

Но на самом деле вы должны просто держать vidEmo и извлекать то, что вам нужно, когда вам это нужно.

0 голосов
/ 09 января 2019

Вы можете сделать что-то подобное:

val defaultEmotionData=(0.0,0.0,0.0,0.0,0.0,0.0,0.0)

object Solution1 extends App{
 case class EmotionData(
                          fearful: Double,
                          angry: Double,
                          sad: Double,
                          neutral: Double,
                          disgusted: Double,
                          surprised: Double,
                          happy: Double
                        )

  case class EmotionDataOption(
                                fearfulOpt: Option[Double],
                                angryOpt: Option[Double],
                                sadOpt: Option[Double],
                                neutralOpt: Option[Double],
                                disgustedOpt: Option[Double],
                                surprisedOpt: Option[Double],
                                happyOpt: Option[Double]
                              )

  val emotion = Some(EmotionData(1.2, 3.4, 5, 6, 7.8, 3, 12))

  val ans: EmotionDataOption = emotion.getOrElse(defaultEmotionData).toOption

  implicit class ToOption(emotionData: EmotionData) {
    def toOption = EmotionDataOption(Some(emotionData.fearful), Some(emotionData.angry), Some(emotionData.sad), Some(emotionData
        .neutral), Some(emotionData.disgusted), Some(emotionData.surprised), Some(emotionData.happy))
  }
}

Теперь, где бы у вас ни был объект типа EmotionData, вы можете использовать для него toOption, и он преобразует свои значения в EmotionDataOption, который будет иметь значения Option [Double].

Если вы вернете Tuple7, тогда будет сложно получить доступ к значениям, поэтому я думаю, что преобразование его в другое case class EmotionDataOption - хорошая идея, и вы сможете легко получить доступ к значениям с помощью имени параметра.

...