Forum: Ruby a beginner case question

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Taylor S. (Guest)
on 2009-01-14 17:57
I am using case in to determine the sorting method used for an array as
so:

  def single_choice(object)

    # sort answer order based on question.list_method
    answers = object.answers

    sorted_answers = case object.list_method
      when 'position' then answers.sort_by {|answer| answer.position}
      when 'random' then answers.sort_by {rand}
      when 'alphabetical' then answers.sort_by {|answer| answer.text}
      when 'reverse_alpha' then answers.sort_by {|answer| answer.text}
        return answers.reverse
      when 'shortest_first' then answers.sort_by {|answer|
answer.text.length}
      when 'longest_first' then answers.sort_by {|answer|
answer.text.length}
        return answers.reverse
    end

This does not because of the return statements.  However, I need to
reverse the result for those two options.  How can I do this with case
statements?  Or must I use if..elsif?
Brian C. (Guest)
on 2009-01-14 18:12
Taylor S. wrote:
>       when 'reverse_alpha' then answers.sort_by {|answer| answer.text}
>         return answers.reverse

What you're doing here may be clearer if you write it out like this:

  when 'reverse_alpha'
    answers.sort_by {|answer| answer.text}
    return answers.reverse

What's happening is (1) the answers.sort_by... line is creating a new
sorted array - and then discarding this array. (2) The last line then
returns the original 'answers' array, reversed.

You could fix this by:

  when 'reverse_alpha'
    answers.sort_by {|answer| answer.text}.reverse

The value of this whole expression becomes the value result of the case
statement, which in turn becomes the return value of the method.

If you really did need to use 'return' to drop straight out of the
method, you could do

  when 'reverse_alpha'
    answers = answers.sort_by {|answer| answer.text}
    return answers.reverse

Or just:

  when 'reverse_alpha'
    return answers.sort_by {|answer| answer.text}.reverse
Jesús Gabriel y Galán (Guest)
on 2009-01-14 18:13
(Received via mailing list)
On Wed, Jan 14, 2009 at 4:56 PM, Taylor S. 
<removed_email_address@domain.invalid>
wrote:
> I am using case in to determine the sorting method used for an array as
> so:

>  This does not because of the return statements.  However, I need to
> reverse the result for those two options.  How can I do this with case
> statements?  Or must I use if..elsif?

You can do this:

 def single_choice(object)

    # sort answer order based on question.list_method
    answers = object.answers

    sorted_answers = case object.list_method
      when 'position' then answers.sort_by {|answer| answer.position}
      when 'random' then answers.sort_by {rand}
      when 'alphabetical' then answers.sort_by {|answer| answer.text}
      when 'reverse_alpha' then answers.sort_by {|answer|
answer.text}.reverse
      when 'shortest_first' then answers.sort_by {|answer|
answer.text.length}
      when 'longest_first' then answers.sort_by {|answer|
answer.text.length}.reverse
    end
end

Jesus.
Michael M. (Guest)
on 2009-01-14 18:18
I don't mean to be too intrusive as I am a dreadful IT Recruiter that
you probably all despise. I am working on filling a number of RoR
Developer positions(beginners are welcomed) for an exciting young
company in NYC. If you are interested in learning more about the
position please e-mail me, removed_email_address@domain.invalid or call me at
212.991.1835. No one has to
know that you contacted me regarding and you can still bash me here in
the blog.

Have a great day! Thanks!






















Taylor S. wrote:
> I am using case in to determine the sorting method used for an array as
> so:
>
>   def single_choice(object)
>
>     # sort answer order based on question.list_method
>     answers = object.answers
>
>     sorted_answers = case object.list_method
>       when 'position' then answers.sort_by {|answer| answer.position}
>       when 'random' then answers.sort_by {rand}
>       when 'alphabetical' then answers.sort_by {|answer| answer.text}
>       when 'reverse_alpha' then answers.sort_by {|answer| answer.text}
>         return answers.reverse
>       when 'shortest_first' then answers.sort_by {|answer|
> answer.text.length}
>       when 'longest_first' then answers.sort_by {|answer|
> answer.text.length}
>         return answers.reverse
>     end
>
> This does not because of the return statements.  However, I need to
> reverse the result for those two options.  How can I do this with case
> statements?  Or must I use if..elsif?
Taylor S. (Guest)
on 2009-01-14 18:26
Thanks Brian and Jesus - I didn't know you could .reverse a code block.
I had tried that with parenthesis but not just on the block itself.
Jesús Gabriel y Galán (Guest)
on 2009-01-14 18:38
(Received via mailing list)
On Wed, Jan 14, 2009 at 5:26 PM, Taylor S. 
<removed_email_address@domain.invalid>
wrote:
> Thanks Brian and Jesus - I didn't know you could .reverse a code block.
> I had tried that with parenthesis but not just on the block itself.

Well, you are not reversing the code block, you are reversing the
result of the call to sort_by, which is an array.

The line is functionally identical to:

(answers.sort_by {|answer| answer.text}).reverse

The  sort_by method receives no params and a block, and returns an
array. You are then calling reverse on the resulting array.
It's more clear in Brian's explanation, where he used a temporary
variable to store the result of the sort_by call.

Hope this clarifies,

Jesus.
F. Senault (Guest)
on 2009-01-15 00:40
(Received via mailing list)
Le 14 janvier 2009 à 16:56, Taylor S. a écrit :

> This does not because of the return statements.  However, I need to
> reverse the result for those two options.  How can I do this with case
> statements?  Or must I use if..elsif?

To go a step further than just answering your question, I'd refactor the
whole function like this :

def single_choice(object)
  l = case object.list_method
      when /^position/ then lambda { |x| x.position }
      when /^rand/     then lambda {     rand       }
      when /^alpha/    then lambda { |x| x.to_s     }
      when /^length/   then lambda { |x| x.length   }
      end
  sorted_answers = object.answers.sort_by(&l)
  if order =~ /desc$/
    sorted_answers.reverse!
  end
  sorted_answers
end

The lamba is there to avoid the repetition of the sort_by calls, the use
of regexpen means you have to standardize your methods (length and
length_desc vs shortest_firts and longest_first).  On the other hand,
you can manage all kind of sorts without duplicating your sort method.

An even nicer form, if your arrays are of a manageable size, would be :

def single_choice(object)
  r = (object.list_method =~ /desc/ ? -1 : 1)
  l = case object.list_method
      when /^position/ then lambda { |x, y| (x.position <=> y.position)
* r }
      when /^rand/     then lambda {        rand
}
      when /^alpha/    then lambda { |x, y| (x.to_s <=> y.to_s)
* r }
      when /^length/   then lambda { |x, y| (x.length <=> y.length)
* r }
      end
  object.answers.sort(&l)
end

(But sort is slower for big arrays.)

You can also extract the case expression into a helper method if you
need to sort other kinds of objects...

HTH,

Fred
Taylor S. (Guest)
on 2009-01-15 00:51
F. Senault wrote:

> To go a step further than just answering your question, I'd refactor the
> whole function like this :
>
> def single_choice(object)
>   l = case object.list_method
>       when /^position/ then lambda { |x| x.position }
>       when /^rand/     then lambda {     rand       }
>       when /^alpha/    then lambda { |x| x.to_s     }
>       when /^length/   then lambda { |x| x.length   }
>       end
>   sorted_answers = object.answers.sort_by(&l)
>   if order =~ /desc$/
>     sorted_answers.reverse!
>   end
>   sorted_answers
> end

That is great usage of regex's -- thanks for showing me that!
This topic is locked and can not be replied to.