Reputation: 181
I am trying to select elements out of an array:
arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n']
whose index is a Fibonacci number. I want the result:
['a', 'b', 'c', 'd', 'f', 'i', 'n']
My code returns both the element and the index.
def is_fibonacci?(i, x = 1, y = 0)
return true if i == x || i == 0
return false if x > i
is_fibonacci?(i, x + y, x)
end
arr.each_with_index.select do |val, index|
is_fibonacci?(index)
end
This code returns:
[["a", 0], ["b", 1], ["c", 2], ["d", 3], ["f", 5], ["i", 8], ["n", 13]]
Please help me understand how I can still iterate through the array and evaluate the index but only return the element.
Upvotes: 18
Views: 24514
Reputation: 110755
Here's another way to do it.
index_gen = Enumerator.new do |y|
i = 0
j = 1
loop do
y.yield i unless i==j
i, j = j, i + j
end
end
#=> #<Enumerator: #<Enumerator::Generator:0x007fa3eb979028>:each>
arr.values_at(*index_gen.take_while { |n| n < arr.size })
#=> ["a", "b", "c", "d", "f", "i", "n"]
or
index_gen.take_while { |n| n < arr.size }.map { |n| arr[n] }
#=> ["a", "b", "c", "d", "f", "i", "n"]
Note:
0, 1, 1, 2,...
. The construction of the enumerator index_gen
skips the second 1
.index_gen.take_while { |n| n < arr.size } #=> [0, 1, 2, 3, 5, 8, 13]
Upvotes: 1
Reputation: 84182
You can change the last bit of your code to
arr.select.with_index do |val, index|
is_fibonacci?(index)
end
This works because if you call a method such as select
without a block, you get an Enumerator
object, on which you can then chain more Enumerable methods.
In this case I've used with_index
, which is very similar to calling each_with_index
on the original array. However because this happens after the select
instead of before, select
returns items from the original array, without the indices appended
Upvotes: 35
Reputation: 7223
Your code seems great so far, I wouldn't change it. You can go over your results after the fact and change the [element, index]
pairs to only contain the element
by mapping over each pair and only taking the first:
>> results = [["a", 0], ["b", 1], ["c", 2], ["d", 3], ["f", 5], ["i", 8], ["n", 13]]
>> results.map(&:first)
=> ["a", "b", "c", "d", "f", "i", "n"]
Upvotes: 3