0

I have this hash in Ruby:

hash = {
   0..25  => { low_battery_count:     13 }, 
  26..75  => { average_battery_count:  4 }, 
  76..100 => { good_battery_count:     4 }
}

Now, what is want is a method (preferably a built-in one) in which if I pass a value (integer e.g. 20, 30, etc), it should return me the value against the range in which this integer lies.

I have already solved it using each method but now, I want to optimize it, even more, to do it without each or map methods to reduce its complexity.

For Example:

method(3) #=> {:low_battery_count=>13}
method(35) #=> {:average_battery_count=>4}
method(90) #=> {:good_battery_count=>4}
mechnicov
  • 12,025
  • 4
  • 33
  • 56
  • 1
    There is no other way than to check if the input is covered by any of the ranges that you use as keys. If you posted your current code then we might be able to optimize it a bit but, in general, you will need to iterate over the keys in some way. Or you might want to consider a different data structure. – spickermann Aug 30 '22 at 11:02
  • Thanks! @spickermann I did it with iteration. I was just wondering if there exists some solution to do the same thing without iteration but it guess there isn't any so let it be the iterations. – Mohsin Nazakat Aug 30 '22 at 11:34
  • Why do you have range keys in the first place? What do they represent? – Stefan Aug 30 '22 at 12:01
  • the ranges represent the battery levels, for example, 0..25 lies in low and the same for the other ones! as for the hash in the value it contains the count that how many times a battery level fell in those ranges. – Mohsin Nazakat Aug 30 '22 at 13:33
  • 1
    Why not use a simple counting hash with keys from 0 to 100. To get the low values, you just run `hash.values_at(*0..25).sum`. This makes it trivial to collect the values (`hash[level] += 1`) and it even allows to adjust the thresholds afterwards. – Stefan Aug 30 '22 at 16:26
  • it's another cool approach of solving above problem. thanks @Stefan – Mohsin Nazakat Aug 30 '22 at 18:28

3 Answers3

1

You need to get exact key in order to fetch a value from the hash. So either way a passed number should be tested against the available keys (ranges).

You might consider introducing an optimisation based on assigning pre-determined ranges to the constants and using a case equality check:

SMALL = 0..25
MEDUIM = 26..75
LARGE = 76..100


def method(n)
  case n
  when SMALL then data[SMALL]
  when MEDUIM then data[MEDUIM]
  when LARGE then data[LARGE]
  end
end

def data
  @data ||= {
    SMALL => { low_battery_count: 13 },
    MEDUIM => { average_battery_count: 4 },
    LARGE =>{ good_battery_count: 4 }
  }
end

method(25)
=> {:low_battery_count=>13}
method(44)
=> {:average_battery_count=>4}
method(76)
=> {:good_battery_count=>4}
bernstein7
  • 111
  • 5
1

We can do this using some of built in methods but there is not any direct way to do this

class Hash
  def value_at_key_range(range_member)
    keys.select{ |range| range.include?(range_member) }.map{ |key| self[key]}
  end
end

hash = {0..25=>{:low_battery_count=>13}, 26..75=>{:average_battery_count=>4}, 76..100=>{:good_battery_count=>4}}

p hash.value_at_key_range(10)

output

[{:low_battery_count=>13}]
Ritesh Choudhary
  • 772
  • 1
  • 4
  • 12
1

You can use find for this hash

BATTERIES = {
  0..25  => { low_battery_count:     13 }, 
  26..75  => { average_battery_count:  4 }, 
  76..100 => { good_battery_count:     4 }
}

def get_batteries_count(value)
  BATTERIES.find { |range, _| range.include?(value) }&.last
end
get_batteries_count(3) #=> {:low_battery_count=>13}
get_batteries_count(35) #=> {:average_battery_count=>4}
get_batteries_count(90) #=> {:good_battery_count=>4}
get_batteries_count(2000) #=> nil
mechnicov
  • 12,025
  • 4
  • 33
  • 56