0
\$\begingroup\$

Problem

Spreadsheet cells are referenced by column and row identifiers. Columns are labeled with alphabetical characters, starting with "A", "B", "C", ...; rows are numbered from 1 in ascending order. Write a function that takes in a string identifying a range of cells in a spreadsheet and returns an ordered list of cells which compose that range.

Example:

"A3:D5" -> ["A3", "A4", "A5", "B3", "B4", "B5", "C3", "C4", "C5", "D3", "D4", "D5"]
"A3:D4" -> ["A3", "A4", "B3", "B4", "B5", "C3", "C4", "C5", "D3", "D4"]

Here is scala implementation of the same ,

import scala.language.postfixOps

object PrintSpreadSheet extends App {

  val validAlphabets = ('A' to 'Z').toSeq

  def cells(range: String): Seq[String] = {
    val corners = (range split ":") flatMap { corner =>
      Seq(corner.head, corner.last)
    }
    val rows = (corners filter (r => validAlphabets.contains(r))) sorted
    val cols = (corners filter (c => !validAlphabets.contains(c))) sorted

    (rows.head to rows.last) flatMap { r =>
      (cols.head to cols.last) map { c =>
        r.toString + ":" + c.toString
      }
    }
  }
  cells("A1:D5") foreach println
}
\$\endgroup\$
1
\$\begingroup\$

I couldn't get your code to compile until I took out the sorted. The output was still good so I don't know what purpose it was supposed to serve.

There's no point in casting validAlphabets to a Seq[Char]. As a Range[Char] the contains() method still works. Better still would be to cast it to a Set[Char]. Then the syntax is more concise and the lookup is faster.

val rows = corners filter validAlphabets
val cols = corners filter (!validAlphabets(_))

Due to the use of .head and .last, this solution won't handle rows past 9. There are also a number of input errors that it won't catch. These can be addressed if you use a Regex to parse the input.

def cells(range: String): Seq[String] = {
  val format = "([A-Z])(\\d+):([A-Z])(\\d+)".r
  val format(colStart, rowStart, colEnd, rowEnd) = range

  for {
    c <- colStart.head to colEnd.head    //from String to Char
    r <- rowStart.toInt to rowEnd.toInt  //from String to Int
  } yield s"$c:$r"  //back to String
}

This will throw if the input string doesn't match the expected format. If you'd prefer it print the error and return nothing (an empty Seq[String]) then you can use a match statement instead, with a default case _ => for the format failure.

Notice that I use a for comprehension here. Whenever you see a map() inside a flatMap() that's a flag indicating that a for might do the same thing in a clearer/cleaner manner.

\$\endgroup\$
  • \$\begingroup\$ Code is compiling for me, here is same code in a online compiler scastie.scala-lang.org/eQxRuAvfR9qgCwNMiapGmg Also sorted is needed to handle input like "D5:A1" , else it will return empty seq. without sorted version - scastie.scala-lang.org/fD8FBWMDRRyzdvPvv4bazg I thought about using regex and agree it makes validation straightforward. But I am not very comfortable in writing regex (specially in an interview), learning it. How about working with Strings and using some thing like this - scala> ("A233" take 1, "A233" drop 1) res92: (String, String) = (A,233) \$\endgroup\$ – vikrant Jan 5 at 5:30
  • \$\begingroup\$ But I do agree regex is best and I should learn it. It makes it super simple. I accept it as a answer. \$\endgroup\$ – vikrant Jan 5 at 5:34
0
\$\begingroup\$

You're failing to handle columns beyond column Z (which should be AA) and rows beyond row 9. Your variable naming is also reversed: the columns are designated using letters and the rows are designated using numbers.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.