How to break down create a substring method - ruby

Can someone help me break down this problem? I have the answer but I'm not really sure how my teacher got to it.
class String
# Write a method, String#substrings, that takes in a optional length argument
# The method should return an array of the substrings that have the given length.
# If no length is given, return all substrings.
#
# Examples:
#
# "cats".substrings # => ["c", "ca", "cat", "cats", "a", "at", "ats", "t", "ts", "s"]
# "cats".substrings(2) # => ["ca", "at", "ts"]
def substrings(length = nil)
subs = []
(0...self.length).each do |start_idx|
(start_idx...self.length) do |end_idx|
sub = self[start_idx..end_idx]
subs << sub
end
end
if length.nil?
subs
else
subs.select { |str| str.length == length}
end
end
end
The start_idx and the end_idx are really confusing, if the start_idx is "ca" for example is the end_idx "ca" as well? Please help..

So think of start_idx and end_idx as a constantly changing variable.
def substrings(length = nil)
subs = []
# in the case of 'cats' the length is 4
# so this is making an array UP TO BUT NOT INCLUDING 4
# [0,1,2,3].each do ...
# let's take 0
(0...self.length).each do |start_idx|
# start_idx = 0 the first time through this each
# Array from start_idx UP TO BUT NOT INCLUDING 4
# so the FIRST time through this is 0, second time through is 1, ...
#[0,1,2,3].each do ...
(start_idx...self.length) do |end_idx|
# end_idx = 0
# slice of the string from the 0th to the 0th value (first letter)
sub = self[start_idx..end_idx]
subs << sub
end
end
if length.nil?
subs
else
subs.select { |str| str.length == length}
end
end
So think of this as a bunch of nested loops using numbers that are reassigned during each pass of the loop.
Does that help?

The following would be a more Ruby-like way of writing that.
class String
def all_substrings
(1..size).flat_map { |n| all_substrings_by_length(n) }
end
def all_substrings_by_length(length)
each_char.each_cons(length).with_object([]) { |a,arr| arr << a.join }
end
end
"cats".all_substrings_by_length(1)
#=> ["c", "a", "t", "s"]
"cats".all_substrings_by_length(2)
#=> ["ca", "at", "ts"]
"cats".all_substrings_by_length(3)
#=> ["cat", "ats"]
"cats".all_substrings
#=> ["c", "a", "t", "s", "ca", "at", "ts", "cat", "ats", "cats"]
Note that 1..size is the same as 1..self.size, all_substrings_by_length(n) is the same as self.all_substrings_by_length(n) and each_char is the same as self.each_char, as self is implied when a method has no explicit receiver.
See Enumerable#flat_map, String#each_char, Enumerable#each_cons and Emumerator#with_object.
Let's break down
each_char.each_cons(length).with_object([]) { |a,arr| arr << a.join }
when length = 2 and self = "cats".
length = 2
e0 = "cats".each_char
#=> #<Enumerator: "cats":each_char>
We can see the elements that will be generated by this enumerator by converting it to an array.
e0.to_a
#=> ["c", "a", "t", "s"]
Continuing,
e1 = e0.each_cons(length)
#=> #<Enumerator: #<Enumerator: "cats":each_char>:each_cons(2)>
e1.to_a
#=> [["c", "a"], ["a", "t"], ["t", "s"]]
e2 = e1.with_object([])
#=> #<Enumerator: #<Enumerator: #<Enumerator:
# "cats":each_char>:each_cons(2)>:with_object([])>
e2.to_a
#=> [[["c", "a"], []], [["a", "t"], []], [["t", "s"], []]]
By examining the return values for the creation of e1 and e2 one can see that they could be thought of as compound enumerators, though Ruby has no formal concept of such. Also, as will be seen, the empty arrays in the last return value will be built up as the calculations progress.
Lastly,
e2.each { |a,arr| arr << a.join }
#=> ["ca", "at", "ts"]
which is our desired result. Now examine this last calculation in more detail. each directs e2 to generate an element and then sets the block variables equal to it.
First, observe the following.
e2
#=> #<Enumerator: #<Enumerator: #<Enumerator: "cats":each_char>:
# each_cons(2)>:=with_object(["ca", "at", "ts"])>
This shows us that we need to return e2 to its initial state in order to reproduce the calculations.
e2 = e1.with_object([])
#=> #<Enumerator: #<Enumerator: #<Enumerator:
# "cats":each_char>:each_cons(2)>:with_object([])>
Then:
a, arr = e2.next
#=> [["c", "a"], []]
Array decomposition breaks this array into parts for a and arr:
a #=> ["c", "a"]
arr
#=> []
We now perform the block calculation:
arr << a.join
#=> ["ca"]
each then commands e2 to generate the next element, assigns values to the block variables and performs the block calculation.
a, arr = e2.next
#=> [["a", "t"], ["ca"]]
a #=> ["a", "t"]
arr
#=> ["ca"]
arr << a.join
#=> ["ca", "at"]
This is repeated once more.
a, arr = e2.next
#=> [["t", "s"], ["ca", "at"]]
arr << a.join
#=> ["ca", "at", "ts"]
Lastly, the exception
a, arr = e2.next
#=> StopIteration (iteration reached an end)
causes each to return
arr
#=> ["ca", "at", "ts"]
from the block, which, being the last calculation, is returned by the method.

Related

Compressing a given String problem in Ruby

I have been trying to solve this problem for a while now, im supposed to take a given string like "aaabbc" and compress it into a new string that states multiples of a letter in a row in place. So it would output "3a2bc"
So far i managed to print it out except it counts all instances of a letter and im not sure how to get rid of the current repeats:
def compress_str(str)
new_str = []
word = str.split("")
word.each do |char|
count = 0
word.each do |ele|
if ele == char
count += 1
end
end
if count > 1
new_str << count
new_str << char
else
new_str << char
end
end
return new_str.join("")
Example output:
Any suggestions on how I'm supposed to get rid of them?
Using Enumerable#chunk might be a good fit for your needs.
uncompressed = %w(aaabbcddaaaaa aaabb 111ddttttabaaacc)
uncompressed.each do |str|
puts str.chars.chunk{|e| e}.map {|e| "#{e[1].length}#{e[0]}"}.join
end
>>> 3a2b1c2d5a
>>> 3a2b
>>> 312d4t1a1b3a2c
Sure, you can add another check inside map block, so omit 1 before a single element and print as is.
You could use String#chars (1), so Enumerable#chunk_while (2), then Enumerable#flat_map (3) into the desired format and finally Array#join:
str = "aaabbcaa"
str.chars.chunk_while { |x, y| x == y }.flat_map { |e| [(e.size unless e.size == 1), e.first] }.join
#=> "3a2bc2a"
Step by step
# (1)
str.chars#.to_a
#=> ["a", "a", "a", "b", "b", "c", "a", "a"]
so
# (2)
str.chars.chunk_while { |x, y| x == y }#.to_a
#=> [["a", "a", "a"], ["b", "b"], ["c"], ["a", "a"]]
then
# (3)
str.chars.chunk_while { |x, y| x == y }.flat_map { |e| [(e.size unless e.size == 1),e.first] }
#=> [3, "a", 2, "b", nil, "c", 2, "a"]
String#scan can also be handy here.
uncompressed = %w(aaabbcddaaaaa aaabb 111ddttttabaaacc)
uncompressed.map { |w| w.scan(/(.)(\1*)/).map(&:join) }
#⇒ [["aaa", "bb", "c", "dd", "aaaaa"],
# ["aaa", "bb"],
# ["111", "dd", "tttt", "a", "b", "aaa", "cc"]]
And to get the desired outcome.
uncompressed.map do |w|
w.scan(/(.)(\1*)/).map(&:join).map do |l|
"#{l.length}#{l[0]}"
end.join
end
#⇒ ["3a2b1c2d5a", "3a2b", "312d4t1a1b3a2c"]

Print elements of array of arrays of different size in same line in Ruby

Maybe someone could help me with this. I have an array of arrays. The internal arrays have different sizes (from 2 to 4 elements).
letters = [["A", "B"],["C", "D", "F", "G"],["H", "I", "J" ]]
I'm trying to print in a same line each array havins as first column element[0] and element[1] joined, as 2nd column element[0], element[1], element[2] joined as 3rd column element[0], element[1], element[3] joined. Elements 2 and 3 not always exist.
The output I'm trying to get is like this:
AB
CD CDF CDG
HI HIJ
I'm doing in this way but I'm getting this error.
letters.map{|x| puts x[0]+x[1] + "," + x[0]+x[1]+x[2] + "," + x[0]+x[1]+x[3]}
TypeError: no implicit conversion of nil into String
from (irb):1915:in "+"
from (irb):1915:in "block in irb_binding"
from (irb):1915:in "map"
from (irb):1915
from /usr/bin/irb:11:in "<main>"
letters.each do |a,b,*rest|
puts rest.each_with_object([a+b]) { |s,arr| arr << arr.first + s }.join(' ')
end
prints
AB
CD CDF CDG
HI HIJ
The steps are as follows.
Suppose
letters = [["C", "D", "F", "G"],["H", "I", "J" ]]
Then
enum0 = letters.each
#=> #<Enumerator: [["C", "D", "F", "G"], ["H", "I", "J"]]:each>
The first element of this enumerator is generated and passed to the block, and the three block variables are assigned values.
a, b, *rest = enum0.next
#=> ["C", "D", "F", "G"]
a
#=> "C"
b
#=> "D"
rest
#=> ["F", "G"]
Next, we obtain
enum1 = rest.each_with_object([a+b])
#=> rest.each_with_object(["CD"])
#=> #<Enumerator: ["F", "G"]:each_with_object(["CD"])>
The first element of this enumerator is generated and passed to the block, and the block variables are assigned values.
s, arr = enum1.next
#=> ["F", ["CD"]]
s
#=> "F"
arr
#=> ["CD"]
The block calculation is now performed.
arr << arr.first + s
#=> arr << "CD" + "F"
#=> ["CD", "CDF"]
The second and last element of enum1 is generated and passed to the block, and block variables are assigned values and the block is computed.
s, arr = enum1.next
#=> ["G", ["CD", "CDF"]]
arr << arr.first + s
#=> ["CD", "CDF", "CDG"]
When an attempt to generate another element from enum1 we obtain
enum1.next
#StopIteration: iteration reached an end
Ruby handles the exception by breaking out of the block and returning arr. The elements of arr are then joined:
arr.join(' ')
#=> "CD CDF CDG"
and printed.
The second and last element of enum0 is now generated, passed to the block, and the three block variables are assigned values.
a, b, *rest = enum0.next
#=> ["H", "I", "J"]
a
#=> "H"
b
#=> "I"
rest
#=> ["J"]
The remaining calculations are similar.
Some readers may be unfamiliar with the method Enumerable#each_with_object, which is widely used. Read the doc, but note that here it yields the same result as the code written as follows.
letters.each do |a,b,*rest|
arr = [a+b]
rest.each { |s| arr << arr.first + s }
puts arr.join(' ')
end
By using each_with_object we avoid the need for the statement arr = [a+b] and the statement puts arr.join(' '). The functions of those two statements are of course there in the line using each_with_object, but most Ruby users prefer the flow when when chaining each_with_object to join(' '). One other difference is that the value of arr is confined to each_with_object's block, which is good programming practice.
Looks like you want to join the first two letters, then take the cartesian product with the remaining.
letters.each do |arr|
first = arr.take(2).join
rest = arr.drop(2)
puts [first, [first].product(rest).map(&:join)].join(" ")
end
This provides the exact output you specified.
Just out of curiosity, Enumerable#map-only solution.
letters = [["A", "B"],["C", "D", "F", "G"],["H", "I", "J" ]]
letters.map do |f, s, *rest|
rest.unshift(nil).map { |l| [f, s, l].join }.join(' ')
end.each(&method(:puts))
#⇒ AB
# CD CDF CDG
# HI HIJ

Ruby: how to count the frequencies of the letters

HELP: do a frequency method: calculate the frequency each letter appears in “str” and assign the output to “letters”. The method should count lower and uppercase as the same letter. but I have the error about NoMethodError: undefined method
Thank you
here is my code
class MyString
attr_accessor :str
attr_reader :letters
def initialize
#str = "Hello World!"
#letters = Hash.new()
end
def frequency
#letters = #str.split(" ").reduce(#letters) { |h, c| h[c] += 1; h}
end
def histogram
##letters.each{|key,value| puts "#{key} + ':' + value.to_s + '*' * #{value}" }
end
end
The error shows :
irb(main):009:0> txt1.frequency
NoMethodError: undefined method `+' for nil:NilClass
from assignment.rb:11:in `block in frequency'
from assignment.rb:11:in `each'
from assignment.rb:11:in `reduce'
from assignment.rb:11:in `frequency'
from (irb):9
from /usr/bin/irb:12:in `<main>'
When you try to add 1 to a value in the hash that doesn't exist, it tries to add 1 to nil, which isn't allowed. You can change the hash so that the default value 0, not nil.
#letters = Hash.new(0)
Now, your program right now is counting word frequencies, not letter frequencies (split(" ") splits on spaces, not on each character). To split on each character, use the appropriately named each_char method.
#letters = #str.each_char.reduce(#letters) { |h, c| h[c] += 1; h}
Whenever one uses a counting hash (#Silvio's answer) one can instead use Enumerable#group_by, which is what I've done here.
str = "It was the best of times, it was the worst of times"
str.gsub(/[[:punct:]\s]/, '').
downcase.
each_char.
group_by(&:itself).
each_with_object({}) { |(k,v),h| h[k] = v.size }
#=> {"i"=>4, "t"=>8, "w"=>3, "a"=>2, "s"=>6, "h"=>2, "e"=>5,
# "b"=>1, "o"=>3, "f"=>2, "m"=>2, "r"=>1}
The steps are as follows.
a = str.gsub(/[[:punct:]\s]/, '')
#=> "Itwasthebestoftimesitwastheworstoftimes"
b = a.downcase
#=> "itwasthebestoftimesitwastheworstoftimes"
e = b.each_char
#=> #<Enumerator: "itwasthebestoftimesitwastheworstoftimes":each_char>
f = e.group_by(&:itself)
#=> {"i"=>["i", "i", "i", "i"],
# "t"=>["t", "t", "t", "t", "t", "t", "t", "t"],
# ...
# "r"=>["r"]}
f.each_with_object({}) { |(k,v),h| h[k] = v.size }
#=> < return value shown above >
Let's look more closely at the last step. The first key-value pair of the hash f is passed to the block as a two-element array, together with the initial value of the hash h:
(k,v), h = [["i", ["i", "i", "i", "i"]], {}]
#=> [["i", ["i", "i", "i", "i"]], {}]
Applying the rules of disambiguation (or decomposition), we obtain the following.
k #=> "i"
v #=> ["i", "i", "i", "i"]
h #=> {}
The block calculation is performed:
h[k] = v.size
#=> h["i"] = 4
So now
h => { "i"=>4 }
The next key-value pair is passed to the block, along with the current value of h:
(k,v), h = [["t", ["t", "t", "t", "t", "t", "t", "t", "t"]], { "i"=>4 }]
#=> [["t", ["t", "t", "t", "t", "t", "t", "t", "t"]], {"i"=>4}]
k #=> "t"
v #=> ["t", "t", "t", "t", "t", "t", "t", "t"]
h #=> {"i"=>4}
h[k] = v.size
#=> 8
So now
h #=> {"i"=>4, "t"=>8}
The remaining calculations are similar.
The method Enumerable#tally, which made its debut in Ruby v2.7, is purpose-built for this task:
str.gsub(/[[:punct:]\s]/, '').downcase.each_char.tally
#=> {"i"=>4, "t"=>8, "w"=>3, "a"=>2, "s"=>6, "h"=>2,
# "e"=>5, "b"=>1, "o"=>3, "f"=>2, "m"=>2, "r"=>1}

Ruby: How to create a letter counting function

I wouldn't have asked for help without first spending a few hours trying to figure out my error but I'm at a wall. So there is my attempt but I'm getting false when I try to pass the argument though and I'm not sure why. I know there are other ways to solve this problem that are a little shorter but I'm more interested in trying to get my code to work. Any help is much appreciated.
Write a method that takes in a string. Your method should return the most common letter in the array, and a count of how many times it appears.
def most_common_letter(string)
idx1 = 0
idx2 = 0
counter1 = 0
counter2 = 0
while idx1 < string.length
idx2 = 0
while idx2 < string.length
if string[idx1] == string[idx2]
counter1 += 1
end
idx2 += 1
end
if counter1 > counter2
counter2 = counter1
counter1 = 0
var = [string[idx1], counter2]
end
idx1 += 1
end
return var
end
puts("most_common_letter(\"abca\") == [\"a\", 2]: #{most_common_letter("abca") == ["a", 2]}")
puts("most_common_letter(\"abbab\") == [\"b\", 3]: #{most_common_letter("abbab") == ["b", 3]}")
I didn't rewrite your code because I think it's important to point out what is wrong with the existing code that you wrote (especially since you're familiar with it). That said, there are much more 'ruby-like' ways to go about this.
The issue
counter1 is only being reset if you've found a 'new highest'. You need to reset it regardless of whether or not a new highest number has been found:
def most_common_letter(string)
idx1 = 0
idx2 = 0
counter1 = 0
counter2 = 0
while idx1 < string.length
idx2 = 0
while idx2 < string.length
if string[idx1] == string[idx2]
counter1 += 1
end
idx2 += 1
end
if counter1 > counter2
counter2 = counter1
# counter1 = 0 THIS IS THE ISSUE
var = [string[idx1], counter2]
end
counter1 = 0 # this is what needs to be reset each time
idx1 += 1
end
return var
end
Here's what the output is:
stackoverflow master % ruby letter-count.rb
most_common_letter("abca") == ["a", 2]: true
most_common_letter("abbab") == ["b", 3]: true
I think you're aware there are way better ways to do this but frankly the best way to debug this is with a piece of paper. "Ok counter1 is now 1, indx2 is back to zero", etc. That will help you keep track.
Another bit of advice, counter1 and counter2 are not very good variable names. I didn't realize what you were using them for initially and that should never be the case, it should be named something like current_count highest_known_count or something like that.
Your question has been answered and #theTinMan has suggested a more Ruby-like way of doing what you want to do. There are many other ways of doing this and you might find it useful to consider a couple more.
Let's use the string:
string = "Three blind mice. Oh! See how they run."
First, you need to answer a couple of questions:
do you want the frequency of letters or characters?
do you want the frequency of lowercase and uppercase letters combined?
I assume you want the frequency of letters only, independent of case.
#1 Count each unique letter
We can deal with the case issue by converting all the letters to lower or upper case, using the method String#upcase or String#downcase:
s1 = string.downcase
#=> "three blind mice. oh! see how they run."
Next we need to get rid of all the characters that are not letters. For that, we can use String#delete1:
s2 = s1.delete('^a-z')
#=> "threeblindmiceohseehowtheyrun"
Now we are ready to convert the string s2 to an an array of individual characters2:
arr = s2.chars
#=> ["t", "h", "r", "e", "e", "b", "l", "i", "n", "d",
# "m", "i", "c", "e", "o", "h", "s", "e", "e", "h",
# "o", "w", "t", "h", "e", "y", "r", "u", "n"]
We can combine these first three steps as follows:
arr = string.downcase.gsub(/[^a-z]/, '').chars
First obtain all the distinct letters present, using Array.uniq.
arr1 = arr.uniq
#=> ["t", "h", "r", "e", "b", "l", "i", "n",
# "d", "m", "c", "o", "s", "w", "y", "u"]
Now convert each of these characters to a two-character array consisting of the letter and its count in arr. Whenever you need convert elements of a collection to something else, think Enumerable#map (a.k.a. collect). The counting is done with Array#count. We have:
arr2 = arr1.map { |c| [c, arr.count(c)] }
#=> [["t", 2], ["h", 4], ["r", 2], ["e", 6], ["b", 1], ["l", 1],
# ["i", 2], ["n", 2], ["d", 1], ["m", 1], ["c", 1], ["o", 2],
# ["s", 1], ["w", 1], ["y", 1], ["u", 1]]
Lastly, we use Enumerable#max_by to extract the element of arr2 with the largest count3:
arr2.max_by(&:last)
#=> ["e", 6]
We can combine the calculation of arr1 and arr2:
arr.uniq.map { |c| [c, arr.count(c)] }.max_by(&:last)
and further replace arr with that obtained earlier:
string.downcase.gsub(/[^a-z]/, '').chars.uniq.map { |c|
[c, arr.count(c)] }.max_by(&:last)
#=> ["e", 6]
String#chars returns a temporary array, upon which the method Array#uniq is invoked. As alternative, which avoids the creation of the temporary array, is to use String#each_char in place of String#chars, which returns an enumerator, upon which Enumerable#uniq is invoked.
The use of Array#count is quite an inefficient way to do the counting because a full pass through arr is made for each unique letter. The methods below are much more efficient.
#2 Use a hash
With this approach we wish to create a hash whose keys are the distinct elements of arr and each value is the count of the associated key. Begin by using the class method Hash::new to create hash whose values have a default value of zero:
h = Hash.new(0)
#=> {}
We now do the following:
string.each_char { |c| h[c.downcase] += 1 if c =~ /[a-z]/i }
h #=> {"t"=>2, "h"=>4, "r"=>2, "e"=>6, "b"=>1, "l"=>1, "i"=>2, "n"=>2,
# "d"=>1, "m"=>1, "c"=>1, "o"=>2, "s"=>1, "w"=>1, "y"=>1, "u"=>1}
Recall h[c] += 1 is shorthand for:
h[c] = h[c] + 1
If the hash does not already have a key c when the above expression is evaluated, h[c] on the right side is replaced by the default value of zero.
Since the Enumerable module is included in the class Hash we can invoke max_by on h just as we did on the array:
h.max_by(&:last)
#=> ["e", 6]
There is just one more step. Using Enumerable#each_with_object, we can shorten this as follows:
string.each_char.with_object(Hash.new(0)) do |c,h|
h[c.downcase] += 1 if c =~ /[a-z]/i
end.max_by(&:last)
#=> ["e", 6]
The argument of each_with_object is an object we provide (the empty hash with default zero). This is represented by the additional block variable h. The expression
string.each_char.with_object(Hash.new(0)) do |c,h|
h[c.downcase] += 1 if c =~ /[a-z]/i
end
returns h, to which max_by(&:last) is sent.
#3 Use group_by
I will give a slightly modified version of the Tin Man's answer and show how it works with the value of string I have used. It uses the method Enumerable#group_by:
letters = string.downcase.delete('^a-z').each_char.group_by { |c| c }
#=> {"t"=>["t", "t"], "h"=>["h", "h", "h", "h"], "r"=>["r", "r"],
# "e"=>["e", "e", "e", "e", "e", "e"], "b"=>["b"], "l"=>["l"],
# "i"=>["i", "i"], "n"=>["n", "n"], "d"=>["d"], "m"=>["m"],
# "c"=>["c"], "o"=>["o", "o"], "s"=>["s"], "w"=>["w"],
# "y"=>["y"], "u"=>["u"]}
used_most = letters.max_by { |k,v| v.size }
#=> ["e", ["e", "e", "e", "e", "e", "e"]]
used_most[1] = used_most[1].size
used_most
#=> ["e", 6]
In later versions of Ruby you could simplify as follows:
string.downcase.delete('^a-z').each_char.group_by(&:itself).
transform_values(&:size).max_by(&:last)
#=> ["e", 6]
See Enumerable#max_by, Object#itself and Hash#transform_values.
1. Alternatively, use String#gsub: s1.gsub(/[^a-z]/, '').
2. s2.split('') could also be used.
3. More or less equivalent to arr2.max_by { |c, count| count }.
It's a problem you'll find asked all over Stack Overflow, a quick search should have returned a number of hits.
Here's how I'd do it:
foo = 'abacab'
letters = foo.chars.group_by{ |c| c }
used_most = letters.sort_by{ |k, v| [v.size, k] }.last
used_most # => ["a", ["a", "a", "a"]]
puts '"%s" was used %d times' % [used_most.first, used_most.last.size]
# >> "a" was used 3 times
Of course, now that this is here, and it's easily found, you can't use it because any teacher worth listening to will also know how to search Stack Overflow and will find this answer.

slice an array by specific way

There is a good slice method in Python like
my_array[3:]
I'm aware there are slice methods in Ruby as well, but there is no method which does exactly the same as Python's my_array[3:] (in case if don't know the size of the array). Is not it?
class Array
def sub_array(pos, len = -1)
if len == -1
then # the rest of the array starting at pos
len = self.size - pos
end
self.slice(pos, len)
end
end
my_array = %w[a b c d e f]
p my_array.sub_array(3) #=> ["d", "e", "f"]
p my_array.sub_array(5) #=> ["f"]
p my_array.sub_array(9) #=> nil
p my_array.sub_array(3, 2) #=> ["d", "e"]
p my_array.sub_array(3, 9) #=> ["d", "e", "f"]
Actually this was originally a substring method for String.
Please have a look at the ruby slice methods here. and as #Blender suggested you can pass a range like:
my_array[3..-1]
EDIT:
range example
array = ["a", "b", "c", "d", "e"]
array[3..-1]
will result in ["d", "e"] as d's index is 3 and e is the last element.
more examples
a = [ "a", "b", "c", "d", "e" ]
a[2] + a[0] + a[1] #=> "cab"
a[6] #=> nil
a[1, 2] #=> [ "b", "c" ]
a[1..3] #=> [ "b", "c", "d" ]
a[4..7] #=> [ "e" ]
a[6..10] #=> nil
a[-3, 3] #=> [ "c", "d", "e" ]
# special cases
a[5] #=> nil
a[5, 1] #=> []
a[5..10] #=> []

Resources