tkachuko
tkachuko

Reputation: 1986

takeRightWhile() method in scala

I might be missing something but recently I came across a task to get last symbols according to some condition. For example I have a string: "this_is_separated_values_5". Now I want to extract 5 as Int.

Note: number of parts separated by _ is not defined.

If I would have a method takeRightWhile(f: Char => Boolean) on a string it would be trivial: takeRightWhile(ch => ch != '_'). Moreover it would be efficient: a straightforward implementation would actually involve finding the last index of _ and taking a substring while the use of this method would save first step and provide better average time complexity.

UPDATE: Guys, all the variations of str.reverse.takeWhile(_!='_').reverse are quite inefficient as you actually use additional O(n) space. If you want to implement method takeRightWhile efficiently you could iterate starting from the right, accumulating result in string builder of whatever else, and returning the result. I am asking about this kind of method, not implementation which was already described and declined in the question itself.

Question: Does this kind of method exist in scala standard library? If no, is there method combination from the standard library to achieve the same in minimum amount of lines?

Thanks in advance.

Upvotes: 1

Views: 892

Answers (4)

Nyavro
Nyavro

Reputation: 8866

Possible solution:

str.reverse.takeWhile(_!='_').reverse

Update

You can go from right to left with following expression using foldRight:

str.toList.foldRight(List.empty[Char]) {
  case (item, acc) => item::acc
}

Here you need to check condition and stop adding items after condition met. For this you can pass a flag to accumulated value:

val (_, list) = str.toList.foldRight((false, List.empty[Char])) {
  case (item, (false, list)) if item!='_' => (false, item::list)
  case (_,    (_,     list))              => (true, list)
}
val res = list.mkString.toInt

This solution is even more inefficient then solution with double reverse:

  1. Implementation of foldRight uses combination of List reverse and foldLeft

  2. You cannot break foldRight execution, so you need flag to skip all items after condition met

Upvotes: 4

sarveshseri
sarveshseri

Reputation: 13985

I am not exactly sure about the problem you are facing. My understanding is that you want have a string of format xxx_xxx_xx_...._xxx_123 and you want to extract the part at the end as Int.

import scala.util.Try

val yourStr = "xxx_xxx_xxx_xx...x_xxxxx_123"

val yourInt = yourStr.split('_').last.toInt

// But remember that the above is unsafe so you may want to take it as Option

val yourIntOpt = Try(yourStr.split('_').last.toInt).toOption

Or... lets say your requirement is to collect a right-suffix till some boolean condition remains true.

import scala.util.Try

val yourStr = "xxx_xxx_xxx_xx...x_xxxxx_123"

val rightSuffix =  yourStr.reverse.takeWhile(c => c != '_').reverse

val yourInt = rightSuffix.toInt

// but above is unsafe so
val yourIntOpt = Try(righSuffix.toInt).toOption

Comment if your requirement is different from this.

Upvotes: 0

tgr
tgr

Reputation: 3608

I'd go with this:

val s = "string_with_following_number_42"
s.split("_").reverse.head
// res:String = 42

This is a naive attempt and by no means optimized. What it does is splitting the String into an Array of Strings, reverses it and takes the first element. Note that, because the reversing happens after the splitting, the order of the characters is correct.

Upvotes: 0

NaHeon
NaHeon

Reputation: 247

You can use StringBuilder and lastIndexWhere.

val str = "this_is_separated_values_5"
val sb = new StringBuilder(str)
val lastIdx = sb.lastIndexWhere(ch => ch != '_')
val lastCh = str.charAt(lastIdx)

Upvotes: -1

Related Questions