user11039395
user11039395

Reputation:

How to remove every element in an array after a conditional?

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

Answers (3)

senjin.hajrulahovic
senjin.hajrulahovic

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

prayagupadhyay
prayagupadhyay

Reputation: 31262

One of the algorithm you can solve with is

  • find the index of element from the last where element has 0, 1 or 2
  • split the array at above index
  • take the last element of first array and whole second array

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

hoyland
hoyland

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

Related Questions