1

I have a YAML file containing:

cat:
  name: Cat
  description: catlike reflexes
dog:
  name: Dog
  description: doggy breath

I want to parse it and break the description into key1 and key2 like so:

cat:
  name: Cat
  description: catlike reflexes
  info:
    key1: catlike
    key2: reflexes
dog:
  name: Dog
  description: doggy breath
  info:
    key1: doggy
    key2: breath

But, for some reason, I cannot do this correctly. What I've tried so far are variations of the code below, which I think I'm overcomplicating:

# to get the original file's data
some_data = YAML.load(File.open("#{Rails.root}/config/some_data.yml"))

new_data = some_data.collect do |old_animal|
  animal = old_animal.second

  if animal && animal["description"]
    new_blocks = Hash.new
    blocks = animal["description"].split(" ")
    new_blocks["key1"] = blocks.first
    new_blocks["key2"] = blocks.second
    animal["info"] = new_blocks
  end
  old_animal.second = animal
  old_animal
end

# to write over the original file
File.write("#{Rails.root}/config/some_data.yml", new_data.to_yaml)
Substantial
  • 6,684
  • 2
  • 31
  • 40
Tom Prats
  • 7,364
  • 9
  • 47
  • 77

1 Answers1

1

You don't say whether or not you can have multiple words in the description, but it's kind of common-sense you would, so I'd do something like this:

require 'yaml'

data = YAML.load(<<EOT)
cat:
  name: Cat
  description: catlike reflexes rules
dog:
  name: Dog
  description: doggy breath
EOT
data # => {"cat"=>{"name"=>"Cat", "description"=>"catlike reflexes rules"}, "dog"=>{"name"=>"Dog", "description"=>"doggy breath"}}

At this point the data from the YAML file is loaded into a hash. Iterate over each hash key/value pair:

data.each do |(k, v)|
  descriptions = v['description'].split
  keys = descriptions.each_with_object([]) { |o, m| m << "key#{(m.size + 1)}" }
  hash = keys.each_with_object({}) { |o, m| m[o] = descriptions.shift }
  data[k]['info'] = hash
end

This is what we got back:

data # => {"cat"=>{"name"=>"Cat", "description"=>"catlike reflexes rules", "info"=>{"key1"=>"catlike", "key2"=>"reflexes", "key3"=>"rules"}}, "dog"=>{"name"=>"Dog", "description"=>"doggy breath", "info"=>{"key1"=>"doggy", "key2"=>"breath"}}}

And what it'd look like if it was output:

puts data.to_yaml
# >> ---
# >> cat:
# >>   name: Cat
# >>   description: catlike reflexes rules
# >>   info:
# >>     key1: catlike
# >>     key2: reflexes
# >>     key3: rules
# >> dog:
# >>   name: Dog
# >>   description: doggy breath
# >>   info:
# >>     key1: doggy
# >>     key2: breath

each_with_object is similar to inject but a little cleaner to use because it doesn't require we return the object we're accumulating into.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • looks great, one problem I have is actually outside of what I asked, but i always have 4 keys and the inside 2 need to be combined. I'm not sure how to edit your code to make it so – Tom Prats Jul 23 '13 at 15:32
  • i sort of figured it out (looks hacky because I hardcode the keys and such) but it works for the one time use! – Tom Prats Jul 23 '13 at 15:58
  • 1
    When you ask a question, it's really important that you accurately state the problem and show the expected data. If you'd specified the REAL needs up front, the answer would have been different and you'd probably not have needed to "hack" anything. Modify the original question so it's accurate, and I'll adjust the answer accordingly. – the Tin Man Jul 23 '13 at 18:02
  • yeah, i understand that. i had just been working on just the basic problem for a while that I forgot what the real problem required – Tom Prats Jul 23 '13 at 22:17