Speaking strictly to how to format that code, without making any structural changes, I'd do it like this:
Console println (
(
io.Source
fromFile "names.txt"
getLines ()
mkString ""
split ","
map (x => x.slice(1, x.length - 1))
sortBy (x => x)
zipWithIndex
)
map (t => (t._2 + 1) * (t._1 map (_.toChar - "A"(0).toChar + 1) sum) )
sum
)
Or, perhaps, to get around the parameterless methods, I'd do this:
Console println (
io.Source
.fromFile("names.txt")
.getLines
.mkString
.split(",")
.map(x => x.slice(1, x.length - 1))
.sortBy(x => x)
.zipWithIndex
.map(t => (t._2 + 1) * (t._1 map (_.toChar - "A"(0).toChar + 1) sum) )
.sum
)
Note that there is plenty of space for comments, but, generally speaking, what
is being done is usually clear. People not used to it might sometimes get lost
halfway through, without variables to keep track of the meaning/type of the
transformed value.
Now, some things I'd do differently are:
println ( // instead of Console println
Source // put import elsewhere
.fromFile("names.txt")
.mkString // Unless you want to get rid of /n, which is unnecessary here
.split(",")
.map(x => x.slice(1, x.length - 1))
.sorted // instead of sortBy
.zipWithIndex
.map { // use { only for multiple statements and, as in this case, pattern matching
case (c, index) => (index + 1) * (c map (_ - 'A' + 1) sum) // chars are chars
}
.sum
)
I'd also not do sum and multiplying in the same step, so:
.sorted
.map(_ map (_ - 'A' + 1) sum)
.zipWithIndex
.map { case (av, index) => av * (index + 1) }
.sum
Finally, I don't much like that string resizing, so I might resort to regex instead. Add a little refactoring, and this is something I'd probably write:
import scala.io.Source
def names = Source fromFile "names.txt" mkString
def wordExtractor = """"(.*?)"""".r
def words = for {
m <- wordExtractor findAllIn names matchData
} yield m group 1
def alphabeticValue(s: String) = s map (_ - 'A' + 1) sum
def wordsAV = words.toList.sorted map alphabeticValue
def multByIndex(t: (Int, Int)) = t match {
case (av, index) => av * (index + 1)
}
def wordsAVByIndex = wordsAV.zipWithIndex map multByIndex
println(wordsAVByIndex.sum)
EDIT
The next step would be a renaming refactoring -- choosing names that better convey what each part of the code is doing. Ken suggested better names in the comments, and I'd appropriate them for one more variant (it also showcases nicely how much better naming improves readability).
import scala.io.Source
def rawData = Source fromFile "names.txt" mkString
// I'd rather write "match" than "m" in the next snippet, but
// the former is a keyword in Scala, so "m" has become more
// common in my code than "i". Also, make the return type of
// getWordsOf clear, because iterators can be tricky.
// Returning a list, however, makes a much less cleaner
// definition.
def wordExtractor = """"(.*?)"""".r
def getWordsOf(input: String): Iterator[String] = for {
m <- wordExtractor findAllIn input matchData
} yield m group 1
def wordList = getWordsOf(rawData).toList
// I stole letterPosition from Kevin's solution. There, I said it. :-)
def letterPosition(c: Char) = c.toUpper - 'A' + 1 // .toUpper isn't necessary
def alphabeticValueOfWord(word: String) = word map letterPosition sum
def alphabeticValues = wordList.sorted map alphabeticValueOfWord
// I don't like multAVByIndex, but I haven't decided on a better
// option yet. I'm not very fond of declaring methods that return
// functions either, but I find that better than explicitly handling
// tuples (oh, for the day tuples/arguments are unified!).
def multAVByIndex = (alphabeticValue: Int, index: Int) =>
alphabeticValue * (index + 1)
def scores = alphabeticValues.zipWithIndex map multAVByIndex.tupled
println(scores sum)
maponto a different line? I'm interested to find out what real Scala-ites have to say about this.