Weekly Puzzle: Snail!

Snail Sort

http://www.codewars.com/kata/snail

Given an n x n array, return the array elements arranged from outermost elements to the middle element, traveling clockwise.

 array = [[1,2,3],
         [4,5,6],
         [7,8,9]]
snail(array) #=> [1,2,3,6,9,8,7,4,5]  

For better understanding, please follow the numbers of the next array consecutively:

array = [[1,2,3],  
         [8,9,4],
         [7,6,5]]
snail(array) #=> [1,2,3,4,5,6,7,8,9]  

This image will illustrate things more clearly:

NOTE: The idea is not sort the elements from the lowest value to the highest; the idea is to traverse the 2-d array in a clockwise snailshell pattern.

NOTE 2: The 0x0 (empty matrix) is represented as [[]]

Important note!

This time the puzzle is a 4kyu difficulty level, so you'll all have 2 weeks to submit:

Submission Instructions

Complete the puzzle on CodeWars and then follow these instructions to get the link to your solution and email that link to weeklypuzzle@bloc.io or tweet it at @trybloc.
Submit by Friday February 19th 9am PST to be included.

Last weeks result: Don't Drink the Water

Last week our puzzle was to sort a glass full of different liquids based on their various densities.

Most solutions used some variation of the same approach.

  1. Flatten the array.
  2. Sort the array with sort_by
  3. Break the array back up into slices using the each_slice method

Here is Daniel Rassiner's solution

def separate_liquids(glass)  
  return [] if glass.empty?

  density = {"O" => 0, "A" => 1, "W" => 2, "H" => 3}
  amount = glass[0].size

  glass.flatten.sort_by{|liquid| density[liquid]}.each_slice(amount).to_a
end  

Mike Jewett's solution is almost identical

def separate_liquids(glass)

  density = { 
      'O' => 0.80, 
      'A' => 0.87,
      'W' => 1.00,
      'H' => 1.36
  }

  if glass.empty?
    []
  else
    glass.flatten.sort_by { |liquid| density[liquid] }.each_slice(glass[0].size).to_a
  end
end  

Brian Bugh used the (cool looking but probably unnecessary in this situation) Spaceship Operator. He also pulled the densities out into a constant, which in my opinion is a better practice than creating it inside the method.

DENSITY = { 'H' => 1.36, 'W' => 1.00, 'A' => 0.87, 'O' => 0.8 }  
def separate_liquids(glass)  
  return [] if glass.empty?
  glass.flatten.sort { |a, b| DENSITY[a] <=> DENSITY[b] }.each_slice(glass[0].length).to_a
end  

Fong Fan also used the spaceship operator and had an unconventional technique for flattening the array.

def separate_liquids(glass)  
  return glass if glass.size == 0

  # Extract all characters into a string
  width = glass[0].size
  glassValues = glass.join('').split('')

  # Sort based on values in a hash
  densities = {
    "H" => 1.36,
    "W" => 1.00,
    "A" => 0.87,
    "O" => 0.80
  }
  glassValues.sort!{|x,y| densities[x] <=> densities[y]}

  # Insert into a sub array until it reaches original size
  subArray = []
  finalArray = []

  glassValues.each do |x|
    subArray << x
    if subArray.size >= width
      finalArray << subArray
      subArray = []
    end
  end

  return finalArray
end  

Aaron Brager had an interesting approach by first converting each array element to its density value, sorting, and then converting back:

def separate_liquids(glass)  
  return [] if glass.empty?

  glass.flatten.map { |s| @density[s] }.sort.map { |d| @density.key(d) }.each_slice(glass[0].length).to_a 
end

@density = {"H" => 1.36, "W" => 1.00, "A" => 0.87, "O" => 0.80}

Thank you to all who participated and hack the planet!