2

I have this hash

FORMATS = {
    'vertical_4'   => %w[vertical small small small small],
    'horizontal_4' => %w[horizontal small small small small],
    'horizontal_2' => %w[large small small],
    'mixed_3'      => %w[vertical horizontal small small],
    'huge'         => %w[horizontal small small horizontal small small small]
  }

I need the max value, so I use

MAX_ELEMENTS   = FORMATS.map {|_,v| v}.max.size

Why it returns 5 instead of 7?

1
  • 2
    You asked 'why' is it 5 and not 7, but no one really answered you on that so I provided some analysis with what's going on. Commented Mar 22, 2018 at 14:13

4 Answers 4

6

The array with the greatest number of elements

FORMATS.values.max_by(&:count)
Sign up to request clarification or add additional context in comments.

3 Comments

Shortest and best answer nice.
Or FORMATS.max_by { |_, v| v.size } if you are also interested in the key.
Yep, I thought about add that :)
3

You asked why, so I'm going to break this down step by step, first we know map returns an array of values (same as FORMATS.values)

FORMATS.map {|_,v| v}
=> [
 ["vertical", "small", "small", "small", "small"],
 ["horizontal", "small", "small", "small", "small"],
 ["large", "small", "small"],
 ["vertical", "horizontal", "small", "small"],
 ["horizontal", "small", "small", "horizontal", "small", "small", "small"]
]

We then call Array#max without any arguments or a block which according to the docs:

Returns the object in ary with the maximum value. The first form assumes all objects implement Comparable;

In your case, each element is an array and since array does respond to the comparable operator <=>

it runs each element against each other like this:

FORMATS.map {|_,v| v}.max { |a, b| a <=> b }

However Array#<=> does it's own comparison element by element:

Arrays are compared in an “element-wise” manner; the first element of ary is compared with the first one of other_ary using the <=> operator, then each of the second elements, etc… As soon as the result of any such comparison is non zero (i.e. the two corresponding elements are not equal), that result is returned for the whole array comparison.

However when we call max we get back the first item in the array, why?

FORMATS.map {|_,v| v}.max
=> ["vertical", "small", "small", "small", "small"]

That's because if we look at this element-by-element, the first value is "vertical" and "vertical" is >= every first element for the other arrays which then leaves us only with the first element:

 ["vertical", "small", "small", "small", "small"]

and the 4th element

 ["vertical", "horizontal", "small", "small"]

If we then compare the second elements, small is > horizontal and we're done, the first element is the max.

You then call size on the max element which is 5.

Comments

2

You can make use of max iterator with a block

FORMATS.values.max { |values| values.length }.size

Comments

2

If you want the max of the size of the arrays you need:

FORMATS.map {|_,v| v}.map(&:size).max

Map the value of the arrays to their size and select max of that array.

UPDATE better answer no need for 2nd map:

FORMATS.map{|_,v| v.size}.max

To explain why your code gave you the result it did, .max uses logic to get the max on enumerable. So if it compares array of strings it will look for the max by alphabetic sort.

[['aaa','bbb'],['ddd','ccc'], ['bbb','ccc']].max #=> returns ["ddd", "ccc"]

Your code was mapping the values to array, calling .max on that, which would give the highs max by alpha sort, then calling .size on that, just gives size of array.

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.