Macrumpel
Macrumpel

Reputation: 1

Find nearest lower number in string array (ruby)

New to ruby. I have an array created by nokogiri like this :

Array = ["10:31 Main title", ...]

It is a schedule in the format hour:minute title. Now I have a time, say 10:35 and I want to find the entry in the array with the nearest lower number (time and title). It is like what is playing now?

How can I do this in ruby? I am at a blank here...

Thank you

Upvotes: 0

Views: 1276

Answers (4)

ala
ala

Reputation: 96

You can achieve this using bsearch like below

a = [1, 4, 8, 11, 97]

a.bsearch {|x| x >= 7} # which results 8

Upvotes: 4

mikezter
mikezter

Reputation: 2463

Since your array contains strings starting with numbers, these can be nicely sorted naturally.

my_array.sort.reverse.find{ |i| i < "10:35" }

This will sort your array in ascending order, then reverse it, and finally return the first item for which the block returns true.

If you are at Ruby version > 2.0, you can also use Array#bsearch:

my_array.sort.bsearch{ |i| i < "10:35" }

This will sort your array and will then use a binary search approach to finding the desired item (Thanks @ala for pointing this out).

These simple lines of code expect the time to be in 24h format with leading zeros (i.e. hh:mm), since it depends on comparing the lines lexicographically.

Upvotes: 0

SoAwesomeMan
SoAwesomeMan

Reputation: 3396

require 'date'
a1 = ["10:31 The Beastmaster", "10:36 C.H.U.D.", "11:30 Goonies", "11:30 Krull", "11:59 Batteries Not Included"]
#=> ["10:31 The Beastmaster", "10:36 C.H.U.D.", "11:30 Goonies", "11:30 Krull", "11:59 Batteries Not Included"]
h1 = {}; a1.each {|x| m = x.match(/(\d{1,2}:\d{2})\s+(\w.*)/); h1[m[1]] ||= []; h1[m[1]] << m[2]}; h1 # => hash with times as keys and array of titles as corresponding values
#=> {"10:31"=>["The Beastmaster"], "10:36"=>["C.H.U.D."], "11:30"=>["Goonies", "Krull"], "11:59"=>["Batteries Not Included"]}
t1 = DateTime.rfc3339('2014-02-03T10:35:00-08:00').to_time.to_i
#=> 1391452500
within_an_hour = 60 * 60
#=> 3600
t2 = t1 + within_an_hour
#=> 1391456100
a2 = h1.keys.partition {|x| x > Time.at(t1).strftime("%I:%M")}[0] # => all upcoming times
#=> ["10:36", "11:30", "11:59"]
h2 = {}; a2.each {|x| h2[x] = h1[x]}; h2 # => all upcoming show times with corresponding titles
#=> {"10:36"=>["C.H.U.D."], "11:30"=>["Goonies", "Krull"], "11:59"=>["Batteries Not Included"]}
a3 = a2.partition {|x| x < Time.at(t2).strftime("%I:%M")}[0] # => upcoming times within an hour
#=> ["10:36", "11:30"]
h3 = {}; a3.each {|x| h3[x] = h1[x]}; h3 # => upcoming show times with corresponding titles within an hour
#=> {"10:36"=>["C.H.U.D."], "11:30"=>["Goonies", "Krull"]}

using the above code in a method:

require 'date'
def what_is_playing_now(time, a1=["10:31 The Beastmaster", "10:36 C.H.U.D.", "11:30 Goonies", "11:30 Krull", "11:59 Batteries Not Included"])
  h1 = {}; a1.each {|x| m = x.match(/(\d{1,2}:\d{2})\s+(\w.*)/); h1[m[1]] ||= []; h1[m[1]] << m[2]}; h1 # => hash with times as keys and array of titles as corresponding values
  t1 = DateTime.rfc3339("2014-02-03T#{time}:00-08:00").to_time.to_i
  a2 = h1.keys.partition {|x| x > Time.at(t1).strftime("%I:%M")}[0] # => all upcoming times
  h2 = {}; a2.each {|x| h2[x] = h1[x]}; h2 # => all upcoming show times with corresponding titles
  "#{a2.first} #{h2[a2.first].sort.first}"
end
what_is_playing_now("10:35")
#=> "10:36 C.H.U.D."

sources:

Upvotes: 0

Cody Caughlan
Cody Caughlan

Reputation: 32748

You're going to have to walk the array and parse each entry. You'll also have to take into consideration whether the times are 12-hour or 24-hour, e.g. "10:31 Main Title" does that mean 10:31 AM or PM (in 12 hour clock). If its a 24-hour clock then 10:31 is 10:31 [am] and you'll also have 22:31 to reflect 10:31 [pm].

So you could walk the array, parsing each entry and then building a new structure which you can sort by. Ultimately you can get the lowest value and then just find the index of that entry in the original array.

Upvotes: 1

Related Questions