Skip to content

Instantly share code, notes, and snippets.

@Kotauror
Created April 23, 2018 09:06
Show Gist options
  • Save Kotauror/6372ecfadea4f52a3b15a0455e49d6e2 to your computer and use it in GitHub Desktop.
Save Kotauror/6372ecfadea4f52a3b15a0455e49d6e2 to your computer and use it in GitHub Desktop.
Sort_by ruby method - sorting using multiple attributes.

Sort_by ruby method - sorting using multiple attributes.

I guess everyone knows that feeling when you struggle with a codewars kata, create (or even not!) a looong solution, then you check the solutions of of others and go whaaaaaaaat -- they did it in one line?

This is how I felt today and this is how I refreshed my memory of Ruby's sort_by method.

Task:

My friend John and I are members of the "Fat to Fit Club (FFC)". John is worried because each month a list with the weights of members is published and each month he is the last on the list which means he is the heaviest.

I am the one who establishes the list so I told him: "Don't worry any more, I will modify the order of the list". It was decided to attribute a "weight" to numbers. The weight of a number will be from now on the sum of its digits.

For example 99 will have "weight" 18, 100 will have "weight" 1 so in the list 100 will come before 99. Given a string with the weights of FFC members in normal order can you give this string ordered by "weights" of these numbers?

Example:
"56 65 74 100 99 68 86 180 90" ordered by numbers weights becomes: "100 180 90 56 65 74 68 86 99"

When two numbers have the same "weight", let us class them as if they were strings and not numbers: 100 is before 180 because its "weight" (1) is less than the one of 180 (9) and 180 is before 90 since, having the same "weight" (9) it comes before as a string.

All numbers in the list are positive numbers and the list can be empty.

My Attempt

My plan was to create a hash map, where each number(key) has a value which is a sum of the numbers the key is built of. Sounds great and almost worked :D The problem was with two k&v pairs having the same value - doing additional sorting for pairs having the same values was a real pain so I gave up and checked other peoples solutions. And I was shocked.

One-liner and how does it work

This is the solution:

def order_weight(strng)
  strng.split.sort_by { |n| [n.chars.map(&:to_i).reduce(:+), n] }.join(" ")
end
What it's doing generally
  • Creates an array from the string by splitting it;
  • Calls sort by with n parameters.
  • joins the array to create the string.
Closer look at sort_by

sort_by sorts from low to high. So given the following array: [2, 1, 3], [2, 1, 3].sort_by{ |i| i } will return [1, 2, 3]. In our case, we also want to sort the numbers, and we will use the |n| parameters as they used the |i|. The only difference is that instead of simply returning numbers (i), we will sort our array by two parameters:

  • n.chars.map(&:to_i).reduce(:+) - according to the sum of numbers which build the number
  • n according to the numbers as such (covers the case that was problematic in the hash solution)

The two parameters work like this:

  • sort according to the first one,
  • if there are two results which are the same, take the second parameter under consideration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment