Chad
Chad

Reputation: 768

Finding character in 2 dimensional scala list

So this might not be the best way to tackle it but my initial thought was a for expression. Say I have a List like

List(List('a','b','c'),List('d','e','f'),List('h','i','j'))

I would like to find the row and column for a character, say 'e'.

def findChar(letter: Char, list: List[List[Char]]): (Int, Int) =
  for {
    r <- (0 until list.length)
    c <- (0 until list(r).length)
    if list(r)(c) == letter
  } yield (r, c)

If there is a more elegant way I'm all ears but I would also like to understand what's wrong with this. Specifically the error the compiler gives me here is

type mismatch;  found   : scala.collection.immutable.IndexedSeq[(Int, Int)]  required: (Int, Int)

on the line assigning to r. It seems to be complaining that my iterator doesn't match the return type but I don't quite understand why this is or what to do about it ...

Upvotes: 0

Views: 241

Answers (2)

som-snytt
som-snytt

Reputation: 39577

For your other ear, the question duplicates

How to capture inner matched value in indexWhere vector expression?

scala> List(List('a','b','c'),List('d','e','f'),List('h','i','j'))
res0: List[List[Char]] = List(List(a, b, c), List(d, e, f), List(h, i, j))

scala> .map(_ indexOf 'e').zipWithIndex.find(_._1 > -1)
res1: Option[(Int, Int)] = Some((1,1))

Upvotes: 2

chris
chris

Reputation: 5028

In the signature of findChar you are telling the compiler that it returns (Int, Int). However, the result of your for expression (as inferred by Scala) is IndexedSeq[(Int, Int)] as the error message indicates. The reason is that (r, c) after yield is produced for every "iteration" in the for expression (i.e., you are generating a sequence of results, not just a single result).

EDIT: As for findChar, you could do:

def findChar(letter: Char, list: List[List[Char]]) = {
  val r = list.indexWhere(_ contains letter)
  val c = list(r).indexOf(letter)
  (r, c)
}

It is not the most efficient solution, but relatively short.

EDIT: Or reuse your original idea:

def findAll(letter: Char, list: List[List[Char]]) =
  for {
    r <- 0 until list.length
    c <- 0 until list(r).length
    if list(r)(c) == letter
 } yield (r, c)

def findChar(c: Char, xs: List[List[Char]]) = findAll(c, xs).head

In both cases, be aware that an exception occurs if the searched letter is not contained in the input list.

EDIT: Or you write a recursive function yourself, like:

def findPos[A](c: A, list: List[List[A]]) = {
  def aux(i: Int, xss: List[List[A]]) : Option[(Int, Int)] = xss match {
    case Nil => None
    case xs :: xss =>
      val j = xs indexOf c
      if (j < 0) aux(i + 1, xss)
      else Some((i, j))
  }
  aux(0, list)
}

where aux is a (locally defined) auxiliary function that does the actual recursion (and remembers in which sublist we are, the index i). In this implementation a result of None indicates that the searched element was not there, whereas a successful result might return something like Some((1, 1)).

Upvotes: 2

Related Questions