Returning all maximum or minimum values that can be multiple
Asked Answered
M

3

5

Enumerable#max_by and Enumerable#min_by return one of the relevant elements (presumably the first one) when there are multiple max/min elements in the receiver. For example, the following:

[1, 2, 3, 5].max_by{|e| e % 3}

returns only 2 (or only 5).

Instead, I want to return all max/min elements and in an array. In the example above, it would be [2, 5] (or [5, 2]). What is the best way to get this?

Masurium answered 1/3, 2014 at 15:0 Comment(0)
W
10
arr = [1, 2, 3, 5]

arr.group_by{|a| a % 3} # => {1=>[1], 2=>[2, 5], 0=>[3]}
arr.group_by{|a| a % 3}.max.last # => [2, 5]
Whiles answered 1/3, 2014 at 15:28 Comment(8)
Your use of group_by is nice, but I think you can improve a step further by replacing sort with max_by on the keys.Masurium
Or just arr.group_by{|a| a % 3}.max.lastNicolis
@Nicolis That is shorter, but from efficiency, isn't it better to use Schwarzian transform? (I may be wrong. Not sure how it works without a block)Masurium
@Masurium you're right. fruity: max_by is faster than max by 19.999999999999996% ± 1.0% max is faster than sort by 19.999999999999996% ± 1.0%Nicolis
I ended up doing arr.group_by{|a| a % 3}.max_by(&:first).last, which is the fastest, and is reasonably simple. Thanks to Sergio and steenslag.Masurium
@sawa: I realized just now that you are the OP :)Whiles
@SergioTulentsev if you do not mind then may I ask you what does OP stand for? -- ThanksStorax
@AlokAnand: original posterWhiles
S
0
arr=[1, 2, 3, 5, 7, 8]
mods=arr.map{|e| e%3}

find max

max=mods.max
indices = []
mods.each.with_index{|m, i| indices << i if m.eql?(max)}
arr.select.with_index{|a,i| indices.include?(i)}

find min

min = mods.min
indices = []
mods.each.with_index{|m, i| indices << i if m.eql?(min)}
arr.select.with_index{|a,i| indices.include?(i)}

Sorry for clumsy code, will try to make it short.

Answer by @Sergio Tulentsev is the best and efficient answer, found things to learn there. +1

Storax answered 1/3, 2014 at 15:33 Comment(0)
N
0

This is the hash equivalent of @Serio's use of group_by.

arr = [1, 2, 3, 5]

arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |e,h| h[e%3] << e }.max.last
  #=> [2, 5]

The steps:

h = arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |e,h| h[e%3] << e }
  #=> {1=>[1], 2=>[2, 5], 0=>[3]}
a = h.max
  #=> [2, [2, 5]]
a.last
  #=> [2, 5]
Necrolatry answered 26/4, 2017 at 21:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.