# Read and write stock prices to file using arrays

**URL:** <https://www.ruby-forum.com/t/read-and-write-stock-prices-to-file-using-arrays/186572>\
**Category:** Ruby\
**Created:** [April 9, 2010, 2:34am UTC](https://www.ruby-forum.com/t/read-and-write-stock-prices-to-file-using-arrays/186572 "2010-04-09T02:34:06Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![dracos](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/ea666f/32.png) [@dracos](https://www.ruby-forum.com/u/dracos)\
**Post date:** [April 9, 2010, 2:34am UTC](https://www.ruby-forum.com/t/read-and-write-stock-prices-to-file-using-arrays/186572/1 "2010-04-09T02:34:06Z")

</div>

I’m trying to write a script that will take stock ticker symbols from a  
file, pull the company name and current price and write it back to the  
file while ignoring comments and blank lines so as to leave the rest of  
the file intact.

I can get the stock prices etc. i’m after using the yahoofinance module

> **[TransparenTech | a Software Investment Agency](https://www.transparentech.com)**

like so

require ‘rubygems’  
require ‘yahoofinance’

quote\_type = YahooFinance::StandardQuote

# Set the symbols for which we want to retrieve quotes.

quote\_symbols = [‘yhoo’,‘goog’]

# Get the quotes from Yahoo! Finance. The get\_quotes method call

# returns a Hash containing one quote object of type “quote\_type” for

# each symbol in “quote\_symbols”. If a block is given, it will be

# called with the quote object (as in the example below).

YahooFinance.get\_quotes( quote\_type, quote\_symbols ) do |qt|  
puts “QUOTING: #{qt.symbol}”  
puts “#{qt.name}”  
puts “#{qt.lastTrade}”  
end

I want to create an array of ticker symbols like quote\_symbols above,  
but from a file.

here’s a test data file i’m using

# this is the comment line

# that was a blank, below is a stock

YHOO  
GOOG # this comment can be deleted

i would like the output to look like

# this is the comment line

# that was a blank, below is a stock

YHOO:Yahoo! Inc.:17.35  
GOOG:Google Inc.:567.49

this is the code i’m having trouble with.  
filename = ARGV[0]

File.open(filename, ‘r+’) do |f|  
lines = f.readlines # load array of lines  
lines.each do |line|  
next if line =~ /^#/ # skip comments  
next if line.chomp.empty? # skip empty lines  
$ticker[lines] = line #\*\*\*the problem  
puts lines.index(line)  
end  
end

$ticker.each do |p|  
puts “#{p}”  
end

\*\*\* I’m trying to create the array $ticker that has all my stocks so i  
can pass it on to the yahoofinance module.

The other stumbling block for me is how to keep the results of YHOO →   
Yahoo! Inc. and the price 17.35 matched up with YHOO so i can put it  
back on the same line.

Thanks  
Peter

---

<div class="post-metadata">

**Author:** ![dracos](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/ea666f/32.png) [@dracos](https://www.ruby-forum.com/u/dracos)\
**Post date:** [April 9, 2010, 10:03am UTC](https://www.ruby-forum.com/t/read-and-write-stock-prices-to-file-using-arrays/186572/2 "2010-04-09T10:03:53Z")

</div>

On Fri, Apr 9, 2010 at 2:34 AM, Peter M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> require ‘yahoofinance’  
> YahooFinance.get\_quotes( quote\_type, quote\_symbols ) do |qt|
> 
> end  
> end

If you are just reading the lines you can do:

lines = File.readlines(filename)

Then:

ticker = []  
lines.each do |line|  
next if line =~ /^#/ # skip comments  
next if line.chomp.empty? # skip empty lines  
ticker \<\< line  
end

p ticker

From your specs I think you are missing also the functionality of  
removing everything after a #. If a line can contain only a ticker  
symbol and optional things (after all a ticker symbol is just a word),  
you can do:

ticker = []  
lines.each do |line|  
next if line =~ /^#/ # skip comments  
next if line.chomp.empty? # skip empty lines  
ticker \<\< line.split(" ")[0] # or maybe line[/\w+/]  
end

> The other stumbling block for me is how to keep the results of YHOO →   
> Yahoo! Inc. and the price 17.35 matched up with YHOO so i can put it  
> back on the same line.

An idea would be to fill a hash with the data retrieved for each symbol:

ticker\_data = {}

YahooFinance.get\_quotes( quote\_type, quote\_symbols ) do |qt|  
puts “QUOTING: #{qt.symbol}”  
puts “#{qt.name}”  
puts “#{qt.lastTrade}”  
ticker\_data[qt.symbol] = [qt.name, qt.lastTrade]  
end

and then write to the file the data in ticker\_data. I can see  
additional complexity if you want to keep the file exactly as it is  
(with the comments) but inserting the retrieved data in place. The  
easiest would be to just write ticker\_data:

File.open(filename, “w”) do |file|  
ticker\_data.each {|ticker, data| file.puts “#{ticker} #{data.join(”  
“)}”}  
end

If you want to keep the same order of the symbols:

File.open(filename, “w”) do |file|  
ticker.each do |ticker|  
file.puts “#{ticker} #{ticker\_data[ticker].join(” “)}”}  
end  
end

Although this would remove the comments from the file.

Jesus.

---

<div class="post-metadata">

**Author:** ![dracos](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/ea666f/32.png) [@dracos](https://www.ruby-forum.com/u/dracos)\
**Post date:** [April 9, 2010, 3:57pm UTC](https://www.ruby-forum.com/t/read-and-write-stock-prices-to-file-using-arrays/186572/3 "2010-04-09T15:57:28Z")

</div>

> ticker = []  
> lines.each do |line|  
> next if line =~ /^#/ # skip comments  
> next if line.chomp.empty? # skip empty lines  
> ticker \<\< line  
> end

> ticker \<\< line.split(" ")[0] # or maybe line[/\w+/]

Thank you. The ticker \<\< line is really helpful.

> An idea would be to fill a hash with the data retrieved for each symbol:
> 
> ticker\_data = {}
> 
> YahooFinance.get\_quotes( quote\_type, quote\_symbols ) do |qt|  
> puts “QUOTING: #{qt.symbol}”  
> puts “#{qt.name}”  
> puts “#{qt.lastTrade}”  
> ticker\_data[qt.symbol] = [qt.name, qt.lastTrade]  
> end

I didn’t realize a hash could point to more than one object. Awesome.

> and then write to the file the data in ticker\_data. I can see  
> additional complexity if you want to keep the file exactly as it is  
> (with the comments) but inserting the retrieved data in place. The  
> easiest would be to just write ticker\_data:

this is why i was trying to do $ticker[lines] = line when storing the  
data. I thought I could point the index of the ticker symbol, which  
would tell me what line it was on, to my array $ticker. Then i wanted to  
use that index to write back the ticker data to the same line it came  
from.

i think i need to capture the tickers position in the file at this point  
ticker \<\< line  
and then somehow use it when writing the file back.

I’m going to work on that, but if there is another approach, or my  
thinking is flawed, a nudge in the right direction would be nice.

The hardest part of learning to code for me is thinking like a computer.

Thanks for the help Jesus.

---

<div class="post-metadata">

**Author:** ![dracos](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/ea666f/32.png) [@dracos](https://www.ruby-forum.com/u/dracos)\
**Post date:** [April 9, 2010, 4:47pm UTC](https://www.ruby-forum.com/t/read-and-write-stock-prices-to-file-using-arrays/186572/4 "2010-04-09T16:47:40Z")

</div>

On Fri, Apr 9, 2010 at 7:27 PM, Peter M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I didn’t realize a hash could point to more than one object. Awesome.

It can’t, but the one value a key points to can be an object of any  
type, including arrays etc… For instance, a very common ruby idiom is  
to group a set of objects by using a hash of arrays. For example,  
let’s say you have a bunch of people, who belong to one of three  
teams, red, blue or green.

data = [[‘john’, ‘red’],  
[‘jane’, ‘green’],  
[‘alice’, ‘red’],  
[‘bob’, ‘blue’],  
[‘tom’, ‘red’],  
[‘dick’, ‘green’],  
[‘harry’, ‘red’],  
[‘bill’, ‘blue’]]

You can collect the teams like so:

teams = {}

data.each {|name, team|  
teams[team] ||= [] # initialize with an empty array if this is the  
first time you’ve seen the team  
teams[team] \<\< name # append the name to the team’s array  
}

p teams

# =\> {“red”=\>[“john”, “alice”, “tom”, “harry”], “green”=\>[“jane”,

“dick”], “blue”=\>[“bob”, “bill”]}

martin

---

<div class="post-metadata">

**Author:** ![dracos](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/ea666f/32.png) [@dracos](https://www.ruby-forum.com/u/dracos)\
**Post date:** [April 9, 2010, 6:57pm UTC](https://www.ruby-forum.com/t/read-and-write-stock-prices-to-file-using-arrays/186572/5 "2010-04-09T18:57:06Z")

</div>

On Fri, Apr 9, 2010 at 3:57 PM, Peter M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I didn’t realize a hash could point to more than one object. Awesome.

As Martin explained, it’s just one object. It happens to be an array  
with many other objects inside, or it could be a struct, for example:

Quote = Struct.new(:symbol, :name, :last\_trade)  
YahooFinance.get\_quotes( quote\_type, quote\_symbols ) do |qt|  
ticker\_data[qt.symbol] = Quote.new qt.symbol, qt.name, qt.last\_trade  
end

or another hash:

YahooFinance.get\_quotes( quote\_type, quote\_symbols ) do |qt|  
ticker\_data[qt.symbol] = {:symbol =\> qt.symbol, :name =\> qt.name,  
:last\_trade =\> qt.last\_trade}  
end

depends on how you want to use it later.

> from.  
> The problem is that you can’t really insert things “in the middle” of a  
> file.  
> If you write something in the current position of a file (the cursor),  
> it will overwrite what’s after, it doesn’t push the rest of the file  
> to make space for what you are writing.

So, the usual simple way is to read the file, modify it in memory and  
write it all again. If you want to preserve everything, you could  
split the file in chunks with the comments, and replace the other  
lines with the result of your calculation. For that, you will have to  
keep track of which lines represent ticker data. For example  
(untested):

TickerData = Struct.new :symbol, :file\_position, :name, :last\_trade

lines = File.readlines(filename)  
tickers = {}  
lines.each\_with\_index do |line, i|  
next if line =~ /^#/ # skip comments  
next if line.chomp.empty? # skip empty lines  
symbol = line[/\w+/]  
tickers[symbol] = TickerData.new symbol, i #store the symbol and the  
file position  
end

YahooFinance.get\_quotes( quote\_type, tickers.keys ) do |qt|  
next unless tickers[qt.symbol]  
tickers[qt.symbol].name = qt.name  
tickers[qt.symbol].last\_trade = qt.last\_trade  
end

Now you have the original lines in an array and a hash with all the  
data, including to which line it corresponds, you can sub the line  
with what you want, and then write the file back:

tickers.each do |symbol, ticker|  
lines[ticker.file\_position] = “#{ticker.symbol} #{ticker.name}  
#{ticker.last\_trade}\n”  
end

File.open(filename, “w”) {|file| file.puts lines.join}

I think this should work but I haven’t tested it.

Jesus.

---

<div class="post-metadata">

**Author:** ![dracos](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/ea666f/32.png) [@dracos](https://www.ruby-forum.com/u/dracos)\
**Post date:** [April 10, 2010, 12:58am UTC](https://www.ruby-forum.com/t/read-and-write-stock-prices-to-file-using-arrays/186572/6 "2010-04-10T00:58:25Z")

</div>

On Apr 9, 2010, at 15:25 , Peter M. wrote:

> next if line.chomp.empty? # skip empty lines

you might want line.strip.empty? if you encounter errant whitespace in  
your data file.

---

<div class="post-metadata">

**Author:** ![dracos](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/ea666f/32.png) [@dracos](https://www.ruby-forum.com/u/dracos)\
**Post date:** [April 10, 2010, 12:25am UTC](https://www.ruby-forum.com/t/read-and-write-stock-prices-to-file-using-arrays/186572/7 "2010-04-10T00:25:00Z")

</div>

well, I just got home from work and was going to hack away… but instead  
i cut n paste and got a working script after a couple minor edits.  
Thanks so much for such a complete solution. I wasn’t expecting that!

Here is the working code in full. It handles my file perfectly.

require ‘rubygems’  
require ‘yahoofinance’

quote\_type = YahooFinance::StandardQuote

filename = ARGV[0]

TickerData = Struct.new :symbol, :file\_position, :name, :last\_trade

lines = File.readlines(filename)  
tickers = {}  
lines.each\_with\_index do |line, i|  
next if line =~ /^#/ # skip comments  
next if line.chomp.empty? # skip empty lines  
symbol = line[/\w+/].upcase  
tickers[symbol] = TickerData.new symbol, i #store the symbol and file  
position  
end

YahooFinance.get\_quotes( quote\_type, tickers.keys ) do |qt|  
next unless tickers[qt.symbol]  
tickers[qt.symbol].name = qt.name  
tickers[qt.symbol].last\_trade = qt.lastTrade  
end

tickers.each do |symbol, ticker|  
lines[ticker.file\_position] = “#{ticker.symbol} #{ticker.name}  
#{ticker.last\_trade}\n”  
end

File.open(filename, “w”) {|file| file.puts lines.join}

Off to do some reading on structs  
Thanks Martin and Jesus
