Reputation:
So I have an array of strings in Scala called pronunciationArray like this:
["EH1","N", "D", "P", "ER0", "EH1", "N", "TH", "AH0", "S", "IY2", "Z"]
and i want to write an if else statement that reads the array in reverse, and as soon as it finds a string with a number in it, it either removes all the strings after or puts everything before into a separate array.
so for the above example, id want it to stop at "IY2", then either create a new array with only ["IY2", "Z"] or remove every string after and leave the original array with, like I said, ["IY2", "Z"]. The number itself isnt an integer btw, its part of the string, and the numbers range from 0-2.
I've tried for loop in reverse with an if else that looks for the numbers 0, 1, 2 but it returns every string with a number, so it returns [IY2, AH0, EH1, ER0, EH1] it doesnt stop as soon as it finds the first string with a number. And i'm not sure how to put everything before that string into a new array if I even found a way to stop it.
for (sounds <- pronunciationArray.reverse) {
val number0 = sounds.contains("0")
if (number0 == true) {
println(sounds)
} else if (number0 == true){
println(sounds)
} else if (number1 == true){
println(sounds)
} else {
-1
}
}
I want it to return just ["IY2", "Z"] but it returned [IY2, AH0, EH1, ER0, EH1]
Upvotes: 0
Views: 480
Reputation: 3191
Scala arrays offer a lastIndexWhere method which takes a predicate and returns the index of the last element which fulfills the predicate:
val arr = Array("EH1","N", "D", "P", "ER0", "EH1", "N", "TH", "AH0", "S", "IY2", "Z")
val idx = arr.lastIndexWhere(e => e.intersect(Array('0', '1', '2')).size > 0)
and then you call the takeRight method to get the desired result
val res = arr.takeRight(arr.size - idx)
Upvotes: 0
Reputation: 31262
One of the algorithm you can solve with is
Example,
val pronunciationArray = Array("EH1","N", "D", "P", "ER0", "EH1", "N", "TH", "AH0", "S", "IY2", "Z")
def takeUntil(inputArray: Array[String], condition: String => Boolean): Array[String] = {
val findIndexFromLast = inputArray.reverse.zipWithIndex.collectFirst {
case (elem, index) if condition.apply(elem) => index
}
findIndexFromLast match { // pattern match to check if there exists element with 0, 1, or 2
case Some(indexFromLast) =>
inputArray.splitAt(inputArray.length - indexFromLast) match {
case (head, tail) => head.last +: tail
}
case None => // no element with 0, 1, 2 exists
inputArray
}
}
takeUntil(
inputArray = pronunciationArray,
condition = elem => elem.contains("0") || elem.contains("1") || elem.contains("2")) //gives [IY2, Z]
The other way to solve the same using .span(predicate)
which is better version of .splitAt(index)
def takeUntil2(inputArray: Array[String]): Array[String] = {
inputArray.reverse.span {
element =>
!element.contains("0") && !element.contains("1") && !element.contains("2")
} match {
case (head, tail) => tail.take(1) ++ head.reverse
}
}
val result = takeUntil2(inputArray = pronunciationArray)
Now, using scala way you can extend Array
to have custom function,
implicit class ArrayOps(array: Array[String]) {
def takeUntil(predicate: String => Boolean): Array[String] = {
val splitAt = array.reverse.span(predicate.apply)
splitAt._2.take(1) ++ splitAt._1.reverse
}
}
val res = pronunciationArray
.takeUntil(predicate = elem => !elem.contains("0") && !elem.contains("1") && !elem.contains("2"))
Similar question: How to implement 'takeUntil' of a list?
How do I pattern match arrays in Scala?
Upvotes: 1
Reputation: 1824
I'm not completely clear on your objective, but Array
has a span
function that splits it into two pieces based on a condition--the first piece is a prefix of the array where everything satisfies the condition and the second piece is the remainder.
Let's say your condition was that the string contains 2
rather than contains a number (just because I'm lazy--it could be any function f: String => Boolean
). If we reverse the array and then split on the negation of the function, we get:
scala> a.reverse.span(!_.contains("2"))
res5: (Array[String], Array[String]) = (Array(Z),Array(IY2, S, AH0, TH, N, EH1, ER0, P, D, N, EH1))
We know that the first element of the second array satisfies our condition, so we can just assemble the result:
scala> val (start, end) = a.reverse.span(!_.contains("2"))
start: Array[String] = Array(Z)
end: Array[String] = Array(IY2, S, AH0, TH, N, EH1, ER0, P, D, N, EH1)
scala> val result = end.take(1) ++ start.reverse
result: Array[String] = Array(IY2, Z)
All the reversing is perhaps not the most efficient, but gets the job done.
Upvotes: 1