I want to get a List[String] from the input. Please help me to find an elegant way.
Desired output:
emp1,emp2
My code:
val ls = List("emp1.id1", "emp2.id2","emp2.id3","emp1.id4")
def myMethod(ls: List[String]): Unit = {
ls.foreach(i => print(i.split('.').head))
}
(myMethod(ls)). //set operation to make it unique ??
If you care about validation, you can consider using Regex:
val ls = List("emp1.id1", "emp2.id2","emp2.id3","emp1.id4","boom")
def myMethod(ls: List[String]) = {
val empIdRegex = "([\\w]+)\\.([\\w]+)".r
val employees = ls collect { case empIdRegex(emp, _) => emp }
employees.distinct
}
println(myMethod(ls))
Outputs:
List(emp1, emp2)
def myMethod(ls: List[String]) =
ls.map(_.takeWhile(_ != '.'))
myMethod(ls).distinct
Since Scala 2.13, you can use List.unfold to do this:
List.unfold(ls) {
case Nil =>
None
case x :: xs =>
Some(x.takeWhile(_ != '.'), xs)
}.distinct
Please not that you want distinct values, therefore you can achieve the same using Set.unfold:
Set.unfold(ls) {
case Nil =>
None
case x :: xs =>
Some(x.takeWhile(_ != '.'), xs)
}
Code run at Scastie.
Related
This question already has answers here:
Scala flatten a List
(5 answers)
Closed 4 months ago.
how to achieve below result list from the list val l1 = List(1,2,List(3,List(4,5,6),5,6,7)?
result = List(1,2,3,4,5,6,7)
Strangely enough, this actually works in Scala 3, and is kinda-sorta "type-safe":
type NestedList[A] = A match {
case Int => Int | List[NestedList[Int]]
case _ => A | List[NestedList[A]]
}
val input: NestedList[Int] = List(1,2,List(3,List(4,5,6),5,6,7))
def flattenNested[A](nested: NestedList[A]): List[A] =
nested match
case xs: List[NestedList[A]] => xs.flatMap(flattenNested)
case x: A => List(x)
val result: List[Int] = flattenNested(input)
println(result.distinct)
I'd consider this more of a curiosity, though, it feels quite buggy. See also discussion here. As it is now, it would be much preferable to model the input data properly as an enum, so one doesn't end up with a mix of Ints and Lists in the first place.
The following would work,
def flattenOps(l1: List[Any]): List[Int] = l1 match {
case head :: tail => head match {
case ls: List[_] => flattenOps(ls) ::: flattenOps(tail)
case i: Int => i :: flattenOps(tail)
}
case Nil => Nil
}
flattenOps(l1).distinct
Regardless the strange input, I would like to make a common pattern of my items in the list:
import scala.reflec.ClassTag
sealed trait ListItem[A] {
def flattenedItems: List[A]
}
case class SingleItem[A](value: A) extends ListItem[A] {
def flattenedItems: List[A] = value :: Nil
}
case class NestedItems[A](values: List[ListItem[A]]) extends ListItem[A] {
def flattenedItems: List[A] = values.flatMap(_.flattenedItems)
}
// The above would probably take much less lines of code in Scala 3
object ListItem {
def parse[A : ClassTag](value: Any): ListItem[A] = {
value match {
case single: A => SingleItem(single)
case nested: List[Any] => NestedItems(nested.map(parse[A]))
}
}
}
Then given the list as:
val l = List(1, 2, List(3, List(4, 5, 6), 5, 6, 7))
You can parse each value, and then get flattened values using the ListItem's method:
val itemizedList: List[ListItem[Int]] = l.map(ListItem.parse[Int])
val result = itemizedList.flatMap(_.flattenedItems).distinct
I am a newbie in Scala and I am trying to resolve the following simple coding problem:
Write a listOfLists recursive method that takes a number of strings as varargs and then
creates a list of lists of strings, with one less string in each, so for example:
listOfLists("3","2","1") should give back: List(List("3","2","1"), List("2","1"), List("1"))
The solution I've found is the following:
def listOfLists(strings: String*): List[List[String]] = {
val strLength = strings.length
#tailrec
def recListOfList(result: List[List[String]], accumulator: Int): List[List[String]] = {
accumulator match {
case x if x < strLength =>
recListOfList(result :+ (strings.toList.takeRight(strings.length - accumulator)), accumulator + 1 )
case _ => result
}
}
val res: List[List[String]] = List(strings.toList)
recListOfList(res, 1)
}
The solution works, however I think it could be written much more better.
A problem I can see is that I convert the varargs to a List with the toList method, but a hint that the problem gave me is to use the eta expansion _* but I don't know how to use it in this context.
Then, I tried to find another way to write in a more efficient way the following instruction:
strings.toList.takeRight(strings.length - accumulator))
but this is the only solution that came up in my mind.
Any review is welcome (also say that this solution is a total mess :D (providing the right reasons))
This meets all the specified requirements.
def listOfLists(strings: String*): List[List[String]] =
if (strings.isEmpty) Nil
else strings.toList :: listOfLists(strings.tail:_*)
You can do this:
def listOfLists(strings: String*): List[List[String]] = {
#annotation.tailrec
def loop(remaining: List[String], acc: List[List[String]]): List[List[String]] =
remaining match {
case head :: tail =>
loop(remaining = tail, (head :: tail) :: acc)
case Nil =>
acc.reverse
}
loop(remaining = strings.toList, acc = List.empty)
}
I believe the code is self-explanatory; but, feel free to ask any questions you may have.
You can see the code running here.
Not a recursive method but worth noting that tails in the standard library can do most of this. Then map and filter to convert to correct type and filter out empty list.
def listOfLists(strings: String *): List[List[String]] = strings.tails.map(_.toList).filter(_.nonEmpty).toList
Test:
scala> listOfLists("a","b","c")
val res6: List[List[String]] = List(List(a, b, c), List(b, c), List(c))
Using almost the same idea you can rewrite your solution in cleaner way:
def listOfLists(strings: String*): List[List[String]] = {
#tailrec
def recListOfList(curr: List[String], accumulator: Seq[List[String]]): Seq[List[String]] = {
curr match {
case head :: tail => recListOfList(tail, curr +: accumulator)
case _ => accumulator
}
}
recListOfList(strings.toList, Nil)
.reverse
.toList
}
With the splat(_*) operator, which adapts a sequence (Array, List, Seq, Vector, etc.) to varargs parameter you can create a shorter solution, but it will not be tail-recursive:
def listOfLists(strings: String*): List[List[String]] = {
val curr = strings.toList
curr match {
case Nil => Nil
case x :: tail => curr :: listOfLists(tail:_*)
}
}
From Scala 2.13 you can use List.unfold and Option.when:
def listOfLists(strings: String*): List[List[String]] = {
List.unfold(strings) { s =>
Option.when(s.nonEmpty)(s.toList, s.tail)
}
}
Code run at Scastie.
Some scala code:
val list = List(Some("aaa"), Some("bbb"), None, ...)
list.filter(_!=None).map {
case Some(x) => x + "!!!"
// I don't want to handle `None` case since they are not possible
// case None
}
When I run it, the compiler complains:
<console>:9: warning: match may not be exhaustive.
It would fail on the following input: None
list.filter(_!=None).map {
^
res0: List[String] = List(aaa!!!, bbb!!!)
How to fix that warning without providing the case None line?
If you are using map after filter, you may to use collect.
list collect { case Some(x) => x + "!!!" }
you can use flatten
scala> val list = List(Some("aaa"), Some("bbb"), None).flatten
list: List[String] = List(aaa, bbb)
scala> list.map {
| x => x + "!!!"
| }
res1: List[String] = List(aaa!!!, bbb!!!)
You could use the #unchecked annotation, although that requires some additional code:
list.filter(_!=None).map { x => ( x : #unchecked) match {
case Some(x) => x + "!!!"
}
}
You can use get method instead of pattern matching.
Here is example code:
scala> val list = List(Some("aaa"), Some("bbb"), None)
list: List[Option[String]] = List(Some(aaa), Some(bbb), None)
scala> list.filter(_ != None).map(_.get + "!!!")
res0: List[String] = List(aaa!!!, bbb!!!)
some other way to solve this issue, without filter and pattern matching
scala> list.flatten map (_ + "!!!")
or
scala> list.flatMap (_ map (_ + "!!!"))
I would like to know how I can split a string using more than one delimiter with Scala.
For instance if I have a list of delimiters :
List("Car", "Red", "Boo", "Foo")
And a string to harvest :
Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed
I would like to be able to output something like :
List( ("Car", " foerjfpoekrfopekf "),
("Red", " ezokdpzkdpoedkzopke dekpzodk "),
("Foo", " azdkpodkzed")
)
You can use the list to create a regular expression and use its split method:
val regex = List("Car", "Red", "Boo", "Foo").mkString("|").r
regex.split("Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed")
That however doesn't tell you which delimiter was used where. If you need that, I suggest you try Scala's parser library.
EDIT:
Or you can use regular expressions to extract one pair at a time like this:
def split(s:String, l:List[String]):List[(String,String)] = {
val delimRegex = l.mkString("|")
val r = "("+delimRegex+")(.*?)(("+delimRegex+").*)?"
val R = r.r
s match {
case R(delim, text, rest, _) => (delim, text) :: split(rest, l)
case _ => Nil
}
}
a bit verbose, but it works:
DEPRECATED VERSION: (it has a bug, left it here because you already accepted the answer)
def f(s: String, l: List[String], g: (String, List[String]) => Int) = {
for {
t <- l
if (s.contains(t))
w = s.drop(s.indexOf(t) + t.length)
} yield (t, w.dropRight(w.length - g(w, l)))
}
def h(s: String, x: String) = if (s.contains(x)) s.indexOf(x) else s.length
def g(s: String, l: List[String]): Int = l match {
case Nil => s.length
case x :: xs => math.min(h(s, x), g(s, xs))
}
val l = List("Car", "Red", "Boo", "Foo")
val s = "Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed"
output:
f(s, l, g).foreach(println)
> (Car, foerjfpoekrfopekf )
> (Red, ezokdpzkdpoedkzopke dekpzodk )
> (Foo, azdkpodkzed)
it returns Array[String] instead of list. but you can just as well do: f(s, l, g).toList
EDIT:
just noticed this code is good if the delimiters only appear once in the string. if had defined s as follows:
val s = "Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed Car more..."
I'd still get the same result, instead of another pair ("Car"," more...")
EDIT#2: BUGLESS VERSION here's the fixed snippet:
def h(s: String, x: String) = if (s.contains(x)) s.indexOf(x) else s.length
def multiSplit(str: String, delimiters: List[String]): List[(String, String)] = {
val del = nextDelimiter(str, delimiters)
del._1 match {
case None => Nil
case Some(x) => {
val tmp = str.drop(x.length)
val current = tmp.dropRight(tmp.length - nextDelIndex(tmp,delimiters))
(x, current) :: multiSplit(str.drop(x.length + current.length), delimiters)
}
}
}
def nextDelIndex(s: String, l: List[String]): Int = l match {
case Nil => s.length
case x :: xs => math.min(h(s, x), nextDelIndex(s, xs))
}
def nextDelimiter(str: String, delimiters: List[String]): (Option[String], Int) = delimiters match {
case Nil => (None, -1)
case x :: xs => {
val next = nextDelimiter(str, xs)
if (str.contains(x)) {
val i = str.indexOf(x)
next._1 match {
case None => (Some(x), i)
case _ => if (next._2 < i) next else (Some(x), i)
}
} else next
}
}
output:
multiSplit(s, l).foreach(println)
> (Car, foerjfpoekrfopekf )
> (Red, ezokdpzkdpoedkzopke dekpzodk )
> (Foo, azdkpodkzed)
> (Car, more...)
and now it works :)
I have often the need to check if many values are equal and in case extract the common value. That is, I need a function that will work like follows:
extract(List()) // None
extract(List(1,2,3)) // None
extract(List(2,2,2)) // Some(2)
Assuming one has a pimp that will add tailOption to seqs (it is trivial to write one or there is one in scalaz), one implementation looks like
def extract[A](l: Seq[A]): Option[A] = {
def combine(s: A)(r: Seq[A]): Option[A] =
r.foldLeft(Some(s): Option[A]) { (acc, n) => acc flatMap { v =>
if (v == n) Some(v) else None
} }
for {
h <- l.headOption
t <- l.tailOption
res <- combine(h)(t)
} yield res
}
Is there something like that - possibly more general - already in Scalaz, or some simpler way to write it?
This seems like a really complicated way to write
def extract[A](l:Seq[A]):Option[A] = l.headOption.flatMap(h =>
if (l.tail.forall(h==)) Some(h) else None)
You don't need tailOption, since the anonymous function that gets passed as an argument to flatMap is only executed if l is not empty.
If you only want to delete duplicates toSet is enough:
def equalValue[A](xs: Seq[A]): Option[A] = {
val set = xs.toSet
if (set.size == 1) Some(set.head) else None
}
scala> equalValue(List())
res8: Option[Nothing] = None
scala> equalValue(List(1,2,3))
res9: Option[Int] = None
scala> equalValue(List(2,2,2))
res10: Option[Int] = Some(2)
This is a fluent solution
yourSeq.groupBy(x => x) match {case m if m.size==1 => m.head._1; case _ => None}
You could use a map to count the number of occurrences of each element in the list and then return only those that occur more than once:
def extract[T](ts: Iterable[T]): Iterable[T] = {
var counter: Map[T, Int] = Map()
ts.foreach{t =>
val cnt = counter.get(t).getOrElse(0) + 1
counter = counter.updated(t, cnt)
}
counter.filter(_._2 > 1).map(_._1)
}
println(extract(List())) // List()
println(extract(List(1,2,3))) // List()
println(extract(List(2,2,2))) // List(2)
println(extract(List(2,3,2,0,2,3))) // List(2,3)
You can also use a foldLeft instead of foreach and use the empty map as the initial accumulator of foldLeft.