17

Is there anyway to find the next item in a Ruby Array?

Code:

# Find ALL languages
if !debug
  lang = Language.all
else
  lang = Language.where("id = ? OR id = ?", 22, 32)
end

# Get all elements
elements = Element.where("human_readable IS NOT NULL")

lang.each do |l|
  code = l.code.downcase
  if File.exists?(file_path + code + ".yml")
    File.delete(file_path + code + ".yml")
  end

  t1 = Time.now

  info = {}
  elements.each do |el|
    unless l.id == 1
      et = el.element_translations.where("language_id = ? AND complete = ?", l.id, true)
    else
      et = el.element_translations.where("language_id = ?", 1)
    end
    et.each do |tran|
      info[code] ||= {}
      info[code][el.human_readable] = tran.content.gsub("\n", "").force_encoding("UTF-8").encode!
    end
  end
  File.open(file_path + code + ".yml", "w", :encoding => "UTF-8") do |f|
    if f.write(info.to_yaml)
      t2 = Time.now

      puts code + ".yml File written"
      puts "It took " + time_diff_milli(t1, t2).to_s + " seconds to complete"
      # This is where I want to display the next item in the lang array
      puts lang.shift(1).inspect
      puts "*"*50
    end
  end
end
6
  • Beware puts lang.shift(1).inspect, using that inside a block iterating over lang might cause some issues. Then again, it might not. Commented Jan 4, 2011 at 17:35
  • 10
    -1 for an incomprehensible question. Don't just dump code on us and expect us to fix it. Put some effort into writing your question. Commented Jan 4, 2011 at 18:22
  • @Theo, I didn't just "dump" code on anyone... I was simply asking a question... the code above was to show the work I had done, in case anyone needed to reference it. Commented Jan 4, 2011 at 18:37
  • 1
    I think the problem is the question seems so simple it's hard to accept that you are actually asking what we think you are asking, i.e. Given item x at index i how do you read the item y at i+1. If so then x = array[i] and y = array[i+1]. This is not even programming 101, it's programming kindergarten, so we can't imagine this is what you are asking. This isn't meant to offend but to clarify. Commented Jan 4, 2011 at 20:10
  • Also in your code always use descriptive variable names. It makes no sense to use single letters and in fact is poor coding practice. Take an extra 500 milliseconds to type a full word and you'll thank yourself three months later when you go back to fix a bug in your code. Commented Jan 4, 2011 at 20:15

5 Answers 5

34

Array includes Enumerable, so you can use each_with_index:

elements.each_with_index {|element, index|
   next_element = elements[index+1]
   do_something unless next_element.nil?
   ...

}
Sign up to request clarification or add additional context in comments.

2 Comments

+1 A few tweaks required to get exactly what the OP wants, but a good general description of the right approach
I think you meant to put do_something unless next_element.nil?
30

A nice way to iterate over an Enumerable if you need to access both an element and the next one is using each_cons:

arr = [1, 2, 3]
arr.each_cons(2) do |element, next_element|
   p "#{element} is followed by #{next_element}"
   #...
end

# => "1 is followed by 2", "2 is followed by 3".

As pointed out by Phrogz, Enumerable#each_cons is available in Ruby 1.8.7+; for Ruby 1.8.6 you can require 'backports/1.8.7/enumerable/each_cons'.

As @Jacob points out, the other approach is to use each_with_index.

2 Comments

For the record, each_cons is only available in 1.8.7+ (not in 1.8.6).
@Phrogz: Indeed! I'm the one normally pointing this out. Answer updated
5
arr[n..-1].find_index(obj) + n

Comments

4

Based on Marc-Andrés nice answer I want to provide an answer that, in a generic way, gives the following element for all elements, also the last one, by padding with nil:

arr = [1, 2, 3]
[arr, nil].flatten.each_cons(2) do |element, next_element|
   p "#{element} is followed by #{next_element || 'nil'}"
end

# "1 is followed by 2"
# "2 is followed by 3"
# "3 is followed by nil"

Now, while we are at it, we can also provide the preceding element for all elements:

arr = [1, 2, 3]
[nil, arr, nil].flatten.each_cons(3) do |prev_element, element, next_element|
  p "#{element} is preceded by #{prev_element || 'nil'} and followed by #{next_element || 'nil'}"
end

# "1 is preceded by nil and followed by 2"
# "2 is preceded by 1 and followed by 3"
# "3 is preceded by 2 and followed by nil"

Comments

4

or add this to your project, then you can call the next_item(item) method on whatever array you like.

class Array
  def next_item(item)
     self[self.find_index(item) + 1] unless self.find_index(item).nil?
  end
end

note on the use of self in my proposed answer. I recognize that the use self is, in one sense, redundant and unnecessary. It is my preference to use self more frequently than needed to make the code more obvious, especially to junior programmers. Simplicity, even when it requires more characters, is important to me.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.