2

I'm trying to search a nested array of characters for a specific character, and then return the indices of the character from the array.

Code Snippet

def search(target: Char, arr:Array[Array[Char]]): List[Int] = {
    for (i <- 0 until arr.length) { //search through first layer of array
    for (j <- 0 until arr(i).length) { //search through second layer of array
        if (arr(i)(j) == target) {
            val x = List(i,j)
            return x
        } }}}

However, I'm getting an error from compilation, that says this function is returning the two type signature. Error message:

error: type mismatch;
 found   : Unit
 required: List[Int]
for (i <- 0 until arr.length) { //search through first layer of array
       ^

I've found two similar threads here: found Unit: required Int. Why is the error not obvious? and found: Unit required: Int - How to correct this?

but they don't solve the problem i'm facing: I am trying to return the List, but the compiler is getting stuck at the for loop..

5 Answers 5

4

Seems like there are a lot of answers already, but I think that this is the most idiomatic way to approach the problem:

The Code

def search(target: Char, arr: Array[Array[Char]]): List[(Int, Int)] = {

    val indices = for{
        (a, i) <- arr.iterator.zipWithIndex
        (c, j) <- a.iterator.zipWithIndex
        if( c == target )
    } yield i -> j

    indices.toList

}

The Explanation

In scala, for-comprehensions are nestable, so you can take care of any degree of nested arrays by simply adding another x <- y line. You can introduce filtering with an if statement inside of the for{...}.

In the comprehension, a is the ith array inside arr, where i is the first index. c is the jth character inside a, where j is the second index. I use iterator so that indices can be evaluated on the fly, without needing to copy the arrays behind the scenes because of the for-comprehension. At the end, I call toList to evaluate the results of the indices iterator into a list.

The return type, List[(Int, Int)] is a list of pairs. It makes more sense to return List( (1,2), (3,4) ) if you found your target at i=1, j=2 and i=3, j=4 than to return List(1,2,3,4).

General Thoughts

Try to avoid using return in scala. You can usually handle your collections with an iterator-like approach, using yield, and then evaluate the result by calling a toList or toMap or toWhatever.

The scala collections API is very helpful for many of these cases, too. In the case where you just want the first item that matches a condition, you can use myCollection.find(...). Explore the scala docs to see the huge variety of convenient functions that are already available to you.

Sign up to request clarification or add additional context in comments.

2 Comments

+1 probably the cleanest solution (in Scala), but probably also the one that is least understandable for non-Scala people
@Leo maybe the code by itself, but the accompanying explanation is very good, and I think that when you dive into a language you should try to learn & write it in the most idiomatic way. I also think you should learn that way early on, so you develop good habits and get the most out of the language. On the other hand, Scala is not a great beginners language, it's beautifully mathematical, which is very abstracted from reality, and most people hate that. I've found it a very long learning curve, but it's expressiveness is really growing on me.
1

I would suggest using a less Java-like approach altogether. I'm not entirely sure what your function is supposed to do, but if you want a list of all (x, y) indices of the match within the nested array, you could do something like this:

def search(target: Char, array: Array[Array[Char]]): Seq[(Int, Int)] = {
  array.zipWithIndex.filter(_._1.contains(target)).map { xa =>
    xa._1.zipWithIndex.filter(_._1 == target).map(xb => (xa._2, xb._2)).toSeq
  }.flatten.toSeq
}

which behaves like this:

val t = Array(
    Array('a', 'b', 'c'), 
    Array('b'), Array('c', 'a'), 
    Array('a', 'a', 'x', 'a')
)
println(search('a', t))
=> ((0,0), (2,1), (3,0), (3,1), (3,3))

Comments

1

Here is my solution to find the first index of a element in a two-dimension array: (replace collectFirt to collect, if you wan to find all indexes)

def search[T](target: T, arr: Array[Array[T]]): List[Int] = 
  arr.indices.collectFirst{
    case k if arr(k).contains(target) => 
      List(k, arr(k).indexWhere(_ == target)) 
  }.getOrElse(Nil)

Test:

scala> val t = Array(
     |     Array('a', 'b', 'c'),
     |     Array('b'), 
     |     Array('c', 'a'),
     |     Array('a', 'a', 'x', 'a')
     | )

scala> println(search('a', t))
List(0, 0)

scala> println(search('x', t))
List(3, 2)

scala> println(search('e', t))
List()

Comments

0

You only return a List if a particular condition is met (arr(i)(j) == target). You have to define a return value for the case that the for comprehensions run through. E.g.

def search(target: Char, arr:Array[Array[Char]]): List[Int] = {
  for (i <- 0 until arr.length) { //search through first layer of array
    for (j <- 0 until arr(i).length) { //search through second layer of array
      if (arr(i)(j) == target) {
        val x = List(i,j)
        return x
      }
    }
  }
  Nil  // not found
}

Comments

0

The for loop itself would return Unit if the second array is empty or the if expression evaluates to false. You could rewrite it so it returns null if it nevers gets to the code after the if-expression.

I would leave out the assignment of x too, there's really no point.

  def search(target: Char, arr:Array[Array[Char]]): List[Int] = 
  {
    for (i <- 0 until arr.length) 
    { //search through first layer of array
      for (j <- 0 until arr(i).length) 
      { //search through second layer of array
        if (arr(i)(j) == target) 
        {
          return List(i,j)
        } 
      }
    }
    Nil
  }

Btw this could probably be rewritten with a more functional approach but that goes beyond the scope of this question.

1 Comment

Actually, even if you omit assignment, you should not omit return, otherwise the loop will not be interrupted and your function will always return null.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.