In Progress
Unit 1, Lesson 1
In Progress

Hash Invert

Video transcript & code

So, I have this Ruby tool I've been working on. The function of the tool isn't important for today's purposes. What you need to know is that it frequently presents the user with lists of long file pathnames.

@files = [
  "this/is/a/very/long/path",
  "so/is/this/one",
  "and/this/one/is/a/straight/up/doozy"
]

puts @files

# >> this/is/a/very/long/path
# >> so/is/this/one
# >> and/this/one/is/a/straight/up/doozy

Now, it's not a very nice user experience to be presented with a list of long filenames and be expected to type them back in in order to tell the tool to do something with the file. So I thought I'd give users a shortcut. Before presenting the user with a list of files, the program first generates a shortcode for each filename. When the files are listed, the shortcodes are listed with them. And the user can choose to specify either a whole filename or a shortcode in future interactions.

@shortcodes = {
  "this/is/a/very/long/path" => "@1",
  "so/is/this/one" => "@2",
  "and/this/one/is/a/straight/up/doozy" => "@3"
}
@files = [
  "this/is/a/very/long/path",
  "so/is/this/one",
  "and/this/one/is/a/straight/up/doozy"
]

@files.each do |file|
  puts "#{file} (#{@shortcodes[file]})"
end

# >> this/is/a/very/long/path (@1)
# >> so/is/this/one (@2)
# >> and/this/one/is/a/straight/up/doozy (@3)

This map of filenames to shortcodes are saved to disk, so that they will always map to the same file in later invocations of the tool.

Of course, this means that when command line arguments are interpreted, the tool has to map shortcodes back to filenames. The method which performs this reverse mapping has to start with the map we've saved to disk, which is structured in the opposite direction: from filename to shortcode.

One way to do this is by using #detect to search through entries in the hash until we find the value we're looking for, then returning the associated key.

@files_to_shortcodes = {
  "this/is/a/very/long/path" => "@1",
  "so/is/this/one" => "@2",
  "and/this/one/is/a/straight/up/doozy" => "@3"
}

def file_for_shortcode(shortcode)
  @files_to_shortcodes.detect{|file, code| code == shortcode}.first
end

file_for_shortcode("@3")
# => "and/this/one/is/a/straight/up/doozy"

But there's an easier way. Ruby hashes have an #invert method which produces a new hash with the keys and values swapped.

@files_to_shortcodes = {
  "this/is/a/very/long/path" => "@1",
  "so/is/this/one" => "@2",
  "and/this/one/is/a/straight/up/doozy" => "@3"
}
@files_to_shortcodes.invert
# => {"@1"=>"this/is/a/very/long/path", "@2"=>"so/is/this/one", "@3"=>"and/th...

Using this method, it becomes trivially easy to implement our shortcode lookup method. We just invert the hash, and look up the full filename using the shortcode as a key.

@files_to_shortcodes = {
  "this/is/a/very/long/path" => "@1",
  "so/is/this/one" => "@2",
  "and/this/one/is/a/straight/up/doozy" => "@3"
}

def file_for_shortcode(shortcode)
  @files_to_shortcodes.invert[shortcode]
end

file_for_shortcode("@1")
# => "this/is/a/very/long/path"

And that's all for today. Happy hacking!

Responses