so I've written a code that iterates through several hundred CSV files, and then stores the last element of each into a new array.
module Example
#array = []
def example(file_names) #where file_names is an array of strings for the csv files
file_names.each { |x|
#array << (CSV.parse open("#{x}.csv").read)[-1] if File.exists?("{x}.csv") == true }
return #array
end
end
Executing this code can take some time, and I want to be able to refer to this newly-created array in other methods without having to run this code again. Is there a way to permanently store the #array variable?
It depends on just how permanent you want your results to be. If you just don't want to parse the CSV files for the lifetime of your program, then you can simply cache the result in a member variable (as you are with #array), and only execute your code if that array is empty for example:
module Example
def example(file_names)
# ||= will only calculate a result if #array is nil, otherwise
# it will return the saved value
#array ||= file_names.map { |x| CSV.parse open("#{x}.csv").read)[-1] if File.exists?("{x}.csv") }
end
end
If you want your work to be saved in-between executions of the program you can try saving your results to a (single) file and reading it back in, using perhaps on of the following:
JSON: http://ruby-doc.org/stdlib-2.2.2/libdoc/json/rdoc/JSON.html
YAML: http://ruby-doc.org/stdlib-2.2.2/libdoc/yaml/rdoc/YAML.html
Marshal: http://ruby-doc.org/core-2.2.2/Marshal.html
See mu-is-too-short's comment for some of the drawbacks of using Marshal
Related
I am trying to concat two strings that are returned by a function to two existing strings in one line.
this is my code with extra steps
def my_function()
return "foo", "bar"
end
foo = String.new
bar = String.new
ret1, ret2 = my_function()
foo.concat(ret1)
bar.concat(ret2)
I am trying something like the following, but it is not working
foo.concat(ret1), bar.concat(ret2) = my_function()
some more information as requested:
I am basically trying to write a config converter. The config files need to be plain text files. To make the code more structured, i created the following module, and then call the module functions whenever i need to genereate a specific part of the config. After that, I write the return into a string, which is then written to a file once everything is done:
module L4_LB
extend self
def build_ssl(some_vars)
returns string_a, string_b
end
def build_vip(some_vars)
returns string_a, string_b
end
def build_pool(some_vars)
returns string_a, string_b
end
end
config_file_a = String.new
config_file_b = String.new
ret_a, ret_b = L4_LB.build_ssl(some_vars)
config_file_a.concat(ret_a)
config_file_a.concat(ret_b)
ret_a, ret_b = L4_LB.build_vip(some_vars)
config_file_a.concat(ret_a)
config_file_a.concat(ret_b)
ret_a, ret_b = L4_LB.build_pool(some_vars)
config_file_a.concat(ret_a)
config_file_a.concat(ret_b)
It depends on how concat is defined. If it accepts multiple arguments, you should be able to do:
config_file_a.concat(*L4_LB.build_pool(some_vars))
Note the * which ensures that each element in the array returned by build_pool is passed as an individual argument to concat.
On the other hand, if concat only accepts a single argument you can define a helper function:
def my_concat(what, values)
values.each do |element|
what.concat(element)
end
end
my_concat(config_file_a, L4_LB.build_pool(some_vars))
If you want the result to be concatenated to two different strings, you could use:
def my_concat2(cs, vs)
cs.each_with_index do |c, index|
c.concat(vs[index])
end
end
cs = [config_file_a, config_file_b]
my_concat2(cs, *L4_LB.build_ssl(some_vars))
my_concat2(cs, *L4_LB.build_vip(some_vars))
my_concat2(cs, *L4_LB.build_pool(some_vars))
So I have an empty array and a .yml file. I have managed to output the results of that file with this code
puts YAML.load_file('some_yml_file.yml').inspect
I was wondering, how can I pull out each of the data and store them into an empty array?
Is it
emptyarray = []
YAML.load_file('some_yml_file.yml').inspect do |entry|
emptyarray << entry
end
Any help would be appreciated! Thanks!
YAML.load_file returns a Ruby object corresponding to the type of data structure the YAML represents. If the YAML contains a sequence, YAML.load_file will return a Ruby array. You don't need to do anything further to put the data into an array, because it's already an array:
yaml = <<END
---
- I am
- a YAML
- sequence
END
data = YAML.load(yaml)
puts data.class
# => Array
puts data == ["I am", "a YAML", "sequence"]
# => true
(You'll notice that I used YAML.load to load the data from a string rather than a file, but the result is the same as using YAML.load_file on a file with the same contents.)
If the top-level structure in the YAML is not a sequence (e.g. if it's a mapping, analogous to a Ruby hash), then you will have to do additional work to turn it into an array, but we can't tell you what that code would look like without seeing your YAML.
Change YAML.load_file('some_yml_file.yml').inspect do |entry| with YAML.load_file('some_yml_file.yml').each do |entry| and it should work as you expect it (assuming it's not a string).
If you post a sample of your data structure inside the YAML file and what you wish to extract and put in an array then that would help.
I have an array of hashes that contain sales data in a ruby program and would like to write code that would save this data to a csv file that I can later access or update. Any suggestions on how I can accomplish this? Thanks for any and all help!
You can use Ruby Marshal builtin class for serialization.
# load array from array.bin or initialize new array
array = if File.exists?('array.bin')
File.open('array.bin') do|file|
Marshal.load(file)
end
else
[]
end
# see what's in array
puts array.inspect
# modify array
array << ["test"]
# save into array.bin file
File.open('array.bin','w') do|file|
Marshal.dump(array, file)
end
I think you can consider using class CSV from Ruby standard library.
http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV.html
Here is the code I'm working with:
class Trader
def initialize(ticker ="GLD")
#ticker = ticker
end
def yahoo_data(days=12)
require 'yahoofinance'
YahooFinance::get_historical_quotes_days( #ticker, days ) do |row|
puts "#{row.join(',')}" # this is where a solution is required
end
end
end
The yahoo_data method gets data from Yahoo Finance and puts the price history on the console. But instead of a simple puts that evaporates into the ether, how would you use the preceding code to populate an array that can be later manipulated as object.
Something along the lines of :
do |row| populate_an_array_method(row.join(',') end
If you don't give a block to get_historical_quotes_days, you'll get an array back. You can then use map on that to get an array of the results of join.
In general since ruby 1.8.7 most iterator methods will return an enumerable when they're called without a block. So if foo.bar {|x| puts x} would print the values 1,2,3 then enum = foo.bar will return an enumerable containing the values 1,2,3. And if you do arr = foo.bar.to_a, you'll get the array [1,2,3].
If have an iterator method, which does not do this (from some library perhaps, which does not adhere to this convention), you can use foo.enum_for(:bar) to get an enumerable which contains all the values yielded by bar.
So hypothetically, if get_historical_quotes_days did not already return an array, you could use YahooFinance.enum_for(:get_historical_quotes_days).map {|row| row.join(",") } to get what you want.
What I'm doing is this: have one file as input, another as output. I chose a random line in the input, put it in the output, and then delete it.
Now, I've iterated over the file and am on the line I want. I've copied it to the output file. Is there a way to delete it? I'm doing something like this:
for i in 0..number_of_lines_to_remove
line = rand(lines_in_file-2) + 1 #not removing the first line
counter = 0
IO.foreach("input.csv", "r") { |current_line|
if counter == line
File.open("output.csv", "a") { |output|
output.write(current_line)
}
end
counter += 1
}
end
So, I have current_line, but I'm not sure how to remove it from the source file.
Array.delete_at might do. Given an index, it removes the object at that index, returning the object.
input.csv:
one,1
two,2
three,3
Program:
#!/usr/bin/ruby1.8
lines = File.readlines('/tmp/input.csv')
File.open('/tmp/output.csv', 'a') do |file|
file.write(lines.delete_at(rand(lines.size)))
end
p lines # ["two,2\n", "three,3\n"]
output.csv:
one,1
Here is a randomline class. You create a new randomline object by passing it an input file name and an output file name. You can then call the deleterandom method on that object and pass it a number of lines to delete.
The data is stored internally in arrays as well as being put to file. Currently output is in append mode so if you use the same file it will just add to the end, you could change the a to a w if you wanted to start the file fresh each time.
class Randomline
attr_accessor :inputarray, :outputarray
def initialize(filein, fileout)
#filename = filein
#filein = File.open(filein,"r+")
#fileoutput = File.open(fileout,"a")
#inputarray = []
#outputarray = []
readin()
end
def readin()
#filein.each do |line|
#inputarray << line
end
end
def deleterandom(numtodelete)
numtodelete.times do |num|
random = rand(#inputarray.size)
#outputarray << inputarray[random]
#fileoutput.puts inputarray[random]
#inputarray.delete_at(random)
end
#filein = File.open(#filename,"w")
#inputarray.each do |line|
#filein.puts line
end
end
end
here is an example of it being used
a = Randomline.new("testin.csv","testout.csv")
a.deleterandom(3)
You have to re-write the source-file after removing a line otherwise the modifications won't stick as they're performed on a copy of the data.
Keep in mind that any operation which modifies a file in-place runs the risk of truncating the file if there's an error of any sort and the operation cannot complete.
It would be safer to use some kind of simple database for this kind of thing as libraries like SQLite and BDB have methods for ensuring data integrity, but if that's not an option, you just need to be careful when writing the new input file.