3
\$\begingroup\$

Write a program that, given a word and a list of possible anagrams, selects the correct sublist.

Given "listen" and a list of candidates like "enlists" "google" "inlets" "banana" the program should return a list containing "inlets".

Here is my solution:

class Anagram(word: String) {
  val sorted_word = word.toLowerCase.sorted
  val origin_word = word.toLowerCase

  def matches(words: Seq[String]) = {
    words.filter(w => w.toLowerCase.sorted == sorted_word && w.toLowerCase != origin_word)
  }

}

Here is the gist with specs.

\$\endgroup\$
1
  • 7
    \$\begingroup\$ Just as a general note, I'd recommend avoiding too many questions in a row, the reviews you get from one question can often be applied to other pieces of code you write as well. \$\endgroup\$ Commented Jul 1, 2015 at 9:56

3 Answers 3

4
\$\begingroup\$

Looks good! Just one little thing that I, personally would do to remove one .toLowerCase call per item: map it first. So, instead of calling .toLowerCase in the filter, you can do this:

words.map(w => w.toLowerCase)
     .filter(w => w.sorted == sorted_word && w != origin_word)

You've also got a random newline before the end of the class.

Aside from that little niggle, it looks good! There's... really not much I can say. It's short code.

\$\endgroup\$
3
  • \$\begingroup\$ Is this a good idea to have a map and a filter separately? It goes through the list two times then \$\endgroup\$
    – HeeL
    Commented Jul 1, 2015 at 14:06
  • \$\begingroup\$ @HeeL My thinking was that, because the toLowerCase was applied only once, it would save time -- AFAIK iteration is faster than iteration and mutation. \$\endgroup\$
    – anon
    Commented Jul 1, 2015 at 17:02
  • \$\begingroup\$ @HeeL I am no Scala expert, but there is a possibility to have them be lazy transformers, which would be quite efficient, see scala-lang.org/docu/files/collections-api/collections_42.html for more info. \$\endgroup\$ Commented Jul 4, 2015 at 16:55
2
\$\begingroup\$

If you call this several times, it will be more efficient to put all of words into a Map[String, List[String]] where the keys are sorted

words.groupBy(_.sorted)

Which allows you to get all the words in a simple lookup from there on in.

\$\endgroup\$
1
\$\begingroup\$

CASE CLASS

I would change Anagram to a case class. One reason for this change is more concise object declaration, e.g:

  val a = Anagram("silent")
  // as opposed to ...
  val b = new Anagram("inlets")

Another benefit is that wordOrigin automatically becomes a field for any Anagram object (this is also possible with standard class declaration by prepending val to the front of the parameter.) These two examples don't really demonstrate the full utility of case classes so if you'd like to learn more check out this link.

STYLE

Current Scala dogma utilizes camelCase. So I've swapped word_origin and word_sorted for wordSorted and wordOrigin. I also added a return type to your method matches.

A QUICK NOTE ON TYPES

One of the cool things about types is that they allow you to constrain your program. For example, as your matches method currently stands there are over twenty different collection types (of the mutable and immutable variety) that may be passed in for words (see these inheritance graphs). Long story short, I swapped Seq for List.

STRING OPERATIONS

Finally, to get around calling toLowerCase I utilized a method from StringOps called equalsIgnoreCase which returns true if the caller and the input String are equal (regardless of capitalization), false otherwise. To be honest I only changed this bit to show you another possibility.

case class Anagram(wordOrigin: String) {
  val wordSorted = wordOrigin.sorted

  def matches(xs: List[String]): List[String] = {
    xs.filter(w => 
      w.sorted.equalsIgnoreCase(wordSorted) && !w.equalsIgnoreCase(wordOrigin) 
    )
  }
}

NAMES

Whoops, one more thing. I think I would also rename your class from Anagram to Word or something similar and rename the method from matches to anagrams. That is:

case class Word(...) {
  // ...
  def anagrams(...) = { ... }
}
\$\endgroup\$
1
  • \$\begingroup\$ Why do you prefer List over its ancestors? In the above code you are not making use of any features that List provides and Iterable does not. \$\endgroup\$ Commented Jul 5, 2015 at 17:28

Your Answer

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

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