tags:

views:

162

answers:

5

I have some big fixed-width files and I need to drop the header line.

Keeping track of an iterator doesn't seem very idiomatic.

# This is what I do now.
File.open(filename).each_line.with_index do |line, idx|
  if idx > 0
     ...
  end
end

# This is what I want to do but I don't need drop(1) to slurp
# the file into an array.
File.open(filename).drop(1).each_line do { |line| ... }

What's the Ruby idiom for this?

+1  A: 

Off the top of my head but I'm sure with some more research there's a more elegant way

File.open( filename ).each_line.to_a[1..-1].each{ |line|... }

Okay scratch that... did a bit of research and this might be better

File.open( filename ).each_line.with_index.drop_while{ |line,index|  index == 0 }.each{ |line, index| ... }
Farrel
That will eagerly evaluate the enumerator to an array before iterating over lines, causing the whole file to be slurped to memory at once.
Samuel Danielson
Yeah realised that. I've updated it using nothing but enumerators.
Farrel
Not sure if drop_while will work as according to the docs it also returns an array...
Farrel
Yup. Looks like drop_while returns an Array.10.times.class => Enumerator ... 10.times.drop_while{ |line,index| index == 0 }.class => Array
Samuel Danielson
+3  A: 

This is slightly neater:

File.open(fname).each_line.with_index do |line, lineno|
  next if lineno == 0
  # ...
end

or

io = File.open(fname)
# discard the first line
io.gets
# process the rest of the file
io.each_line {|line| ...}
io.close
glenn jackman
I like glenn's second solution here, even if it doesn't use the neater-looking `File.open() do ... end` closure.
bta
+1  A: 

I doubt that this is idiomatic, but it's simple.

f = File.open(filename)
f.readline
f.each_line do |x|
   #...
end
Shadowfirebird
I apologise, I see that while I was composing this Glenn beat me to it.
Shadowfirebird
+1  A: 

Now that you've gotten reasonable answers, here's a completely different way to handle it.

class ProcStack
  def initialize(&default)
    @block = default
  end
  def push(&action)
    prev = @block
    @block = lambda do |*args|
      @block = prev
      action[*args]
    end
    self
  end
  def to_proc
    lambda { |*args| @block[*args] }
  end
end
#...
process_lines = ProcStack.new do |line, index|
  puts "processing line #{index} => #{line}"
end.push do |line, index|
  puts "skipping line #{index} => #{line}"
end
File.foreach(filename).each_with_index(&process_lines)

It's neither idiomatic, nor terribly intuitive the first time through, but it's fun!

rampion
actually, if you use a queue, it's a lot clearer (less reversed logic)
rampion
+2  A: 

If you need it more than once, you could write an extension to Enumerator.

class Enumerator
  def enum_drop(n)
    with_index do |val, idx|
      next if n == idx
      yield val
    end
  end
end

File.open(testfile).each_line.enum_drop(1) do |line|
  print line
end

# prints lines #1, #3, #4, …
Debilski
This is a very nice (and rubyish) solution. If you don't like the language, change it. I was sure that what I wanted to do was so common there would be an existing idiom or function for it though. It's been two days since I asked though so I suppose not. enum_cons and enum_slice exist so maybe the name enum_drop would fit the stdlib better. Thank you.
Samuel Danielson
You’re right. That sounds better. Changed it to `enum_drop`.
Debilski