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
Related
This might be a silly question, but I'm struggling with outputting the positions of an array of hashes I have.
If I have an array of hashes, we'll call some_array, that looks like this:
some_array =
[{:id=>7, :people=>["Bob B", "Jimmy J"], :product=>"product1"},
{:id=>2, :people=>["Sally S"], :product=>"product1"},
{:id=>5, :people=>["Hank H", "Stan C"], :product=>"product2"},
{:id=>3, :people=>["Sue T"], :product=>"product2"},
{:id=>4, :people=>["Anne W"], :product=>"product3"}]
I then iterate though some_array like so:
some_array.select{|p| p[:product] == "product2"]}.each do |product|
product[:people].join("<br>")
product[:product]
Which outputs like:
Hank K product 2
Stan C
Sue T product 2
How would I go about outputting the index/position of each hash in the array?
Would I be able to do something along the lines of:
some_array.select{|p| p[:product] == "product2"]}.each do |product|
product.index
product[:people].join("<br>")
product[:product]
And get:
2 Hank K product2
Stan C
3 Sue T product2
Thank you!
You can use each_with_index and format to your use case:
some_array.each_with_index do |product, index|
if product[:product] == "product2"
p index
p product
end
end
In Ruby, you can chain methods on Enumerable, which allows you to call with_index before you select to get the original index of the element:
some_array.each_with_index.select do |element, _|
element[:product] == "product2"
end.each do |product, index|
p [index, product[:people].join("<br />"), product[:product]]
end
# outputs:
# [2, "Hank H<br />Stan C", "product2"]
# [3, "Sue T", "product2"]
While you can call select.with_index, and it may be tempting to do so, the index won't carry over into the each, because select returns the elements that matched and doesn't care about the input. When you call each_with_index (or each.with_index), though, you now have a new Enumerable which is each element in your array with its index in that array, and select ends up returning these new array elements:
some_array.each.with_index.select { |element, _| element[:product] == "product2" }
# => [[{:id=>5, :people=>["Hank H", "Stan C"], :product=>"product2"}, 2],
[{:id=>3, :people=>["Sue T"], :product=>"product2"}, 3]]
fmt_str_first = "%-4d%-10s%10s"
fmt_str_rest = "#{' '*4}%-10s"
some_array.each_with_index do |h,i|
next unless h[:product] == "product2"
first, *rest = h[:people]
puts fmt_str_first % [i, first, "product2"]
rest.each { |name| puts fmt_str_rest % name }
puts
end
2 Hank H product2
Stan C
3 Sue T product2
See Kernel#sprintf. Note that %-10s in the format string means that the corresponding entry, a string (s), is to be left-adjusted (-) in a field of width 10. %10s would cause the entry to be right-adjusted.
you can just use each_with_index and skip the item you don't need
some_array.each_with_index do |product, index|
next if product[:product] != "product2"
index
product[:people].join("<br>")
product[:product]
end
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
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]
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Tell the end of a .each loop in ruby
I have a Hash:
=> {"foo"=>1, "bar"=>2, "abc"=>3}
and a code:
foo.each do |elem|
# smth
end
How to recognize that an element in cycle is last?
Something like
if elem == foo.last
puts 'this is a last element!'
end
For example like this:
foo.each_with_index do |elem, index|
if index == foo.length - 1
puts 'this is a last element!'
else
# smth
end
end
The problem you might have is that items in a map are not coming in any specific order. On my version of Ruby I see them in the following order:
["abc", 3]
["foo", 1]
["bar", 2]
Maybe you want to traverse the sorted keys instead. Like this for example:
foo.keys.sort.each_with_index do |key, index|
if index == foo.length - 1
puts 'this is a last element!'
else
p foo[key]
end
end
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) }