Largest number in an array below a given value

hello all. I've run into a snag in an assignment I'm doing. the description:

Return the first number from an Array that is less than a particular number
- 'limit.' You should make use of Enumerable#find

and then gives me this code to work with:

def first_under(array, limit)

end

puts first_under([13, 21, 7, 0, 11, 106], 10) == 7

for the life of me I can't figure out how to make this work. I've feebaly
tried a couple different things, but none including .find. I'm pretty much
at a total loss. This should be so simple (i think) so I really don't know
what I'm missing.

Hi.

find is a method you can use on an Enumerable. An Array is an Enumerable. The docs indicates that find accept a block and "Returns the first entry for which block is not false."

Search for blocks, this should solve your problem. :slight_smile:

Guillaume Hivert
Ruby && Crystal Developper
LinkedIn Profile<https://fr.linkedin.com/in/ghivert>

···

On déc. 3 2016, at 11:29 pm, Micky Scandal <mickyscandal@gmail.com> wrote:
hello all. I've run into a snag in an assignment I'm doing. the description:

Return the first number from an Array that is less than a particular number - 'limit.' You should make use of Enumerable#find

and then gives me this code to work with:

def first_under(array, limit)

end

puts first_under([13, 21, 7, 0, 11, 106], 10) == 7

for the life of me I can't figure out how to make this work. I've feebaly tried a couple different things, but none including .find. I'm pretty much at a total loss. This should be so simple (i think) so I really don't know what I'm missing.

Return the first number from an Array that is less than a particular number
- 'limit.' You should make use of Enumerable#find

That is a different problem to solve than the one described in the
subject. So which is it that you want to solve?

and then gives me this code to work with:

def first_under(array, limit)

end

puts first_under([13, 21, 7, 0, 11, 106], 10) == 7

From the name of the method it seems your subject is wrong and you are
really seeking the first number less than the limit. But your first
solution posted rather tries to implement the subject. I think
Guiilaume's solution is spot on:

array.find { |num| num < limit }

This is a nice illustration how important it is to *first* clarify
what problem to solve before coding solutions.

Kind regards

robert

···

On Sat, Dec 3, 2016 at 11:28 PM, Micky Scandal <mickyscandal@gmail.com> wrote:

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

OH! I must have missed that when I read through the docs. I was under the
impression that it would return all entries where block is not false. so
here's my solution:

def first_under(array, limit)
  array.sort.reverse!
  array.find {|num| num < limit}
end

might not be the most elegant(and if there is a better way I would like to
know) but alas! it works and that's good enough (at least until I get to
the re-factoring section...)
thank you thank you!

···

On Sat, Dec 3, 2016 at 2:44 PM, Guillaume Hivert < guillaume.hivert@outlook.com> wrote:

"Returns the first entry for which *block* is not false."

Be careful, array.sort returns a new array, and will not gives you what you want.
array.sort.reverse.find { |num| num < limit } is what you are looking for, or array.sort!.reverse!.find{ |num| num < limit }, if you want to keep a sorted array. (Or even array.sort.reverse!.find { |num| num < limit } will work.)

However, "Return the first number from an Array that is less than a particular number" doesn't seem to ask for a sorted array. Maybe array.find { |num| num < limit } is enough ?

Guillaume Hivert
Ruby && Crystal Developper
LinkedIn Profile<https://fr.linkedin.com/in/ghivert>

···

On déc. 4 2016, at 1:10 am, Micky Scandal <mickyscandal@gmail.com> wrote:

On Sat, Dec 3, 2016 at 2:44 PM, Guillaume Hivert <guillaume.hivert@outlook.com<mailto:guillaume.hivert@outlook.com>> wrote:
"Returns the first entry for which block is not false."

OH! I must have missed that when I read through the docs. I was under the impression that it would return all entries where block is not false. so here's my solution:

def first_under(array, limit)
  array.sort.reverse!
  array.find {|num| num < limit}
end

might not be the most elegant(and if there is a better way I would like to know) but alas! it works and that's good enough (at least until I get to the re-factoring section...)
thank you thank you!

huh... it looks like I was confusing myself and doing way more work than I
need to again. when it said the first number less than... i took that to
mean the first number chronologically not the first number from left to
right. but I suppose that does make sense. thanks for clarifying that for
me, and sorry for the confusion

···

On Sun, Dec 4, 2016 at 2:54 AM, Robert Klemme <shortcutter@googlemail.com> wrote:

On Sat, Dec 3, 2016 at 11:28 PM, Micky Scandal <mickyscandal@gmail.com> > wrote:

> Return the first number from an Array that is less than a particular
number
> - 'limit.' You should make use of Enumerable#find

That is a different problem to solve than the one described in the
subject. So which is it that you want to solve?

> and then gives me this code to work with:
>
> def first_under(array, limit)
>
> end
>
> puts first_under([13, 21, 7, 0, 11, 106], 10) == 7

From the name of the method it seems your subject is wrong and you are
really seeking the first number less than the limit. But your first
solution posted rather tries to implement the subject. I think
Guiilaume's solution is spot on:

array.find { |num| num < limit }

This is a nice illustration how important it is to *first* clarify
what problem to solve before coding solutions.

Kind regards

robert

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

That's almost exactly what I have except mine's on two lines. does that
make any difference? and i used the bang on reverse and not sort because I
guess I just assumed I only needed it on one of the two? is that not right?
(I did get the right answer with the code as it currently is)

  However, "Return the first number from an Array that is less than a
particular number" doesn't seem to ask for a sorted array. Maybe
array.find { |num| num < limit } is enough ?

in this particular case that would be enough but lets say I had this array:
   [13,21,0,7,11,106] # same array but the 0 and 7 are switched
the correct answer should be 7, but If I ran just the find like that it
would return 0 instead of 7 because it comes across the 0 first. that's why
I sorted and reversed it.

···

On Sat, Dec 3, 2016 at 4:28 PM, Guillaume Hivert < guillaume.hivert@outlook.com> wrote:

(Or even array.sort.reverse!.find { |num| num < limit } will work.)

I don't see your function as working. In order to get your function to do the right thing in two lines, you'd need

def first_under(array,limit)
   array.sort!.reverse!
   array.find {|num| num < limit}
end

The bang after the sort and reverse methods means that you are replacing the existing array with the modified one. If you have

array.sort.reverse!

the array.sort creates a new array, the reverse! reverses the new array, which is not saved, and the array.find will operate on the original, unmodified array.

array.sort!.reverse! sorts and replaces the array, and then reverses and replaces the array, and then array.find will operate on the modified array.

if you use

array.sort.reverse.find{ |num} num < limit }

you aren't modifying the array, the sort creates a new (sorted) array, the reverse creates a new (reversed) array on the sorted array, and the find operates on the new reversed sorted array (and then the created arrays are all garbage collected). This is preferred because the function above has the side effect of reordering the array, which is unexpected and may not be appropriate.

···

On 12/03/16 19:09, Micky Scandal wrote:

On Sat, Dec 3, 2016 at 4:28 PM, Guillaume Hivert > <guillaume.hivert@outlook.com <mailto:guillaume.hivert@outlook.com>> > wrote:

    (Or even array.sort.reverse!.find { |num| num < limit } will work.)

That's almost exactly what I have except mine's on two lines. does that make any difference? and i used the bang on reverse and not sort because I guess I just assumed I only needed it on one of the two? is that not right? (I did get the right answer with the code as it currently is)

  However, "Return the first number from an Array that is less than a particular number" doesn't seem to ask for a sorted array. Maybe array.find { |num| num < limit } is enough ?

in this particular case that would be enough but lets say I had this array:
   [13,21,0,7,11,106] # same array but the 0 and 7 are switched
the correct answer should be 7, but If I ran just the find like that it would return 0 instead of 7 because it comes across the 0 first. that's why I sorted and reversed it.

That's almost exactly what I have except mine's on two lines. does that
make any difference? and i used the bang on reverse and not sort because I
guess I just assumed I only needed it on one of the two? is that not right?
(I did get the right answer with the code as it currently is)

  However, "Return the first number from an Array that is less than a
particular number" doesn't seem to ask for a sorted array. Maybe
array.find { |num| num < limit } is enough?

Seems that way to me. Note that it says "first number", not "largest
number".

in this particular case that would be enough but lets say I had this array:
   [13,21,0,7,11,106] # same array but the 0 and 7 are switched
the correct answer should be 7, but If I ran just the find like that it
would return 0 instead of 7 because it comes across the 0 first. that's why
I sorted and reversed it.

The answer 7 only applies to the array given in the example, if you start
with a different array you should probably expect a different result. As
written, I'd expect 0.

Consider this approach, which doesn't use #find but solves the problem
using another key word ("first"):

    array.select{|e| e < limit }.first

Also note that bang methods (like #reverse!) sometimes return nil, so be
careful when chaining them.

Cheers

···

On 4 Dec 2016 11:11, "Micky Scandal" <mickyscandal@gmail.com> wrote:

On Sat, Dec 3, 2016 at 4:28 PM, Guillaume Hivert < guillaume.hivert@outlook.com> wrote:

(Or even array.sort.reverse!.find { |num| num < limit } will work.)

​If it helps keep these sort of things clear:

An array (even outside of programming) is "a systematic or ordered
arrangement of similar objects." In programming 'systematic' and 'ordered'
mean pretty much the same thing, and with all ordered collections, "first"
always means "first according to the *order* of the collection."

It doesn't matter if the ordering is by-index (as an Array), or
by-insertion (as a Hash), or by-smallest-to-largest (if you built a sorting
tree object) or any other system you can think of.

Cheers

···

On 5 December 2016 at 07:07, Micky Scandal <mickyscandal@gmail.com> wrote:

huh... it looks like I was confusing myself and doing way more work than I
need to again. when it said the first number less than... i took that to
mean the first number chronologically not the first number from left to
right. but I suppose that does make sense. thanks for clarifying that for
me, and sorry for the confusion

--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

It seems as if max_by() deserves a mention here:

    def first_under(array, limit)
      array.max_by{|e| e < limit ? e : 0 }
    end

That will get you the highest number in the array less than the limit. If you want the first number in the array less than the limit, as already covered, that’s just find():

    def first_under(array, limit)
      array.find{|e| e < limit }
    end

···

From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Matthew Kerwin
Sent: 04 December 2016 11:24 pm
To: Ruby users
Subject: Re: largest number in an array below a given value

On 5 December 2016 at 07:07, Micky Scandal <mickyscandal@gmail.com<mailto:mickyscandal@gmail.com>> wrote:
huh... it looks like I was confusing myself and doing way more work than I need to again. when it said the first number less than... i took that to mean the first number chronologically not the first number from left to right. but I suppose that does make sense. thanks for clarifying that for me, and sorry for the confusion

​If it helps keep these sort of things clear:

An array (even outside of programming) is "a systematic or ordered arrangement of similar objects." In programming 'systematic' and 'ordered' mean pretty much the same thing, and with all ordered collections, "first" always means "first according to the order of the collection."

It doesn't matter if the ordering is by-index (as an Array), or by-insertion (as a Hash), or by-smallest-to-largest (if you built a sorting tree object) or any other system you can think of.

Cheers
--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

No, this does not work reliably (e.g. when there are only negative
numbers in array).

There are multiple ways to solve this more robustly but if you want to
have only one iteration through the Enumerable (does not need to be an
Array!) then #inject leads to a nice solution:

def max_under(array, limit)
  array.inject nil do |max, i|
    i < limit && (max.nil? || i > max) ? i : max
  end
end

irb(main):011:0> max_under [13, 21, 7, 0, 11, 106], 10
=> 7
irb(main):012:0> max_under [13, 21, 7, 0, 11, 9, 106], 10
=> 9

Kind regards

robert

···

On Mon, Dec 5, 2016 at 9:38 AM, Andy Jones <Andy.Jones@jameshall.co.uk> wrote:

It seems as if max_by() deserves a mention here:

    def first_under(array, limit)
      array.max_by{|e| e < limit ? e : 0 }
    end

That will get you the highest number in the array less than the limit.

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

No, this does not work reliably (e.g. when there are only negative
numbers in array).

Okay, then:

    Array.max_by{|x| x < limit ? x : nil }

Nothing wrong with max_by AFAIK.

None of my example code posted here is fully tested against all possible data sets. I'm sorry if you assumed it was.

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer&gt;

No, this does not work reliably (e.g. when there are only negative
numbers in array).

Okay, then:

    Array.max_by{|x| x < limit ? x : nil }

irb(main):016:0> Array.max_by{|x| x < limit ? x : nil }
NoMethodError: undefined method `max_by' for Array:Class
        from (irb):16
        from /usr/bin/irb:11:in `<main>'

Nothing wrong with max_by AFAIK.

irb(main):015:0> limit = 10; [-1, 20, -2].max_by{|x| x < limit ? x : nil }
ArgumentError: comparison of NilClass with -1 failed
        from (irb):15:in `each'
        from (irb):15:in `max_by'
        from (irb):15
        from /usr/bin/irb:11:in `<main>'

None of my example code posted here is fully tested against all possible data sets. I'm sorry if you assumed it was.

Then why do you post them, especially since testing is so easy?

Regards

robert

···

On Mon, Dec 5, 2016 at 2:15 PM, Andy Jones <Andy.Jones@jameshall.co.uk> wrote:

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

> Okay, then:
>
> Array.max_by{|x| x < limit ? x : nil }

irb(main):016:0> Array.max_by{|x| x < limit ? x : nil }
NoMethodError: undefined method `max_by' for Array:Class
        from (irb):16
        from /usr/bin/irb:11:in `<main>'

Outlook habitually capitalises the first character in every line. I missed one. You know very well from the previous context we were talking about a parameter name, not a class.

irb(main):015:0> limit = 10; [-1, 20, -2].max_by{|x| x < limit ? x : nil }
ArgumentError: comparison of NilClass with -1 failed
        from (irb):15:in `each'
        from (irb):15:in `max_by'
        from (irb):15
        from /usr/bin/irb:11:in `<main>'

    [1] pry(main)> limit = 10; [-1, -20, -2].max_by{|x| x < limit ? x : nil }
    => -1
    [2] pry(main)> limit = 10; [-1, 20, -2].max_by{|x| x < 10 ? x : -9999999 }
    => -1

See my previous answer about my example code not working against all test cases?

Please try not to act like an idiot. This is not a competition where you can win points by trying to prove other people are wrong, and I'm not interested in having an argument with you.

You found an interesting edge case in max_by that I was unaware of. What a shame we can't have a pleasant discussion about that now...

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer&gt;

irb(main):015:0> limit = 10; [-1, 20, -2].max_by{|x| x < limit ? x : nil }
ArgumentError: comparison of NilClass with -1 failed
        from (irb):15:in `each'
        from (irb):15:in `max_by'
        from (irb):15
        from /usr/bin/irb:11:in `<main>'

    [1] pry(main)> limit = 10; [-1, -20, -2].max_by{|x| x < limit ? x : nil }
    => -1

This will fail for the quite common case that there is at least one
value larger or equal the limit among other values.

    [2] pry(main)> limit = 10; [-1, 20, -2].max_by{|x| x < 10 ? x : -9999999 }
    => -1

This will fail for arrays where all values are above the limit.

See my previous answer about my example code not working against all test cases?

It is our task as programmers to recognize reasonable test cases and
code accordingly. I found these cases quite obvious and in no way
arcane. A solution that does not cover common or fairly common cases
should not be propagated IMO.

Enumerable#max_by is just not suited to this kind of problem, because
it requires at the same time to translate invalid values (i.e. above
the limit) to special values to avoid including them in the
calculation of the maximum (ideally of other type, e.g. nil) AND to
translate all inputs to values which can be compared - typically this
is the case for uniform type translation. These are contradicting
requirements and that shows in the test inputs I presented.

If you want to use a max method then you have to reduce to the valid set, e.g.

[-1, 20, -2].select {|x| x < 10}.max

This is quite elegant although it requires two passes through the data
set. For small inputs that most likely does not make a difference. If
inputs can be really large it probably does.

Please try not to act like an idiot.

I am sorry, I was a jerk when pointing at Array.max_by. I'll try
improving, but I cannot give any guarantees.

This is not a competition where you can win points by trying to prove other people are wrong, and I'm not interested in having an argument with you.

The point is to discuss weaknesses of solutions for the learning
benefit of other readers.

You found an interesting edge case in max_by that I was unaware of. What a shame we can't have a pleasant discussion about that now...

Well, if you say so.

Regards

robert

···

On Mon, Dec 5, 2016 at 4:22 PM, Andy Jones <Andy.Jones@jameshall.co.uk> wrote:

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/