Skip to content

Instantly share code, notes, and snippets.

@malandrina
Created September 18, 2012 19:35
Show Gist options
  • Save malandrina/3745321 to your computer and use it in GitHub Desktop.
Save malandrina/3745321 to your computer and use it in GitHub Desktop.
Anagram solvers for same-length anagrams, in Ruby

Anagram solvers for same-length, single word anagrams, in Ruby

I wanted to write an anagram solver that would return a list of the anagrams of the same length of a given word/combination of characters, such that:

same_length_anagrams("deify") => "edify"

or

same_length_anagrams("ogb") => "bog", "gob"

or

same_length_anagrams("quone") => nil

It seemed clear that a good way to tell if two words are anagrams is by checking for a match when both words' characters are sorted alphabetically, i.e.

("deify").chars.to_a.sort.join == ("edify").chars.to_a.sort.join

The difficult part, for me, was figuring out how to speedily iterate through an enormous word list to find matches.

In my first attempt, I created a hash from a list of (English) words in which values are words and keys are characters in those words, sorted alphabetically. Anagrams are found by returning the values associated with an input word's sorted characters.

Then, just to see if a different approach would be faster and/or require fewer lines of code, I wrote another method which simply uses a loop to check each entry of the word list for a match.

So far, one method does not appear to be substantially faster than the other.

Using a hash

class AnagramSolver
	def find_same_length_anagrams(word, word_list)
			# Create 'dictionary' by reading word list into array
			word_list = File.open(word_list).read.split(/\b/)
			# Map array to hash
			word_hash = Hash[word_list.map { |x| [x.upcase, x.chars.to_a.sort.join.upcase] }]
			# Group hash elements by values
			word_hash = word_hash.group_by { |key, value| word_hash[key] }
			# Remove non-word values
			word_hash.each_pair do |key, value|
				word_hash[key] = value.transpose.delete_at(0)
			end

			# Search for anagrams
			if word_hash[word.chars.to_a.sort.join.upcase] == nil
				puts "Sorry! No #{word.length}-letter anagrams found."
			else
				puts word_hash[word.chars.to_a.sort.join.upcase]
			end
		end
	end
end

solver = AnagramSolver.new
puts "Anagram this:"
solver.same_length_anagrams(gets.chomp, 'wordlist.txt')

I like the idea of using a hash, but my implementation seems cumbersome and slow. Seems like it could use improvement in the following areas:

  1. Aesthetics/readability: Nicer-looking, more compact code for creating the hash
  2. Functionality: Defining the creation of the hash as a separate activity which occurs when the program loads, rather than each time I call #same_length_anagrams.

Using an array and a loop

class AnagramSolver
	def same_length_anagrams(word, word_list)
		# Create 'dictionary' by reading word list into array
		dictionary = File.open(word_list).read.split(/\b/)
		result = []
		# Sort the characters of the input word alphabetically
		word_characters = word.chars.to_a.sort.join.downcase
		# Use a loop to check each dictionary entry for a match
		dictionary.each do |x|
			if x.chars.to_a.sort.join.downcase == word_characters
				result.push(x)
			end
		end

		# Return anagrams
		if result == []
			puts "Sorry! No #{word.length}-letter anagrams found."
		else
			puts "Anagrams of '#{word}':"
			puts result
		end
	end
end

solver = AnagramSolver.new
puts "Anagram this:"
solver.same_length_anagrams(gets.chomp, 'wordlist.txt')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment