Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby get nth item from massive range

Suppose I have this range:

("aaaaa".."zzzzz")

How would I get the Nth item from the range without generating the entire thing before hand/each time?

like image 545
Bub Bradlee Avatar asked Apr 28 '10 19:04

Bub Bradlee


2 Answers

A quick and easy way:

("aaaaa".."zzzzz").first(42).last  # ==> "aaabp"

If for some reason you have to do this over and over, or if you need to avoid building the intermediate array for the first N elements, you could write something like:

module Enumerable
  def skip(n)
    return to_enum :skip, n unless block_given?
    each_with_index do |item, index|
      yield item unless index < n
    end
    self
  end
end

("aaaaa".."zzzzz").skip(41).first # ==> "aaabp"

Note: I'm assuming you want a solution that works for any Enumerable, not for range of letters (in which case you should calculate it directly). I'm also assuming Ruby 1.8.7+, otherwise upgrade or require "backports"

like image 71
Marc-André Lafortune Avatar answered Nov 07 '22 18:11

Marc-André Lafortune


Enumerate only up to n,

or

Develop a function that given a number n, f(n) gives you the nth item of your range of possible solutions.

In your case you may treat your range as a number system with base 26. Rebasing a number is a well known problem. There's an example on my site to go from a base-10 number to a base-26 number (represented by the alphabet) even in ruby (made by a colleague of mine). Some variation of this algorithm would probably also work for you.

Update Maybe it didn't sink in that this is your answer :D

Here's the ruby code to get the nth item of your range:

def rbase(value)
  a = ('a'..'z')
  b = a.to_a
  base = b.length
  text = []
  begin 
    value, rest = value.divmod(base)
    text << b[rest]
  end until value.zero?
  text.reverse.join
end

then you can use it like that.

irb(main):030:0> rbase(789).rjust(10,'a')
=> "aaaaaaabej"
like image 20
flq Avatar answered Nov 07 '22 18:11

flq