Some time ago I was doing some repetitive task. My friend saw this and asked me: “Why don’t you just write a computer program. aren’t you a programmer?”. Sure, I was a programmer, but this was a task too small to spend time writing a whole C++ program. There was all this compiling and writing a Makefile, it was too difficult. I needed a scripting language. I tried a few times to learn Perl, but it felt way to complicated. Then one day I found Ruby and it just clicked in. Everything is so simple. Finally the computer helps me programming, not the other way around.

Map & Select

Some weeks ago, I realized I didn’t even know how to write a for loop in Ruby. You see, I never needed to write one. Every enumeration has the each method. And there are the map and select methods. Chaining them a few times is enough to solve any problem.

Recently I exported some JPG files with the wrong name. Should I change them by hand or ask the computer to change them for me. The names were in the format Picture_1234.JPG. But I wanted them to be named IMG_1234.JPG. This is how you could change some files.

  Dir.glob('*.jpg').
    map { |name| [name, name.sub(/^Picture/, 'IMG')] }.
      each{ |old, new| File.rename(old, new) }

  Dir.glob('*.jpg')
    => ["IMG_8200.jpg", "IMG_8201.jpg", "IMG_8202.jpg", ... ]


So what’s happening here? First we get all JPG files, then we substitute the name and replace ‘Picture’ at the beginning of the string with ‘IMG’, then for each entry we call File.rename.

Let’s try another problem. I went on a long hike last summer. In total 2 weeks and 240km of walking. I would like to know how many meters did I ascend and descend in total. I had no GPS data so how could I calculate this? I remembered I took many pictures. Would it be possible, to extract the GPS data from those JPG files? Sure enough, the exifr library can read GPS data from JPG files. Excellent!

    require 'exifr'
    files = Dir.glob('hiking_pics/*.JPG')
    altitudes = files.map{ |img_file| EXIFR::JPEG.new(img_file) }.
      reject{ |img| img.gps.nil? }.
      map{ |img| img.gps_altitude.to_f }

    difs = altitudes.each_cons(2).map{ |a, b| b - a }

    up = difs.select{ |x| x > 0}.inject(:+)
      => 19219.77

    down = difs.select{ |x| x < 0}.inject(:+)
      => -19036.42


Not bad. I climbed 19Km up and about the same down. You would ask how come I didn’t climb down the same altitude. Well, it’s because of errors in GPS altitude measurements and because some photos didn’t have GPS data.

I hope this will encourage you to use the computer more for those tasks that are too small to spin up the C++ compiler, but repetitive enough that it would make you feel bad because you are doing the work your computer should do. After all the computer is the slave, not the humans.