Clojure Euler: Problem 022

Using names.txt a 46K text file containing over five-thousand first names, begin by sorting it into alphabetical order. Then working out the alphabetical value for each name, multiply this value by its alphabetical position in the list to obtain a name score.

For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would obtain a score of 938 x 53 = 49714.

What is the total of all the name scores in the file?


Problem is very easy, assuming you know how to work with files.

First of all read the file, clojure has awesome simple function slurp, discussed in Clojure Euler: Problem 008.

(slurp "names.txt")

Then, you need to select all words in that file:

(re-seq #"\w+" (slurp "names.txt"))

It return a list with the names. How that simple, yeah?

Next two steps are:

By the way, score consists of two parts. First of all, you must calculate natural score of the word, without its actual position in sorted list, and then you must multiply that score on its position.

First part looks like this:

(defn score [string]
  (reduce + (map #(- (int %) 64) string)))

Convert each character to its positional number in alphabet. For example the character "A" (capitalized) has ascii value of 65. If we substract 64 it becomes 1, what means "A" is a first letter in alphabet and so on.

To track positions in list we use map-indexed function. It works almost the same way as map, except it accept function of two arguments, index of item in current list, and item itself.

For example:

(map-indexed #(vec [%1 %2]) ["a" "b" "c"]) => ([0 "a"] [1 "b"] [2 "c"])

As indices start from zero, do not forget increment!

Bind all together

(reduce +
        (map-indexed #(* (inc %1) (score %2)) 
                     (sort (re-seq #"\w+" (slurp "names.txt"))))))

Solved! Code is here

P.S. Actually, this code is not elegant. We need to read from the end to the beginning of expression. And it can be solved by Threading Macro. Read the nice Fogus explanation

Our last solution, becomes transformed to:

(->> "names.txt"
    (re-seq #"\w+")
    (map-indexed #(* (inc %1) (score %2)))
    (reduce +))

Way more readable, huh?

mishadoff 20 October 2013
blog comments powered by Disqus