ruby, tk lib. Output from getopenfile - arrays

I am trying to load multiple files via ruby/tk lib and put them into array:
def openFiles
return Tk.getOpenFile( 'title' => 'Select Files',
'multiple' => true,
'defaultextension' => 'csv',
'filetypes' => "{{Comma Seperated Values} {.csv}} {TXT {.txt}} {All files {.*}}")
end
and then in code
filess = TkVariable.new()
button1 = TkButton.new(root){
text 'Open Files'
command (proc {filess.value = openFiles; puts filess; puts filess.class; puts filess.inspect})
}.grid(:column => 1, :row => 1, :sticky => 'we')
The problem is that I can not manage to get the output as array and I do not know if it is possible or I will have to somehow parse the output. Hm? Please help. Thank you.
this is the output, when I click on the button:
C:\file1
C:\file2
TkVariable
#<TkVariable: v00000>
I think it should be: (for the array part)
['C:\file1','C:\file2']

TkVariable implements #to_a, which you can use to convert its value into the Array you want.
button1 = TkButton.new(root) {
text 'Open Files'
command (proc do
filess.value = openFiles
puts filess.to_a.class
puts filess.to_a.inspect
end)
}.grid(:column => 1, :row => 1, :sticky => 'we')
Array
["C:\file1", "C:\file2"]

This worked for me using Ruby 2.2.5 (with Tk 8.5.12) on Windows 7:
require 'tk'
def extract_filenames_as_ruby_array(file_list_string)
::TkVariable.new(file_list_string).list
end
def files_open
descriptions = %w[
Comma\ Separated\ Values
Text\ Files
All\ Files
]
extensions = %w[ {.csv} {.txt} * ]
types = descriptions.zip(extensions).map {|d,e| "{#{d}} #{e}" }
file_list_string = ::Tk.getOpenFile \
filetypes: types,
multiple: true,
title: 'Select Files'
extract_filenames_as_ruby_array file_list_string
end
def lambda_files_open
#lambda_files_open ||= ::Kernel.lambda do
files = files_open
puts files
end
end
def main
b_button_1
::Tk.mainloop
end
# Tk objects:
def b_button_1
#b_button_1 ||= begin
b = ::Tk::Tile::Button.new root
b.command lambda_files_open
b.text 'Open Files'
b.grid column: 1, row: 1, sticky: :we
end
end
def root
#root ||= ::TkRoot.new
end
main
For reference, Tk.getOpenFile is explained in the Tk Commands and Ruby documentation.

Related

How do i copy an array from a lua file

I Want to copy an array from a text file and make another array equal it
so
local mapData = {
grass = {
cam = "hud",
x = 171,
image = "valley/grass",
y = 168,
animated = true
}
}
This is an array that is in Data.lua
i want to copy this array and make it equal another array
local savedMapData = {}
savedMapData = io.open('Data.lua', 'r')
Thank you.
It depends on Lua Version what you can do further.
But i like questions about file operations.
Because filehandlers in Lua are Objects with methods.
The datatype is userdata.
That means it has methods that can directly be used on itself.
Like the methods for the datatype string.
Therefore its easy going to do lazy things like...
-- Example open > reading > loading > converting > defining
-- In one Line - That is possible with methods on datatype
-- Lua 5.4
local savedMapData = load('return {' .. io.open('Data.lua'):read('a'):gsub('^.*%{', ''):gsub('%}.*$', '') .. '}')()
for k, v in pairs(savedMapData) do print(k, '=>', v) end
Output should be...
cam => hud
animated => true
image => valley/grass
y => 168
x => 171
If you need it in the grass table then do...
local savedMapData = load('return {grass = {' .. io.open('Data.lua'):read('a'):gsub('^.*%{', ''):gsub('%}.*$', '') .. '}}')()
The Chain of above methods do...
io.open('Data.lua') - Creates Filehandler (userdata) in read only mode
(userdata):read('a') - Reading whole File into one (string)
(string):gsub('^.*%{', '') - Replace from begining to first { with nothing
(string):gsub('%}.*$', '') - Replace from End to first } with nothing

Ruby Array of Hash parsing

I have a yaml file in the format:
parameters:
- param_name: age
requires:
- name
- param_name: height
requires:
- name
Based on this format I would like to accept a hash of keys and values and determine if the combination of keys and values is valid. For example based on the above example if someone submitted a hash with the values:
{'age' => 15, 'height' => '6ft'}
it would be considered invalid since the parameter name is required. So a valid submission would look like
{'age' => 15, 'height' => '6ft', 'name' => 'Abe Lincoln'}.
Essentially what I want is this:
For each parameter object, if it has a requires array underneath it. Check all parameter param_names for elements in that array, if any are missing exit.
I have a very ugly double loop that checks for this but I want to tighten the code up. I think I can use blocks in order to validate the data I need. Here is what I have come up with so far:
require 'yaml'
requirements = YAML.load_file('./require.yaml')
require_fields = Array.new
requirements['parameters'].each do |param|
require_fields.concat(param['require']) if param.has_key? 'require'
end
require_fields.each do |requirement|
found = false
requirements['parameters'].each do |param|
if param['param_name'] == requirement
found = true
end
end
abort "#{requirement} is a required field" unless found
end
You can clean this up a lot if you make it more idiomatic Ruby:
require 'yaml'
requirements = YAML.load_file('./require.yaml')
require_fields = requirements['parameters'].select do |param|
param.has_key?('require')
end.map do |param|
param['require']
end
require_fields.each do |requirement|
found = requirements['parameters'].any? do |param|
param['param_name'] == requirement
end
abort "#{requirement} is a required field" unless found
end
You could also do this:
require_fields = requirements['parameters'].map do |param|
param['require']
end.compact
Where that's probably good enough so long as your require is either something or nil.
You could also transform that input YAML into a simple hash structure of dependencies:
dependencies = requirements.map do ||
[ param['param_name'], param['requires'] ]
end.to_h
Then you can test really easily:
dependencies.each do |name, requirements|
found = requirements.find do |required_name|
!dependencies[required_name]
end
abort "#{found} is a required field" unless found
end
This is a really rough adaptation of your code, but I hope it gives you some ideas.
I would go with subsequent checks, collecting errors and reporting all at once:
req = YAML.load 'parameters:
- param_name: age
requires:
- name
- param_name: height
requires:
- name'
input = {'age' => 15, 'height' => '6ft'}
req['parameters'].each_with_object([]) do |req, err|
next unless input[req['param_name']] # nothing to check
missed = req['requires'].reject { |param| input[param] }
errors = missed.map do |param|
[req['param_name'], param].join(' requires ')
end
err.concat(errors)
end
#⇒ ["age requires name", "height requires name"]
Or, chaining:
req['parameters'].each_with_object(Hash.new { |h, k| h[k] = [] }) do |req, err|
next unless input[req['param_name']] # nothing to check
req['requires'].each do |param|
err[param] << req['param_name'] unless input[param]
end
end.map do |missing, required|
"Missing #{missing} parameter, required for: [#{required.join(', ')}]"
end.join(',')
#⇒ "Missing name parameter, required for: [age, height]"

putting a hash value into an array in ruby

I am trying to insert a list of account numbers into an array from an json return, I turned the json return into an hash, but I cannot for some reason insert the values into an array. I checked the hash locations on irb, and it gets the account number, for an example the location my_hash["aws_accounts"][0]["owner_id"] will get me the first account number and my_hash["aws_accounts"][0]["status"]["level"] will get me the status of the first account.
I essentially want to iterate through all the accounts and store the account number if its respective status color is "yellow".
HERE IS MY CODE:
require 'json'
require 'rest-client'
j = RestClient.get 'https://chapi.cloudhealthtech.com/v1/aws_accounts?api_key=###&page=1&per_page=100'
my_hash = JSON.parse(j)
accnt_size = my_hash["aws_accounts"].size
intaccntsize = accnt_size.to_i
account_number_array = Array.new
x = 0
for accnt_iteration in x..intaccntsize do
puts accnt_iteration
if my_hash["aws_accounts"][accnt_iteration]["status"]["level"] == "yellow"
account_number_array.push(my_hash["aws_accounts"][accnt_iteration]["owner_id"])
end
end
HERE IS THE ERROR MESSAGE
in `block in <main>': undefined method `[]' for nil:NilClass (NoMethodError)
from C:/Users/----/Desktop/-----/ruby_aws_sdk.rb:12:in `each'
from C:/Users/------/Desktop/-------/ruby_aws_sdk.rb:12:in `<main>'
any suggestions will help. thanks.
The actual solution to your problem is to use the 3 dot range instead of 2 dot (3 dots is end-exclusive and 2 dots is end-inclusive) as seen in the following (this uses the same my_hash as my second code block below):
for x in 0..my_hash["aws_accounts"].size do
puts x
end
# 0
# 1
# 2
=> 0..2
my_hash["aws_accounts"][2]
=> nil
for x in 0...my_hash["aws_accounts"].size do
puts x
end
# 0
# 1
=> 0...2
my_hash["aws_accounts"][1]
=> {"owner_id"=>2, "status"=>{"level"=>"orange"}}
Instead of getting the number of accounts and trying to access them through their index, I would just iterate over the accounts on their own. Here's a quick sample, with what I believe (based on your description) are the relevant pieces of my_hash and your expected output.
my_hash = {
"aws_accounts" => [
{ "owner_id" => 1, "status" => { "level" => "yellow" } },
{ "owner_id" => 2, "status" => { "level" => "orange" } }
]
}
account_number_array = []
my_hash["aws_accounts"].each do |account|
if account["status"]["level"] == "yellow"
account_number_array << account["owner_id"]
end
end
puts account_number_array.inspect
# => [1]

Ruby - Array of Hashes to CSV

I have collected some data and saved it to an array of hashes in the form of:
info = [
{'Name' => 'Alex', 'Tel' => 999, 'E-mail' => "bla#bla.com"},
{'Name' => 'Ali', 'Tel' => 995, 'E-mail' => "ali#bla.com"}
# ...
]
But not all information is always there:
{'Name' => 'GuyWithNoTelephone', 'E-mail' => "poor#bla.com"}
I want to turn this information into a CSV file. Here is what I tried:
def to_csv (info)
CSV.open("sm-data.csv", "wb") do |csv|
csv << ["Name", "Tel", "E-mail"]
info.each do |person|
csv << person.values
When I try this, the format of the table is not correct, and say, if a telephone number is missing, then the e-mail of that person appears at telephone column.
How can I selectively write this info to the CSV file, i.e how can I tell to skip columns if a value is missing?
But sadly when I try this, the format of the table is not correct, and say, if a telephone number is missing, then the e-mail of that person appears at telephone column.
That's because you are omitting the telephone number in that case, providing just 2 of the 3 values. To fix this, you simply have to provide all 3 values, even if they don't exist in the hash:
csv << ['Name', 'Tel', 'E-mail']
info.each do |person|
csv << [person['Name'], person['Tel'], person['E-Mail']]
end
or via Hash#values_at:
csv << ['Name', 'Tel', 'E-mail']
info.each do |person|
csv << person.values_at('Name', 'Tel', 'E-Mail')
end
For:
{'Name' => 'GuyWithNoTelephone', 'E-mail' => "poor#bla.com"}
this results in:
csv << ['GuyWithNoTelephone', nil, 'poor#bla.com']
which generates this output: (note the two commas, denoting an empty field in-between)
"GuyWithNoTelephone,,poor#bla.com\n"
Try this,
def to_csv(csv_filename="sm-data.csv")
# Get all unique keys into an array:
keys = info.map(&:keys).inject(&:|)
CSV.open(csv_filename, "wb") do |csv|
csv << keys
info.each do |hash|
# fetch values at keys location, inserting null if not found.
csv << hash.values_at(*keys)
end
end
end
Simple as this:
path = "data/backtest-results.csv"
CSV.open(path, "wb") do |csv|
csv << ["Asset", "Strategy", "Profit"]
result_array.each do |p|
csv << p.map { |key, value| value }
end
end
use "(File.file?(path) ? "ab" : "wb")" rather than "wb" if you want to continue adding as the new data comes

Finding text in array Ruby

I have a code
require 'rubygems'
conf_array = []
File.open("C:/My Program Files/readme.txt", "r").each_line do |line|
conf_array << line.chop.split("\t")
end
a = conf_array.index{|s| s.include?("server =")}
puts a
and it doesn't display the index of item. Why?
Array looks like
conf_array = [
["# This file can be used to override the default puppet settings."],
["# See the following links for more details on what settings are available:"],
["# - docs.puppetlabs.com/puppet/latest/reference/config_important_settings.html"],
["# - docs.puppetlabs.com/puppet/latest/reference/config_about_settings.html"],
["# - docs.puppetlabs.com/puppet/latest/reference/config_file_main.html"],
["# - docs.puppetlabs.com/references/latest/configuration.html"], ["[main]"],
["server = server.net.pl"],
["splay = true"],
["splaylimit = 1h"],
["wiatforcert = 30m"],
["http_connect_timeout = 2m"],
["http_read_timeout = 30m"],
["runinterval = 6h"],
["waitforcert = 30m"]
]
And next how to display that item? I mean a = conf_array[#{a}] says syntax error.
I tried also
new_array = []
new_array = conf_array.select! {|s| s.include?("server =")}
and it again does't display found item. Any suggestion?
Perfect use case for Enumerable#grep
File.open("C:/My Program Files/readme.txt", "r")
.each_line
# no need to .flat_map { |l| l.split(/\t/) }
.grep /server =/
#⇒  ["server = server.net.pl"]
The problem is that you don't call String#include?, but Array#include? :
["server = something.pl"].include?('server = ')
# false
"server = something.pl".include?('server = ')
# true
Remove the split("\t").
To read the file into an array, you can just use :
conf_array = File.readlines("C:/My Program Files/readme.txt")
or
conf_array = File.readlines("C:/My Program Files/readme.txt").map(&:chomp)

Resources