I have this val: val offsets: Option[Map[String, Int]] = jsonOffsets.get(topic)
How do I get all the keys from offsets? Is it offsets[0], offsets.keys isn't working.
offsets is an Option, so it may or may not contain a Map. Use pattern matching to handle that:
offsets match {
case Some(map) => // Whatever you want to do with the map
case None => // What should you do when there's no map?
}
If you don't know about options, this is a good read.
Related
I have a collection with elements that have a field field1. I want to get all field1s that are options of type MyType.
Currently this is my code.
elems.map(_.field1).map {case Some(found: MyType) => found}
I'm sure this can be done in a much nicer way.. It bugs me that I need to use map twice. Is there a way to do this with only one map/collect ?
EDIT: My code works. I'm just wondering if it can be done in a better (i.e. shorter or prettier way).
elems.flatMap(_.field1.collect { case x: MyType => x })
I believe utilising .flatMap may solve this issue for you
elems.flatMap(_.field1 match {
case myType: MyType => Some(myType)
case _ => None
}
Calling iterator before transforming the collection accumulates all the transformations into a single one so perhaps try
elems
.iterator
.flatMap(_.field1)
.collect { case v: MyType => v }
.toList
if your Seq type is case class you can use pattern matching with one collect function like so (see actual seq):
case class MyTypeWrapper(field1: Option[MyType])
case class MyType(x: String)
val elems = Seq.empty[MyTypeWrapper]
val expected: Seq[MyType] = elems.map(_.field1).map{ case Some(found: MyType) => found }
val actual: Seq[MyType] = elems.collect{ case MyTypeWrapper(Some(mt: MyType)) => mt }
// expected and actual should contains the same elements
I have a map of Map[String, Info], it contains keys which can be either uppercase or lowercase, like this:
person1: PersonInfo1
person2: PersonInfo2
PERSON1: PersonInfo1
i want to get the value for key 'person1', if nothing found I'll try with key of 'PERSON1', I tried this code:
val info = map.get(key) match {
case Some(personInfo) => personInfo
case None =>
map.get(key.toUpperCase()) match {
case Some(personInfo) => personInfo
case None => None
}
}
but this return info as type of Product with Serializable, how can I have info returned as type of PersonInfo? Is there a way in Scala that allow me to get value from map by key and ignore cases of the key?
There are comparators for sorted maps which allow getting from the map case insensitively. Example: https://scastie.scala-lang.org/PfHTh16CROag7PNrknx1sQ
val map = scala.collection.immutable.SortedMap("key1" -> 45, "Key2" -> 43, "KEY3" -> 42)(scala.math.Ordering.comparatorToOrdering(String.CASE_INSENSITIVE_ORDER))
map.get("key1") // Some(45)
map.get("key2") // Some(43)
map.get("key3") // Some(42)
map.get("key4") // None
Your actual problem can be fixed if you return Options on all cases, for example:
val info = map.get(key) match {
case somePi#Some(personInfo) => somePi
case None => map.get(key.toUpperCase()) match {
case Some(personInfo) => Some(personInfo)
case None => None
}
}
Note the somePi# => somePi parts for referring the expression or the Some(personInfo).
Probably worth explaining why you got this error message. I assume personInfo is a case class which implements Product and Serializable, just like None. The common type for them is Product with Serializable.
You could chain gets with orElse. I would create an extension method for that:
implicit class CaseInsensitiveGetMap[V] (m: Map[String,V]) {
def iget (key: String): Option[V] = m.get(key)
.orElse(m.get(key.toUpperCase())) //you can add more orElse in chain
}
Then you can just use it like:
map.iget("person2")
The reason you're getting Product with Serializable is because your code is trying to return either a String (if key is good) or an Option (i.e. None if key not found). Those two types are not compatible. You should decide if you want String (maybe an empty string if key not found) or Option (i.e. Some[String] or None).
See if this works for you. It returns an Option[String].
map.get(key).fold(pm.get(key.toUpperCase))(Some(_))
The 1st get() returns an Option. The fold()() unwraps the Option and either tries a 2nd get() with upper case key value, or, if the 1st get returned a value, the value is re-wrapped in an Option so that the types match up.
If, on the other hand, you want to return a String instead, you might do this.
map.getOrElse(key, pm.getOrElse(key.toUpperCase, ""))
You can do a find instead of a get but you may want to consider performance when doing this.
map.find(k => k._1.equalsIgnoreCase(key)) match {
case Some =>
case None =>
}
I've got a Map and I'd like to have different behavior if it's empty or not. I cannot figure out how to match an empty map. I've consulted other answers and pattern matching documentation, and I can't figure it out. I thought that Nil might work like it does for lists, but that isn't the case. I also can't seem to match against Map(), Map[String, String].empty or Map[String, String]()
myMap match {
// doesn't work
case Nil => false
case _ => true
}
myMap match {
// also doesn't work
case Map[String, String]() => false
case _ => true
}
The approaches in this answer seem like overkill for checking if a Map is empty. Also, the accepted answer checks if the Map contains any of the maps to be matched, which I do not think would apply in my situation
Map doesn't provide any extractor with unapply()/unapplySeq() methods out of the box, so it is impossible to match key-value pairs it in pattern matching. But if you need only to match if map is empty you can:
val map = Map.empty[String, String]
val result = map match {
case m:Map[String, String] if m.isEmpty => false
case _ => true
}
println(result)
outputs:
false
Map doesn't have an unapply()/unapplySeq() method so it can't deconstructed via pattern matching.
As #Luis has commented, Nil is the List terminus and unrelated to Map.
Without over using patten mathing:
val map = Map.empty[String, String]
val result = map.nonEmpty
println(result)
// Output: false
Or if you insist or have more logic than just true/false:
val map = Map.empty[String, String]
map.isEmpty match {
case true =>
println("The map is empty")
case false =>
println("The map is not empty")
}
// Output: The map is empty
I have a list of Person and want to retrieve a person by its id
val person = personL.find(_.id.equals(tempId))
After that, I want to get as a tuple the first and last element of a list which is an attribute of Person.
val marks: Option[(Option[String], Option[String])] = person.map { p =>
val marks = p.school.marks
(marks.headOption.map(_.midtermMark), marks.lastOption.map(_.finalMark))
}
This work's fine but now I want to transform the Option[(Option[String], Option[String])] to a simple (Option[String], Option[String]). Is it somehow possible to do this on-the-fly by using the previous map?
I suppose:
person.map{...}.getOrElse((None, None))
(None, None) is a default value here in case if your option of tuple is empty
You are, probably, looking for fold:
personL
.collectFirst {
case Person(`tempId`, _, .., school) => school.marks
}.fold[Option[String], Option[String]](None -> None) { marks =>
marks.headOption.map(_.midtermMark) -> marks.lastOption.map(_.finalMark)
}
(NOTE I'm quit new to Scala and still struggle with most common operations of collection manipulation.)
I would like to convert a List[Task] into a Map. Here's some details:
// assignee may be null
case class Task(assignee: String, description: String)
// might refactor it into:
// case class Task(assignee: Option[String], description: String)
I want a Map where Keys are the assignees and each Value is a Set[Task]. I'm having trouble managing the following two situations:
Map's not being (cough) friendly (cough) with null Keys (I worked around this one using Option[String] for assignee) and
having to distinguish whether a Key already exists in the map (only add value to existing set) vs key already added so the Set value exists
I came up with the following but it looks overly verbose.
def groupByAssignee(tasks : List[Task]) : Map[Option[String], Set[Task]] = {
tasks.foldLeft(Map[Option[String], Set[Task]]())(
(m, t) => {
m.get(t.assignee) match {
case Some(_) => m + ((t.assignee, m.get(t.assignee).get.+(t)))
case _ => m + ((t.assignee, Set(t)))
}
})
}
What's a easier/clearer way to achieve this?
Thanks!
This use case is so common that there is a built-in method for that:
tasks groupBy {_.assignee}
groupBy however will return Map[String,List[Task]] while you want .Map[String, Set[String]]. This should do it:
groupBy {_.assignee} mapValues {_ map {_.description} toSet}
groupBY is null-friendly, but you shouldn't be. Option[String] is much better and more idiomatic.