Reputation: 39
I have a case class looks like this
case class EmotionData(
fearful: Double,
angry: Double,
sad: Double,
neutral: Double,
disgusted: Double,
surprised: Double,
happy: Double
)
I receive an Option[EmotionData]
and I need each emotion data as an Option[Double].
What I did was:
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)
}
This way I have each field as an Option[Double] value.
But isn't there a way to do this in Scala where I can iterate through all fields of an object and extract them without rewriting each field?
Upvotes: 1
Views: 249
Reputation: 51271
Here's a slightly different approach that might be, perhaps, a little more palatable.
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))
But really, you should just keep vidEmo
around and extract what you need when you need it.
Upvotes: 4
Reputation: 6220
Maybe this?
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)
Which results in:
Vector(Some(1), Some(2), Some(3), Some(4), Some(5), Some(6), Some(7))
List(None, None, None, None, None, None, None)
Upvotes: 0
Reputation: 27356
Yes, there is a way to iterate through the fields of an object by using productIterator
. It would look something like this:
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))
As you can see, this isn't much better than what you already have, and is more prone to error. The problem is that the number and order of fields is explicit in the result you have specified, so there are limits to how much this can be automated. And this only works because the type of all the fields is the same.
Personally I would keep the value as Option[EmotionData]
as long as possible, and pick out individual values as needed, like this:
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
Upvotes: 1
Reputation: 2686
You can do Something like that:
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))
}
}
Now where ever you will have an object of type EmotionData you can use toOption on that and it will convert it's values into EmotionDataOption which will have values Option[Double].
If you will return Tuple7
then it will be tough to access values, that's why I think converting it into another case class EmotionDataOption
is a good idea and you will be able to access the values easily with the parameter name.
Upvotes: -1