Ruby each_with_index offset - ruby

Can I define the offset of the index in the each_with_index loop iterator?
My straight forward attempt failed:
some_array.each_with_index{|item, index = 1| some_func(item, index) }
Edit:
Clarification: I don't want an array offset I want that the index within the each_with_index doesn't start from 0 but e.g. 1.

Actually, Enumerator#with_index receives offset as an optional parameter:
[:foo, :bar, :baz].to_enum.with_index(1).each do |elem, i|
puts "#{i}: #{elem}"
end
outputs:
1: foo
2: bar
3: baz
BTW, I think it is there only in 1.9.2.

The following is succinct, using Ruby's Enumerator class.
[:foo, :bar, :baz].each.with_index(1) do |elem, i|
puts "#{i}: #{elem}"
end
output
1: foo
2: bar
3: baz
Array#each returns an enumerator, and calling Enumerator#with_index returns another enumerator, to which a block is passed.

1) The simplest is to substitute index+1 instead of index to the function:
some_array.each_with_index{|item, index| some_func(item, index+1)}
but probably that is not what you want.
2) The next thing you can do is to define a different index j within the block and use it instead of the original index:
some_array.each_with_index{|item, i| j = i + 1; some_func(item, j)}
3) If you want to use index in this way often, then define another method:
module Enumerable
def each_with_index_from_one *args, &pr
each_with_index(*args){|obj, i| pr.call(obj, i+1)}
end
end
%w(one two three).each_with_index_from_one{|w, i| puts "#{i}. #{w}"}
# =>
1. one
2. two
3. three
Update
This answer, which was answered a few years ago, is now obsolete. For modern Rubies, Zack Xu's answer will work better.

If some_index is somehow meaningful, then consider using a hash, rather than an array.

I ran into it.
My solution not necessary is the best, but it just worked for me.
In the view iteration:
just add: index + 1
That's all for me, as I don't use any reference to those index numbers but just for show in a list.

Yes, you can
some_array[offset..-1].each_with_index{|item, index| some_func(item, index) }
some_array[offset..-1].each_with_index{|item, index| some_func(item, index+offset) }
some_array[offset..-1].each_with_index{|item, index| index+=offset; some_func(item, index) }
UPD
Also I should notice that if offset is more than your Array size it will though an error. Because:
some_array[1000,-1] => nil
nil.each_with_index => Error 'undefined method `each_with_index' for nil:NilClass'
What can we do here:
(some_array[offset..-1]||[]).each_with_index{|item, index| some_func(item, index) }
Or to prevalidate offset:
offset = 1000
some_array[offset..-1].each_with_index{|item, index| some_func(item, index) } if offset <= some_array.size
This is little hacky
UPD 2
As far as you updated your question and now you need not Array offset, but index offset so #sawa solution will works fine for you

Ariel is right. This is the best way to handle this, and it's not that bad
ary.each_with_index do |a, i|
puts i + 1
#other code
end
That is perfectly acceptable, and better than most of the solutions I've seen for this. I always thought this was what #inject was for...oh well.

Another approach is to use map
some_array = [:foo, :bar, :baz]
some_array_plus_offset_index = some_array.each_with_index.map {|item, i| [item, i + 1]}
some_array_plus_offset_index.each{|item, offset_index| some_func(item, offset_index) }

This works in every ruby version:
%W(one two three).zip(1..3).each do |value, index|
puts value, index
end
And for a generic array:
a.zip(1..a.length.each do |value, index|
puts value, index
end

offset = 2
some_array[offset..-1].each_with_index{|item, index| some_func(item, index+offset) }

Related

How can I modify my ruby method so it takes in a block of code as well?

I have a method called myFilter that takes in an array, and filters out the elements that don't meet the requirement.
For example.
arr = [4,5,8,9,1,3,6]
answer = myfilter(arr) {|i| i>=5}
this run would return an array with elements 5,8,9,6 since they are all greater than or equal to 5.
How would I preform this? the algorithm is easy, but I don't understand how we take in that condition.
Thank you.
I take for granted you don't want to use select method or similar but you want to understand how blocks work.
def my_filter(arr)
if block_given?
result = []
arr.each { |element| result.push(element) if yield element } # here you use the block passed to this method and execute it with the current element using yield
result
else
arr
end
end
The idiomatic way would be:
def my_filter(arr)
return enum_for(:my_filter, arr) unless block_given?
arr.each_with_object([]) do |e, acc|
acc << e if yield e
end
end
More info on Enumerator::Lazy#enum_for.
you can do
def my_filter(arr, &block)
arr.select(&block)
end
then call
my_filter([1, 2, 3]) { |e| e > 2 }
=> [3]
but instead you can just call select with a block directly :)

Iterating over each element of an array, except the first one

What is the idiomatic Ruby way to write this code?
Given an array, I would like to iterate through each element of that array, but skip the first one. I want to do this without allocating a new array.
Here are two ways I've come up with, but neither feels particularly elegant.
This works but seems way too verbose:
arr.each_with_index do |elem, i|
next if i.zero? # skip the first
...
end
This works but allocates a new array:
arr[1..-1].each { ... }
Edit/clarification: I'd like to avoid allocating a second array. Originally I said I wanted to avoid "copying" the array, which was confusing.
Using the internal enumerator is certainly more intuitive, and you can do this fairly elegantly like so:
class Array
def each_after(n)
each_with_index do |elem, i|
yield elem if i >= n
end
end
end
And now:
arr.each_after(1) do |elem|
...
end
I want to do this without creating a copy of the array.
1) Internal iterator:
arr = [1, 2, 3]
start_index = 1
(start_index...arr.size).each do |i|
puts arr[i]
end
--output:--
2
3
2) External iterator:
arr = [1, 2, 3]
e = arr.each
e.next
loop do
puts e.next
end
--output:--
2
3
OK, maybe this is bad form to answer my own question. But I've been racking my brain on this and poring over the Enumerable docs, and I think I've found a good solution:
arr.lazy.drop(1).each { ... }
Here's proof that it works :-)
>> [1,2,3].lazy.drop(1).each { |e| puts e }
2
3
Concise: yes. Idiomatic Ruby… maybe? What do you think?

Some help on using index where an object appears multiple times in an array (Ruby)

I am learning how to use the .index function to show a string's position inside an array. It is pretty useful. However, I am wondering how I can use it (or some other method) to show ALL the places a string exists in an array?
So for example if I have the code:
array= ['cat', 'dog', 'eagle', 'moose', 'pets', 'animal', 'eagle', 'hawk']
puts 'At position ' + array.index('eagle').to_s + ' in the array is eagle!'
puts array.index('eagle')
each time, it tells me that the position is 2, even though it is 2 and also position 6 in the array. Is there a simple way to get back all the instances of a particular string within an array? Or would I need to write a loop to do it manually?
Another way:
array.each_index.select { |i| array[i] == value } #=> [2, 6]
I would get the array elements into pairs of [obj, index] using each_with_index, then select pairs where obj is 'eagle', and finally return all the index values (last in a pair, so use the last method):
array.each_with_index.select { |x| x[0] == 'eagle' }.map(&:last)
# => [2, 6]
As a convenience method in Array:
class Array
def indices_of(obj)
each_with_index.select { |x| x[0] == obj }.map(&:last)
end
end
['cat', 'dog', 'eagle', 'moose', 'pets', 'animal', 'eagle', 'hawk'].indices_of 'eagle'
# => [2, 6]
You can try this:
value = "eagle"
array.each_with_index { |item, index| puts index if item == value }
In your question you just seemed to want to print the index value. you can do what ever you want in this block.
value = "eagle"
array.each_with_index do |item, index|
if item == value
# do whatever you like
puts index
end
end

How could I make index still when I delete element in Array.each loop?

As I know , when I delete an element in Array.The index still move forward. How can I make the index still when I delete elements in the loop?
Below is just an example. In this example, I hope to delete all the elements with unique num ,but finally num '2' is still left.
class Test
attr_accessor :num
attr_accessor :job
def initialize(num,job)
#num = num
#job = job
end
end
a = []
a << Test.new(1,'jobA')
a << Test.new(2,'jobB')
a << Test.new(1,'jobC')
a << Test.new(3,'jobD')
b = Hash.new
a.each_with_index do |i,index|
#puts i.num,index
if b[i.num] == nil
b[i.num] = true
a.delete(i)
end
end
a.each do |i|
puts i.num,i.job
end
# it shows:
# 2 jobB
# 1 jobC
# I hope to get only:
# 1 jobC
In this new example , I hope 'jobC' with the duplicate num '1' left, and hope other unique num elements deleted.
Hope someone can help, Thank you !
I use delete_if finally , Thanks for all your replies to help me.
I think you're looking for #delete_if
This method allows you to specify a test - if the element passes the test, it is deleted.
#clear is for removing all elements unconditionally
You shouldn't be removing elements from within an iterator such as #each
I'm not sure If I understand your logic for this operation correctly or maybe you need to rethink why you approaching it this way, but something like this might work for you instead of .each:
a = (1..5).to_a
a.reject! do |c|
puts c # or perform other operations on the element
true
end
puts a.inspect
If you just want to keep that array of same length and replace all the values with nil then
a = some_array
Array.new(a.length)
#[nil, nil, nil, nil, nil]
Or
If you want to delete only some values and also preserve the index of other elements then do
For Ex: Deleting even numbers
a = [1,2,3,4,5]
a.each_with_index { |num, index| a[index] = nil if num % 2 == 0 }
#[1, nil, 3, nil, 5]

each_with_index_do starting at 1 for index

I am using a ruby iterator on a view in a rails app like so:
<% (1..#document.data.length).each_with_index do |element, index| %>
...
<% end %>
I thought the addition of the 1.. instead of just saying:
#document.data
would get the trick of having the index above start at 1. But alas, the above code index is still 0 to data.length (-1 effectively). So what am I doing wrong, i need the index to equal 1-data.length...no clue how to set up the iterator to do this.
Unless you're using an older Ruby like 1.8 (I think this was added in 1.9 but I'm not sure), you can use each.with_index(1) to get a 1-based enumerator:
In your case it would be like this:
<% #document.data.length.each.with_index(1) do |element, index| %>
...
<% end %>
I think maybe you misunderstand each_with_index.
each will iterate over elements in an array
[:a, :b, :c].each do |object|
puts object
end
which outputs;
:a
:b
:c
each_with_index iterates over the elements, and also passes in the index (starting from zero)
[:a, :b, :c].each_with_index do |object, index|
puts "#{object} at index #{index}"
end
which outputs
:a at index 0
:b at index 1
:c at index 2
if you want it 1-indexed then just add 1.
[:a, :b, :c].each_with_index do |object, index|
indexplusone = index + 1
puts "#{object} at index #{indexplusone}"
end
which outputs
:a at index 1
:b at index 2
:c at index 3
if you want to iterate over a subset of an array, then just choose the subset, then iterate over it
without_first_element = array[1..-1]
without_first_element.each do |object|
...
end
This may not be exactly the same each_with_index method in question, but I think the result may close to something in mod is asking...
%w(a b c).each.with_index(1) { |item, index| puts "#{index} - #{item}" }
# 1 - a
# 2 - b
# 3 - c
For more information https://ruby-doc.org/core-2.6.1/Enumerator.html#method-i-with_index
Use Integer#next:
[:a, :b, :c].each_with_index do |value, index|
puts "value: #{value} has index: #{index.next}"
end
produces:
value: a has index: 1
value: b has index: 2
value: c has index: 3
There is no such thing as making the index start from 1. If you want to skip the first item in the array use next.
<% (1..#document.data.length).each_with_index do |element, index| %>
next if index == 0
<% end %>
An array index is always going to be zero based.
If you want to skip the first element, which it sounds like you do:
#document.data[1..-1].each do |data|
...
end
If I understand your question right, you want to start the index from 1, but in ruby arrays goes as 0 base indexes, so the simplest way would be
given #document.data is an array
index = 1
#document.data.each do |element|
#your code
index += 1
end
HTH
I had the same problem, and solved it by using the each_with_index method. But added 1 to the index in the code.
#someobject.each_with_index do |e, index|
= index+1

Resources