Replace Sibling Value in YAML file using Ruby - ruby

I have a yaml file with hundrends of stores. Here is an example:
- store_number: "1"
title_tag: "Title tag for store 1"
meta_description: "Description of store 1"
store_details: "Details of store 1"
find_us:
- store_number: "2"
title_tag: "Title tag for store 2"
meta_description: "Description of store 2"
store_details: "Details of store 2"
find_us:
I need to loop through each of these, compare the store number, and if it matches, add a value to the find_us key.
I've been able to search the file using this:
stores_file = YAML.load_file('_data/stores.yml')
stores_file.select do |store|
if store['store_number'] == '1'
puts store
end
end
This prints as expected:
{"store_number"=>"1", "title_tag"=>"Title tag for store 1", "meta_description"=>"Description of store 1", "store_details"=>"Details of store 1", "find_us"=>nil}
I have two problems, I don't know how to find a sibling (find_us in this case), and I don't know how to write to the find_us key.

Use YAML::Store
With just a little refactoring, you can essentially treat your YAML file as a transactional flat-file database using YAML::Store from the Ruby Standard Library. In order to iterate, though, you will probably need to ensure you have a single root in your YAML data store, rather than an array of hashes as you appear to have now. For example, given:
# _data/stores.yml
---
stores:
- store_number: "1"
title_tag: "Title tag for store 1"
meta_description: "Description of store 1"
store_details: "Details of store 1"
find_us:
- store_number: "2"
title_tag: "Title tag for store 2"
meta_description: "Description of store 2"
store_details: "Details of store 2"
find_us:
require 'yaml/store'
#db = YAML::Store.new '_data/stores.yml'
def update_find_us_for store_number, value
# coerce arguments to String objects
store_number, value = [store_number, value].map &:to_s
# completed transactions write the results back to the
# YAML data store for you
#db.transaction do
#db['stores'].each do |store|
store['find_us'] = value if store['store_number'] == store_number
end
end
end
update_find_us_for 1, 'foo'
Validate Expected Results
Validate in IRB
If you ran the code above in irb, you can validate that you made the changes you expected with pp _.first, which should print:
{"store_number"=>"1",
"title_tag"=>"Title tag for store 1",
"meta_description"=>"Description of store 1",
"store_details"=>"Details of store 1",
"find_us"=>"foo"}
Validate Programmatically
You can get at the changed data through instance variables, too. For example:
#db.instance_variable_get(:#table)['stores'].first.fetch 'find_us'
#=> "foo"
Validate at Shell Prompt
If you'd rather validate the stores.yml file directly, then from the Bash shell:
grep -A4 "store_number: '1'" _data/stores.yml
- store_number: '1'
title_tag: Title tag for store 1
meta_description: Description of store 1
store_details: Details of store 1
find_us: foo

When you load the YAML you end up with an array of hashes:
stores_file.class #=> Array
stores_file.first.class #=> Hash
So, you can loop, change the values according to your needs and save back the file.
Here I'm using a different file as destination.
stores_file.each do |store|
if store['store_number'] == '1'
store['find_us'] = 'here'
end
end
destination_file = 'SO00123_.yml'
File.open(destination_file,"w") do |file|
file.write stores_file.to_yaml
end
The resulting file is like:
---
- store_number: '1'
title_tag: Title tag for store 1
meta_description: Description of store 1
store_details: Details of store 1
find_us: here
- store_number: '2'
title_tag: Title tag for store 2
meta_description: Description of store 2
store_details: Details of store 2
find_us:

Create a test YAML file
str =<<~END
- store_number: "1"
title_tag: "Title tag for store 1"
meta_description: "Description of store 1"
store_details: "Details of store 1"
find_us:
- store_number: "2"
title_tag: "Title tag for store 2"
meta_description: "Description of store 2"
store_details: "Details of store 2"
find_us:
END
FILE_NAME = 't.yml'
File.write(FILE_NAME, str)
#=> 302
Define parameters
target_store = "2"
find_us_value = "whoa"
Read the YAML file and convert it to an array of hashes
require 'yaml'
arr = YAML.load(File.read(FILE_NAME))
#=> [{"store_number"=>"1", "title_tag"=>"Title tag for store 1",
# "meta_description"=>"Description of store 1",
# "store_details"=>"Details of store 1", "find_us"=>nil},
# {"store_number"=>"2", "title_tag"=>"Title tag for store 2",
# "meta_description"=>"Description of store 2",
# "store_details"=>"Details of store 2", "find_us"=>nil}]
Determine the index of the hash in arr whose value for the key "store_number" equals the value of target_store
i = arr.index { |h| h["store_number"] == target_store }
#=> 1
Update the hash arr[i]
After confirming that i is not nil,
arr[i]["find_us"] = find_us_value
#=> "whoa"
Confirm:
arr
#=> [{"store_number"=>"1", "title_tag"=>"Title tag for store 1",
# "meta_description"=>"Description of store 1",
# "store_details"=>"Details of store 1", "find_us"=>nil},
# {"store_number"=>"2", "title_tag"=>"Title tag for store 2",
# "meta_description"=>"Description of store 2",
# "store_details"=>"Details of store 2", "find_us"=>"whoa"}]
Convert the updated array arr to a YAML string and write that string to file
File.write(FILE_NAME, arr.to_yaml)
#=> 299
Confirm:
puts File.read(FILE_NAME)
---
- store_number: '1'
title_tag: Title tag for store 1
meta_description: Description of store 1
store_details: Details of store 1
find_us:
- store_number: '2'
title_tag: Title tag for store 2
meta_description: Description of store 2
store_details: Details of store 2
find_us: whoa
Note that YAML uses three dashes (“---”) to separate directives from the document content. This also serves to signal the start of a document if no directives are present.

Related

Sort an array that are merged together

I have 2 strings that I made into an array to sort then to convert back into a string. But, in my test in my response.body The given string is sorted differently. I have a method that takes 2 strings and removes the headers from both and mergers the array and sorts it. But getting different results. How can I get the desired results of the below response.body string
string1 = "Category Name,Code,Enabled?,Category Hidden?\nPRESENT AVAIALBLE,PRESENT AVAILABLE,No,No,\nBUG AVAILABLE,BUG,No,No,\nBUG,BUG,No,No,\nPRESENT,PRESENT,No,No\n"
string2 = "Category Name,Code,Enabled?,Category Hidden?\nBUG,BUG,No,No,\nBUG AVAILABLE,BUG,No,No,\nEXAMPLE 1,EXAMPLE 1,Yes,No,\nEXAMPLE 2,EXAMPLE 2,Yes,No,\nPRESENT AVAIALBLE,PRESENT AVAILABLE,No,No,\nPRESENT,PRESENT,No,No\n"
how would I get that array to be sorted as the response.body string before inserting the header "Category Name,Code,Enabled?,Category Hidden?"
response.body string
"Category Name,Code,Enabled?,Category Hidden?
BUG,BUG,No,No,
BUG AVAILABLE,BUG,No,No,
EXAMPLE 1,EXAMPLE 1,No,No,
EXAMPLE 2,EXAMPLE 2,Yes,No,
PRESENT,PRESENT,No,No"
PRESENT AVAIALBLE,PRESENT AVAILABLE,No,No"
My output from method
"Category Name,Code,Enabled?,Category Hidden?
BUG AVAILABLE,BUG,No,No,
BUG,BUG,No,No,
EXAMPLE 1,EXAMPLE 1,No,No,
EXAMPLE 2,EXAMPLE 2,Yes,No,
PRESENT AVAIALBLE,PRESENT AVAILABLE,No,No,
PRESENT,PRESENT,No,No"
method I wrote
def merge(string1, string2)
string1 = string1.split("\n") # Split into array.
headers = string1.first # Get headers.
string1.shift # Remove headers.
string2 = string2.split("\n")[1..-1] # Remove headers.
final = (string1 + string2).sort.unshift(headers).join("\n") + "\n" # Create merged sorted string.
end
desired result wanted
"Category Name,Code,Enabled?,Category Hidden?
BUG,BUG,No,No,
BUG AVAILABLE,BUG,No,No,
EXAMPLE 1,EXAMPLE 1,No,No,
EXAMPLE 2,EXAMPLE 2,Yes,No,
PRESENT,PRESENT,No,No"
PRESENT AVAIALBLE,PRESENT AVAILABLE,No,No"
Here are three ways to do that.
I assume you are given two strings:
str1 = "Category Name,Code,Enabled?,Category Hidden?\nBUG,BUG,No,No\nEXAMPLE 1,EXAMPLE 1,No,No\nPRESENT,PRESENT,No,No"
str2 = "Category Name,Code,Enabled?,Category Hidden?\nBUG AVAILABLE,BUG,No,No\nEXAMPLE 2,EXAMPLE 2,Yes,No\nPRESENT AVAILABLE,PRESENT AVAILABLE,No,No"
Then
header, *body1 = str1.split("\n")
#=> ["Category Name,Code,Enabled?,Category Hidden?",
# "BUG,BUG,No,No",
# "EXAMPLE 1,EXAMPLE 1,No,No",
# "PRESENT,PRESENT,No,No"]
so
header
#=> "Category Name,Code,Enabled?,Category Hidden?"
body1
#=> ["BUG,BUG,No,No",
# "EXAMPLE 1,EXAMPLE 1,No,No",
# "PRESENT,PRESENT,No,No"]
and
_, *body2 = str2.split("\n")
#=> ["Category Name,Code,Enabled?,Category Hidden?",
# "BUG AVAILABLE,BUG,No,No",
# "EXAMPLE 2,EXAMPLE 2,Yes,No",
# "PRESENT AVAILABLE,PRESENT AVAILABLE,No,No"]
so
_ #=> "Category Name,Code,Enabled?,Category Hidden?"
body2
#=> ["BUG AVAILABLE,BUG,No,No",
# "EXAMPLE 2,EXAMPLE 2,Yes,No",
# "PRESENT AVAILABLE,PRESENT AVAILABLE,No,No"]
We may then compute the desired string.
str = [header].concat(body1.zip(body2).flatten).join("\n")
#=> "Category Name,Code,Enabled?,Category Hidden?\nBUG,BUG,No,No\nBUG AVAILABLE,BUG,No,No\nEXAMPLE 1,EXAMPLE 1,No,No\nEXAMPLE 2,EXAMPLE 2,Yes,No\nPRESENT,PRESENT,No,No\nPRESENT AVAILABLE,PRESENT AVAILABLE,No,No"
which when displayed appears as follows.
puts str
Category Name,Code,Enabled?,Category Hidden?
BUG,BUG,No,No
BUG AVAILABLE,BUG,No,No
EXAMPLE 1,EXAMPLE 1,No,No
EXAMPLE 2,EXAMPLE 2,Yes,No
PRESENT,PRESENT,No,No
PRESENT AVAILABLE,PRESENT AVAILABLE,No,No
See Array#concat, Array#zip, Array#flatten and Array#join.
The variable _ in _, *body2 = str2.split("\n") is so-named to tell the reader that it is not used in subsequent calculations. Sometimes might write _header, *body2 = str2.split("\n") to convey the same message.
Here is a second way of doing that by treating the strings as comma-delimited CSV strings.
require 'csv'
arr1 = CSV.parse(str1)​
#=> [["Category Name", "Code", "Enabled?", "Category Hidden?"],
# ["BUG", "BUG", "No", "No"],
# ["EXAMPLE 1", "EXAMPLE 1", "No", "No"],
# ["PRESENT", "PRESENT", "No", "No"]],
arr2 = CSV.parse(str2)
#=> [["Category Name", "Code", "Enabled?", "Category Hidden?"],
# ["BUG AVAILABLE", "BUG", "No", "No"],
# ["EXAMPLE 2", "EXAMPLE 2", "Yes", "No"],
# ["PRESENT AVAILABLE", "PRESENT AVAILABLE", "No", "No"]]
Then
str = CSV.generate do |csv|
csv << arr1.shift
arr2.shift
until arr2.empty? do
csv << arr1.shift
csv << arr2.shift
end
end
#=> "Category Name,Code,Enabled?,Category Hidden?\nBUG,BUG,No,No\nBUG AVAILABLE,BUG,No,No\nEXAMPLE 1,EXAMPLE 1,No,No\nEXAMPLE 2,EXAMPLE 2,Yes,No\nPRESENT,PRESENT,No,No\nPRESENT AVAILABLE,PRESENT AVAILABLE,No,No\n"
puts str
Category Name,Code,Enabled?,Category Hidden?
BUG,BUG,No,No
BUG AVAILABLE,BUG,No,No
EXAMPLE 1,EXAMPLE 1,No,No
EXAMPLE 2,EXAMPLE 2,Yes,No
PRESENT,PRESENT,No,No
PRESENT AVAILABLE,PRESENT AVAILABLE,No,No
See CSV::parse and CSV::generate.
This can also be done without converting the strings to arrays, manipulating those arrays to form a single array and then converting the single array back to a string.
arr = [str1, str2]
str_indices = 0..str1.count("\n")
arr_indices = 0..arr.size-1
idx_begin = Array.new(arr.size, 0)
puts str_indices.each_with_object("") do |i, str|
arr_indices.each do |j|
idx_end = arr[j].index(/(?:\n|\z)/, idx_begin[j])
s = arr[j][idx_begin[j]..idx_end]
s << "\n" unless s[-1] == "\n" || (i == str_indices.last && j == arr_indices.last)
str << s unless i.zero? && j > 0
idx_begin[j] = idx_end + 1
end
end
Category Name,Code,Enabled?,Category Hidden?
BUG,BUG,No,No
BUG AVAILABLE,BUG,No,No
EXAMPLE 1,EXAMPLE 1,No,No
EXAMPLE 2,EXAMPLE 2,Yes,No
PRESENT,PRESENT,No,No
PRESENT AVAILABLE,PRESENT AVAILABLE,No,No
The regular expression /(?:\n|\z)/ matches a newline character (\n) or (|) the end of the string (\z).
See the form of String#index that takes an optional second argument that specifies the string index where the search is to begin.

How to get the last character of a string in an array [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I am trying to get the last character of strings in an array like this:
Down = ["up 1", "up 2", "up 3"]
I want to receive the "1", "2", and "3", and make a conditional statement like for.each based on the result, which would run this logic:
If the final character is "1", let #x = 0
If the final character is "2", let #x = 1
If the final character is "3", let #x = 2
your variable name should not start with Capital letter,
> down = ["up 1", "up 2", "up 3"]
> numbers_ary = down.map{|s|s[-1]}
#=> ["1", "2", "3"]
OR
> numbers_ary = down.map{|s| s.chars.last}
#=> ["1", "2", "3"]
make a conditional statement like for.each based on the result
If you make a loop of an array and assign value to same variable #x using any logic, it will be override and assign last value.
But as per your concern of logic, can do this way:
num = numbers_ary.sample #sample is used to pick random element from numbers_ary
case num
when "1", "2", "3"
#x = num.to_i - 1
end
The answer below has a rails dependency. To remove that dependency, change down.last to down.chars.last or down[-1]
Down.each do |down|
case(down.last)
when '1'
#x = 0
when '2'
#x = 1
when '3'
#x =2
end
end
EDIT: I am adding the output I got after running this on the irb console for the people who are adding comment about me running this before I posted this answer and downvoting this answer without any clear comments.
irb(main):014:0> Down=["up 1", "up 2", "up 3"]
=> ["up 1", "up 2", "up 3"]
irb(main):015:0> Down.each do |down|
irb(main):016:1* case(down.last)
irb(main):017:2> when '1'
irb(main):018:2> #x = 0
irb(main):019:2> puts "word: #{down}, last:#{down.last}, x: #{#x}"
irb(main):020:2> when '2'
irb(main):021:2> #x = 1
irb(main):022:2> puts "word: #{down}, last:#{down.last}, x: #{#x}"
irb(main):023:2> when '3'
irb(main):024:2> #x = 2
irb(main):025:2> puts "word: #{down}, last:#{down.last}, x: #{#x}"
irb(main):026:2> end
irb(main):027:1> end
word: up 1, last:1, x: 0
word: up 2, last:2, x: 1
word: up 3, last:3, x: 2
=> ["up 1", "up 2", "up 3"]

Ruby REGEX split, any issues with the code

I am a rookie in Regex for Ruby. I read some tutorials and evaluated a piece of code.
Please let me know if I can do it in a better way.
Here is my text which needs to be split at {iwsection(*)} and {{usersection}}
t='{{iwsection(1)}}
This has some sample text 1 - line 1
This has some sample text 1 - line 2
{{iwsection(2)}}
This has some sample text 2
{{iwsection(3)}}
This has some sample text 3
{{usersection}}
This is a user section.
This has some sample text
This has some sample text'
Here is the ruby regex code I was able to manage.
t.split(/^({{[i|u][wsection]\w*...}})/)
Thank You.
The Desired Output :
A array as,
[ '{{iwsection(1)}}', 'This has some sample text 1\nThis has some sample text 1 - line 2',
'{{iwsection(2)}}', 'This has some sample text 2',
'{{iwsection(3)}}', 'This has some sample text 3',
'{{usersection}}', 'This is a user section\nThis has some sample text\nThis has some sample text.']
With this I will build a Hash,
{
'{{iwsection(1)}}' => 'This has some sample text 1\nThis has some sample text 1 - line 2',
'{{iwsection(2)}}' => 'This has some sample text 2',
'{{iwsection(3)}}' => 'This has some sample text 3',
'{{usersection}}' => 'This is a user section\nThis has some sample text\nThis has some sample text.'
}
Edit: .....
The code.
section_array = text.chomp.split(/\r\n|\n/).inject([]) do |a, v|
if v =~ /{{.*}}/
a << [v.gsub(/^{{|}}$/, ""), []]
else
a.last[1] << v
end
a
end.select{ |k, v| (k.start_with?("iwsection") || k.start_with?("usersection")) }.map{ |k, v| ["{{#{k}}}", v.join("\n")] }
Using String#scan:
> t.scan(/{{([^}]*)}}\r?\n(.*?)\r?(?=\n{{|\n?$)/)
=> [["iwsection(1)", "This has some sample text 1"], ["iwsection(2)", "This has some sample text 2"], ["iwsection(3)", "This has some sample text 3"], ["usersection", "This is a user section."]]
> h = t.scan(/{{([^}]*)}}\r?\n(.*?)\r?(?=\n{{|\n?$)/).to_h
=> {"iwsection(1)"=>"This has some sample text 1", "iwsection(2)"=>"This has some sample text 2", "iwsection(3)"=>"This has some sample text 3", "usersection"=>"This is a user section."}
> h.values
=> ["This has some sample text 1", "This has some sample text 2", "This has some sample text 3", "This is a user section."]
> h.keys
=> ["iwsection(1)", "iwsection(2)", "iwsection(3)", "usersection"]
> h["usersection"]
=> "This is a user section."
Update:
#!/usr/bin/env ruby
t = "{{iwsection(1)}}\nThis has some sample text 1 - line 1\nThis has some sample text 1 - line 2\n{{iwsection(2)}}\nThis has some sample text 2\n{{iwsection(3)}}\nThis has some sample text 3\nThis has some sample text\nThis has some sample text\n{{usersection}}\nThis is a user section.\nThis has some sample text\nThis has some sample text"
h = t.chomp.split(/\n/).inject([]) do |a, v|
if v =~ /{{.*}}/
a << [v.gsub(/^{{|}}$/, ""), []]
else
a.last[1] << v
end
a
end.select{ |k, v| k.start_with? "iwsection" or k === "usersection" }.map{ |k, v| [k, v.join("\n")] }.to_h
puts h.inspect
Output:
{"iwsection(1)"=>"This has some sample text 1 - line 1\nThis has some sample text 1 - line 2", "iwsection(2)"=>"This has some sample text 2", "iwsection(3)"=>"This has some sample text 3\nThis has some sample text\nThis has some sample text", "usersection"=>"This is a user section.\nThis has some sample text\nThis has some sample text"}
You can do that like this:
t.split(/{{iwsection\(\d+\)}}|{{usersection}}/)
#=> ["", "\n This has some sample text 1\n ",
# "\n This has some sample text 2\n ",
# "\n This has some sample text 3\n ",
# "\n This is a user section."]
That's what you asked for, but if you want to clean that up, add .map(&:strip):
t.split(/{{iwsection\(\d+\)}}|{{usersection}}/).map(&:strip).map(&:strip)
#=> ["", "This has some sample text 1", "This has some sample text 2",
# "This has some sample text 3", "This is a user section."]
You may not want the empty string at offset zero, but that's how String#split works when you are splitting on a substring that is at the beginning of the string. Suppose the string were instead:
t =
'Some text here{{iwsection(1)}}
This has some sample text 1
{{iwsection(2)}}
This has some sample text 2'
t.split(/{{iwsection\(\d+\)}}|{{usersection}}/).map(&:strip).map(&:strip)
#=> ["Some text here", "This has some sample text 1",
# "This has some sample text 2"]
Here you want "Some text here", so you can't just delete the first element of the array.
Additional requirements
To satisfied your added requirement, you could do this:
t='{{iwsection(1)}}
Text 1 - line 1
Text 1 - line 2
{{iwsection(2)}}
Text 2
{{iwsection(3)}}
Text 3
{{usersection}}
User section.
Text
Text'
h = t.scan(/(?:{{iwsection\(\d+\)}}|{{usersection}})/)
.zip(t.split(/{{iwsection\(\d+\)}}|{{usersection}}/)[1..-1])
.map { |s1,s2| [s1, s2.strip
.lines
.map(&:strip)
.join("\n")] }
.to_h
#=> {"{{iwsection(1)}}"=>"Text 1 - line 1\nText 1 - line 2",
# "{{iwsection(2)}}"=>"Text 2",
# "{{iwsection(3)}}"=>"Text 3",
# "{{usersection}}"=>"User section.\nText\nText"}
Note that this formatting may not be understood by IRB or PRY, but will work fine from the command line.
Explanation
a = t.scan(/(?:{{iwsection\(\d+\)}}|{{usersection}})/)
#=> ["{{iwsection(1)}}", "{{iwsection(2)}}", "{{iwsection(3)}}", "{{usersection}}"]
b = t.split(/{{iwsection\(\d+\)}}|{{usersection}}/)
#=> ["", "\n Text 1 - line 1\n Text 1 - line 2\n ",
# "\n Text 2\n ", "\n Text 3\n ",
# "\n User section.\n Text\n Text"]
c = b[1..-1]
#=> ["\n Text 1 - line 1\n Text 1 - line 2\n ",
# "\n Text 2\n ", "\n Text 3\n ",
# "\n User section.\n Text\n Text"]
h = a.zip(c)
#=> [["{{iwsection(1)}}", "\n Text 1 - line 1\n Text 1 - line 2\n "],
# ["{{iwsection(2)}}", "\n Text 2\n "],
# ["{{iwsection(3)}}", "\n Text 3\n "],
# ["{{usersection}}", "\n User section.\n Text\n Text"]]
d = h.map { |s1,s2| [s1, s2.strip
.lines
.map(&:strip)
.join("\n")] }
#=> [["{{iwsection(1)}}", "Text 1 - line 1\nText 1 - line 2"],
# ["{{iwsection(2)}}", "Text 2"], ["{{iwsection(3)}}", "Text 3"],
# ["{{usersection}}", "User section.\nText\nText"]]
d.to_h
#=> {"{{iwsection(1)}}"=>"Text 1 - line 1\nText 1 - line 2",
# "{{iwsection(2)}}"=>"Text 2",
# "{{iwsection(3)}}"=>"Text 3",
# "{{usersection}}"=>"User section.\nText\nText"}

Ruby String concatenation

I have an array
books = ["Title 1", "Title 2", "Title 3"]
I need to iterate through this array and get a variable like this:
#books_read = "Title 1 \n Title 2 \n Title 3"
I tried this bit of code:
books.each do |book|
#books_read += "#{book} \n"
end
puts #books_read
But, the + operator does not concatenate the strings. Any leads on this please.
Cheers!
You can use Array#join: books.join(" \n ").
join(sep=$,) → str
Returns a string created by converting each element of the array to a
string, separated by sep.
You can use join: books.join(" \n ")

Read data from yaml file and produce an array in ruby

I have the following data in a yaml file -
---
- :Subject_list
Subject 1:
:Act 1: A
:Act 2: B
Subject 2:
:Skill 1:
:Act 1: B
:Act 2: B
:Skill 2:
:Act 1: B
I need to read data from this file and and generate an output which is given below -
For subject 1 it will be like this as it has no skill level. Meaning the first element of the array is null.
["","Act 1", "A"], ["","Act 2", "B"]
For the second subject it will be like this -
["Skill 1","Act 1", "B"], ["","Act 2" "B"],["Skill 2","Act 1", "B"]
I am using these values to generate a prawn pdf table. Any help is greatly appreciated.
I tried doing this -
data=YAML::load(File.read("file.yaml"));
subject = data[:Subject_list]
sub_list =["Subject 1", "Subject 2"]
sub_list.each do |sub|
sub_data = []
sub_data = subject["#{sub}"]
# I convert the list symbol to an array, so i can loop through the sub activities.
#I need some direction here as how to check whether the symbol will be a skill or activity
end
Cheers!!
First off, your yaml file is not correct YAML, you cannot have keys like that, if you have space or weirdness in them you need to quote them, and what's up with the : at the beginning?
"Subject_list":
"Subject 1":
"Act 1": A
"Act 2": B
"Subject 2":
"Skill 1":
"Act 1": B
"Act 2": B
"Skill 2":
"Act 1": B
Then you need to load the file properly. You call the method load_file on the YAML module. No :: for method access in ruby afaik.
require 'yaml'
data = YAML.load_file "file.yaml"
subject = data["Subject_list"]
require 'pp'
subject.each do |s|
item = s.last
if item.keys.first =~ /Skill/
pp item.keys.inject([]) { |memo,x| item[x].map { |i| memo << i.flatten.unshift(x) } ; memo}
else
pp item.map { |k,v| ["", k, v] }
end
end
When building up a YAML file for data, especially a complex data structure, I let YAML generate it for me. Then I tweak as necessary:
require 'yaml'
require 'pp'
foo = ["Skill 1","Act 1", "B"], ["","Act 2" "B"],["Skill 2","Act 1", "B"]
puts foo.to_yaml
When I run that code I get this output:
---
- - Skill 1
- Act 1
- B
- - ""
- Act 2B
- - Skill 2
- Act 1
- B
You can prove the data is correctly generated by having YAML generate, then immediately parse the code and show what it looks like as the returned structure, letting you compare it, and by an equality check:
bar = YAML.load(foo.to_yaml)
pp bar
puts "foo == bar: #{ foo == bar }"
Which would output:
[["Skill 1", "Act 1", "B"], ["", "Act 2B"], ["Skill 2", "Act 1", "B"]]
foo == bar: true

Resources