Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: What's an elegant way to pick a random line from a text file?

Tags:

file

io

ruby

I've seen some really beautiful examples of Ruby and I'm trying to shift my thinking to be able to produce them instead of just admire them. Here's the best I could come up with for picking a random line out of a file:

def pick_random_line
  random_line = nil
  File.open("data.txt") do |file|
    file_lines = file.readlines()
    random_line = file_lines[Random.rand(0...file_lines.size())]
  end 

  random_line                                                                                                                                                               
end 

I feel like it's gotta be possible to do this in a shorter, more elegant way without storing the entire file's contents in memory. Is there?

like image 956
Tres Avatar asked Jun 13 '12 01:06

Tres


People also ask

How to process every line in a text file with Ruby?

There are quite a few ways to open a text file with Ruby and then process its contents, but this example probably shows the most concise way to do it: # ruby sample code. # process every line in a text file with ruby (version 1). file='GettysburgAddress.txt' File.readlines (file).each do |line| puts line end

How do I have Python select a random line out of text?

How do I have python select a random line out of my text file and give my output as that number? Assuming the file is relatively small, the following is perhaps the easiest way to do it: import random line = random.choice (open ('data.txt').readlines ())

How to randomize the number of lines in a text file?

Let’s take a closer look at the last command line: In the section rnd=$ ( ( 1 + $RANDOM % $ (wc -l < example_file.txt) )), we choose a random number from within range of 1 to the number of lines in the file

How do I read a file in Ruby?

You can read a file in Ruby like this: 1 Open the file, with the open method. 2 Read the file, the whole file, line by line, or a specific amount of bytes. 3 Close the file, with the close method. More ...


2 Answers

There is already a random entry selector built into the Ruby Array class: sample().

def pick_random_line
  File.readlines("data.txt").sample
end
like image 155
Dave Isaacs Avatar answered Oct 02 '22 11:10

Dave Isaacs


You can do it without storing anything except the most recently-read line and the current candidate for the returned random line.

def pick_random_line
  chosen_line = nil
  File.foreach("data.txt").each_with_index do |line, number|
    chosen_line = line if rand < 1.0/(number+1)
  end
  return chosen_line
end

So the first line is chosen with probability 1/1 = 1; the second line is chosen with probability 1/2, so half the time it keeps the first one and half the time it switches to the second.

Then the third line is chosen with probability 1/3 - so 1/3 of the time it picks it, and the other 2/3 of the time it keeps whichever one of the first two it picked. Since each of them had a 50% chance of being chosen as of line 2, they each wind up with a 1/3 chance of being chosen as of line 3.

And so on. At line N, every line from 1-N has an even 1/N chance of being chosen, and that holds all the way through the file (as long as the file isn't so huge that 1/(number of lines in file) is less than epsilon :)). And you only make one pass through the file and never store more than two lines at once.

EDIT You're not going to get a real concise solution with this algorithm, but you can turn it into a one-liner if you want to:

def pick_random_line
  File.foreach("data.txt").each_with_index.reduce(nil) { |picked,pair| 
    rand < 1.0/(1+pair[1]) ? pair[0] : picked }
end
like image 44
Mark Reed Avatar answered Oct 02 '22 11:10

Mark Reed