Collections, method chains and train wrecks (and SQL)

Last Friday, I got to teach about collections and closures in Ruby for ECC. That gave me an idea to write a post about one of the mistakes people coming from other languages tend to make when going into Ruby.

Let’s take the first problem from Project Euler:

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.

Looks simple enough. In pseudocode, your typical fresh grad programmer might do this:

sum <- 0
for i <- 1 to 999
  if i % 3 == 0 or i % 5 == 0
    sum += i
  end if
end for

A rubyist, however, will compress that 6 line program into a single line. Here is one possible solution:

(1..999).select { |x| x % 3 == 0 or x % 5 == 0 }.reduce(:+)

This line of code chains the 3 main components of the algorithm above:

  1. (1..999) - find a way to process numbers from 1 to 999. Here we created a Range that we can process as a whole.
  2. .select { |x| x % 3 == 0 or x % 5 == 0 } - process only the multiples of 3 and 5. Here, the method called selects only the elements that return true inside the passed block.
  3. .reduce(:+) - find the sum of the elements. Here we used the shorthand form of Ruby's reduce operation that sums the elements.

Let's try a harder example, problem 6:

(1..100).reduce(:+) ** 2 - (1..100).map { |x| x * x }.reduce(:+)

Here we see Ruby's map, which simply creates a copy of the source collection and applying the mapping function to each element. The map above is pretty trivial; we could even replace it with the long form of the reduce method.

(1..100).reduce(:+) ** 2 - (1..100).reduce(0) { |sum, x| sum + x * x }

While method chaining wouldn't be new to the novice developer, the concept of passing functions to methods, allowing greater flexibility, will be. Functional programming has been long forgotten even at the top universities in this country.

Another problem is that method chains can be too long. Some people call these chains "train wrecks". Obviously, this is a subjective matter, but one cannot deny that very long method chains are hard to debug. For example, here's one possible solution to problem 20:

(2..100).reduce(:*).to_s.scan(/./).map { |x| x.to_i }.reduce(:+)

This line simply:

  1. creates a range from 2 to 100 (1 is ignored in the factorial)
  2. calculate the factorial by multiplying them together
  3. convert it to a string
  4. create an array whose elements consist of single characters from the string (split("") also works)
  5. convert each element to integer
  6. calculates the sum of the elements

One way of debugging this long method chain would be to insert a tap method call to inspect the intermediate value of the chain. For example, if you do this:

(2..100).reduce(:*).to_s.scan(/./).map { |x| x.to_i }
.tap { |x| puts x.inspect }.reduce(:+)

you'll get the array of numbers before the reduce.

irb(main):001:0>(2..100).reduce(:*).to_s.scan(/./).map { |x| x.to_i }
.tap { |x| puts x.inspect }.reduce(:+)
[9, 3, 3, 2, 6, 2, 1, 5, 4, 4, 3, 9, 4, 4, 1, 5, 2, 6, 8, 1, 6, 9, 9, 2, 3, 8, 8 
, 5, 6, 2, 6, 6, 7, 0, 0, 4, 9, 0, 7, 1, 5, 9, 6, 8, 2, 6, 4, 3, 8, 1, 6, 2, 1,
4, 6, 8, 5, 9, 2, 9, 6, 3, 8, 9, 5, 2, 1, 7, 5, 9, 9, 9, 9, 3, 2, 2, 9, 9, 1, 5,
 6, 0, 8, 9, 4, 1, 4, 6, 3, 9, 7, 6, 1, 5, 6, 5, 1, 8, 2, 8, 6, 2, 5, 3, 6, 9, 7
, 9, 2, 0, 8, 2, 7, 2, 2, 3, 7, 5, 8, 2, 5, 1, 1, 8, 5, 2, 1, 0, 9, 1, 6, 8, 6,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
=> 648

Not exactly pretty, nor is it the most interesting use of tap, but it still gets the work done.

As a bonus, I'd just like to share a realization I had a while back.

Web developers shouldn't have to have problems with list processing because they deal with lists all the time: in SQL!

Think about it, you can define filter options in WHERE clauses, while map and reduce can be done in the SELECT clause. Assuming you have a table numbers with a column number with 100 records, each corresponding to numbers from 1 to 100, problem 6 can be solved by the following SQL statement:

SELECT SUM(number) * SUM(number) - SUM(number * number) FROM numbers

Web Developer Interview Questions

Earlier this week, I had to interview a bunch of applicants for a web developer role. The idea is to filter out those who aren’t really experienced as the job asks for people with at least 6 months of experience.

Anyway, below is the test I gave them. I don’t feel like giving something like it again in the future (it’s pretty crappy IMHO) so I think it would be a good idea to share it instead of just throwing it away.

Determine whether the statements below are true or false. Be prepared to explain your answer.

  1. A primary key can be composed of multiple columns.
  2. When you have two tables in a parent-child relationship (i.e. one table has a foreign key referring to the other table) deleting a parent record will delete all child records of that record.
  3. Escaping special characters is the best way to avoid SQL injection.
  4. You can undo UPDATE and DELETE changes to the database.
  5. The VARCHAR data type can be used to save space when used over CHAR.
  6. When using an RDBMS, normalization must be done for all tables.
  7. Indexes speed up database actions.
  8. Foreign keys are usually indexed.
  9. Many-to-many relationships are implemented via junction/join tables.
  10. Some HTML elements have been deprecated in favor of CSS.
  11. The <strong> element can be used interchangeably with the <b> element.
  12. Under strict XHTML rules, <br> is not a valid usage of the line break element.
  13. The href attribute of the anchor element only accepts relative and absolute links.
  14. The image tag is a block element.
  15. When a form is submitted, the submitted data is derived from only the input elements inside the form.
  16. Multiple elements can have the same id attribute.
  17. Web servers serve content at port 443.
  18. A web server can identify if a client has visited the website before.
  19. POST is idempotent.
  20. A browser redirect can be initiated by a response with an empty body.
  21. In JavaScript, the var keyword is optional when declaring variables so it can be omitted in all cases.
  22. You must specify a function name when declaring JavaScript functions.
  23. Ajax will prevent you from performing other actions until the Ajax action is completed.
  24. You are limited to using XML in Ajax.
  25. You cannot change the values of a class variable.
  26. Constructors are instance methods.
  27. Polymorphism refers to the ability to define functions to have different behaviors depending on the passed arguments.
  28. High cohesion and loose coupling can improve coding speed.
  29. You can combine the features of two classes via inheritance.
  30. Encapsulation is primarily used for security reasons.

Answers below the cut.

Continue reading “Web Developer Interview Questions”