0

I am new to ruby and doing a RubyMonk tutorial. One of the problems is the following. Can someone please enlighten me because I am not understanding the suggested solution?

Problem Statement Create a method named 'sort_string' which accepts a String and rearranges all the words in ascending order, by length. Let's not treat the punctuation marks any different than other characters and assume that we will always have single space to separate the words.

Example: Given a string "Sort words in a sentence", it should return "a in Sort words sentence".

Suggested Solution:

def sort_string(string)
  string.split(' ').sort{|x, y| x.length <=> y.length}.join(' ')
end

My questions are;

1) Why are there two block variables being passed through? Should there only be one, because you are going through every element of the sentence one at a time?

2) I looked up the <=> operator and it states,"Combined comparison operator. Returns 0 if first operand equals second, 1 if first operand is greater than the second and -1 if first operand is less than the second." So aren't we essentially sorting by -1, 0, and 1 then, not the words?

Thank you very much in advance for your help!

4 Answers 4

3

1) Why are there two block variables being passed through? Should there only be one, because you are going through every element of the sentence one at a time?

Because that's how the sort method works. It compares two elements at a time, and the block tells it how to compare the two elements. There is a single-element method called sort_by which will only require one which could be used in this case:

def sort_string(string)
  string.split(' ').sort_by{|x| x.length}.join(' ')
end

Or even shorter:

def sort_string(string)
  string.split(' ').sort_by(&:length).join(' ')
end

2) I looked up the <=> operator and it states,"Combined comparison operator. Returns 0 if first operand equals second, 1 if first operand is greater than the second and -1 if first operand is less than the second." So aren't we essentially sorting by -1, 0, and 1 then, not the words?

Again, this is how sorting works. Sort looks at the result and, depending upon the value -1, 0, or 1 will order the original data accordingly. It's not ordering the results of <=> directly. If you've done any C programming and used strcmp, think about how you would use that function. It's based upon the same concept.

Sign up to request clarification or add additional context in comments.

2 Comments

You could shorten .sort_by{|x| x.length} to .sort_by(&:length).
@Robert indeed, that would be the most concise, and it's good to point it out. I left the verbose method for clarity of explanation since the OP was asking specifically about the number of block parameters.
0

For the first question, if you look at the documentation for the sort method its block form takes two variables

http://www.ruby-doc.org/core-2.0.0/Array.html#method-i-sort

For the second question, the spaceship operator does a comparison between the two operands and then returns -1, 0, or 1, and then you're sorting on the results. Yes, you're sorting on -1, 0, and 1, but those values are obtained from the comparison.

Comments

0
  1. There are two block variables because to sort you need two items - you can't compare one item against nothing or itself.

  2. You are sorting by -1, 0 and 1 - through the words.

Both of these questions are related to the sort method - here's an example which might make it clearer:

(1..10).sort { |a, b| b <=> a }  #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

for each number 1 -10, sort looks at 'a' and 'b' - the block arguments. then in the block, the code says to order b higher than a - and this is what it does

Comments

0

The sort function by default sorts the items in some order.

However, if it is passed a block, it uses that block to compare the elements of the array, so that you can define a different, custom order of the elements.

That is, the block has to compare the elements. A minimalistic working version of such comparison is to compare two elements, just to know which one is "greater-or-equal".

This is why the custom block takes two parameters: those are the elements to compare. You don't actually know which one. The sort will perform some sorting algorithm and depending on the internals, it will pick some pairs of elements, compare them using your block, and then, well, it will use that knowledge to reorder the elements in order.

As you provide a block that 'compares', it'd be not very efficient to just return BOOL that says "greater or not". A better and often a bit faster way is to determine if the elements are equal, less, or greater. At once.

arr.sort {|item1, item2}
    if item1 < item2 return :lesser
    if item1 == item2 return :equal
    if item1 < item2 return :greater
}

This is just pseudocode.

With numbers, it is very easy: just subtract them. If you get less-than-zero, you know that the first was lesser. If you get more-than-zero, the first was bigger. If you got zero, they were equal. So, over the time it was 'standarized' way of describing the three-way comparison result to some sorting algorithms.

arr.sort {|item1, item2}
    if item1 < item2 return -1
    if item1 == item2 return 0
    if item1 > item2 return 1
}

or just

arr.sort {|item1, item2}
    return item1 - item2
}

Not all types can be subtracted though. Ruby went somewhat further and defined "comparison operator". Instead of just separate </>/==/<=/>=, it provides you with <=> that returns numeric values. -1 meaning that left was lesser and so on.

arr.sort {|item1, item2}
    return item1 <=> item2
}

Now, if you provide <=> operator to MyClass, you will be able to sort them easily, even if plain item1-item2 cannot work on non-numeric 'MyClass'..

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.