YANQ: Nesting in YAML

(basi) #1

Hello,

How does one tell YAML to use an array substructure rather than a hash?
Given the following YAML data:

an:
  - at sf
  - fc di
  - ps:
      - na vb
      - ex anEx
  - ps:
      - na no
      - ex otherEx
      - pr pro
in:
  - at sf
  - fc lo

YAML gives it the structure: {[{[...]}]}, that is, Hash embeds Array
which embeds another Hash which embeds Array, as below (output
reformatted for clarity).

{"an"=>
   [ "at sf",
     "fc di",
    {"ps"=>
       ["na vb",
        "ex anEx"]},
    {"ps"=>
       ["na no",
        "ex otherEx",
        "pr pro"]}],
  "in"=>
    ["at sf",
     "fc lo"]
}

I feel that the structure below: {[[[...]]]}, that is, hash embeds
array which embeds another array makes it easier to pick off keys and
values than the other structure (though, honestly, I haven't gone that
far yet).

{"an"=>
   [ "at sf",
     "fc di",
     "ps"=>
       ["na vb",
        "ex anEx"]],
     "ps"=>
       ["na no",
        "ex otherEx",
        "pr pro"]]],
  "in"=>
    ["at sf",
     "fc lo"]
}

Thanks for the help.
basi

(lists) #2

Hello,

How does one tell YAML to use an array substructure rather than a hash?

<snip>

{"an"=>
   [ "at sf",
     "fc di",
     "ps"=>
       ["na vb",
        "ex anEx"]],
     "ps"=>
       ["na no",
        "ex otherEx",
        "pr pro"]]],
  "in"=>
    ["at sf",
     "fc lo"]
}

YAML::dump() might be helpful:

require 'yaml'

hash = {"an"=>
   [ "at sf",
     "fc di",
     {"ps"=>
   ["na vb",
     "ex anEx"]}],
     "ps"=>
   ["na no",
     "ex otherEx",
     "pr pro"],
     "in"=>
   ["at sf",
     "fc lo"]
}

puts YAML::dump(hash)

That will show you the yaml representation of the 'hash' data structure (I know my 'hash' is different from what you posted, but yours didn't parse).

Hope this helps,
Ryan

···

On Aug 17, 2005, at 9:51 PM, basi wrote:

(Ben Giddings) #3

This doesn't look like a hash containing an array to me. The "ps" entries
inside look like they're hashes, the key is "ps", the value is an array
containing ["na vb", "ex anEx"]...

In any event, I think if you have value: stuff in YAML, it will decide it's a
hash entry. Array entries start with '-'.

If you don't care about the "ps" keys:

irb(main):004:0> puts ["at sf", "fc di", ["na vb", "ex anEx"],["na no", "ex
otherEx", "pr pro"]].to_yaml

···

On Wednesday 17 August 2005 22:51, basi wrote:

I feel that the structure below: {[[[...]]]}, that is, hash embeds
array which embeds another array makes it easier to pick off keys and
values than the other structure (though, honestly, I haven't gone that
far yet).

{"an"=>
   [ "at sf",
     "fc di",
     "ps"=>
       ["na vb",
        "ex anEx"]],
     "ps"=>
       ["na no",
        "ex otherEx",
        "pr pro"]]],
  "in"=>
    ["at sf",
     "fc lo"]
}

---
- at sf
- fc di
-
  - na vb
  - ex anEx
-
  - na no
  - ex otherEx
  - pr pro

Ben

(Zach Dennis) #4

lists wrote:

That will show you the yaml representation of the 'hash' data structure (I know my 'hash' is different from what you posted, but yours didn't parse).

It's because in the *wanted* structure, the OP tried to say:

{"an"=>
    [ "at sf",
      "fc di",
      "ps"=>
        ["na vb",
         "ex anEx"]],
  # etc...
}

{"an"=>
    [ "at sf",
      "fc di",
      "ps"=>
        ["na vb",
         "ex anEx"] ] }

The OP is treating "ps" has a hash key => value pair inside of an array. This is why you need "ps" to be inside of another hash.

Zach

(basi) #5

You're right. Please see my posting more recent posting.

(basi) #6

Soon as I posted the correct description above, I tried:

in YAML
x:
  bb:
    - boo
  aa:
    - 2
    - 3
y:
  cc:
    - see
    - sea:
      - weed
      - sick
  aa:
    - 5

and got a result that looks good. (I swear I tried that solution before
and it didn't work!)

{"x"=>
  {"aa"=>[2, 3],
   "bb"=>["boo"]},
  "y"=>
  {"aa"=>[5],
   "cc"=>["see",
               {"sea"=>["weed", "sick"]}]}}

Apologies again and cheers!
basi

(basi) #7

Hi,
Your tip to use

puts YAML::dump(hash)

I find very useful today.

Thanks much!
basi

(basi) #8

Yes, I screwed up the description of the problem big time. Here's a
better description:

Given the YAML statements:

x:
  bb:
    - boo
  aa:
    - 2
    - 3
y:
  cc:
    - see
    - sea
  aa:
    - 5

YAML produces:

{"x"=>{"aa"=>[2, 3], "bb"=>["boo"]}, "y"=>{"aa"=>[5], "cc"=>["see",
"sea"]}}

I think I can handle picking up keys and values here, so I'm happy with
the nesting. But now my problem is how to add extra levels of nesting
to the YAML statements, where "see" has its own substructure, something
like:

x:
  bb:
    - boo
  aa:
    - 2
    - 3
y:
  cc:
    - see
      sea:
        - weed
        - sick
  aa:
    - 5

The above doesn't parse. Gives the message:

ArgumentError: parse error on line 9, col 7: ` sea:
'
  from c:/ruby/lib/ruby/1.8/YAML.rb:119:in `load'
  from c:/ruby/lib/ruby/1.8/YAML.rb:119:in `load'
  from (irb):52

My apologies for the incorrect OP. And thanks.
basi
    
  value3

···

from :0

(basi) #9

But now I cannot get at the values ["weed", "sick"]

irb(main):056:0> h["y"]["cc"]
=> ["see", {"sea"=>["weed", "sick"]}]
irb(main):057:0> h["y"]["cc"]["sea"]
TypeError: cannot convert String into Integer
  from (irb):57:in `[]'
  from (irb):57
  from :0

I think I'm gonna quit for a while. I feel I'm getting sick. I need
some weed, er, I mean sleep.

Cheers,
Basi

(Ben Giddings) #10

irb(main):051:0> h = {"x"=>{"aa"=>[2, 3], "bb"=>["boo"]}, "y"=>{"aa"=>[5],
"cc"=>["see", {"sea" => ["weed", "sick"]}]}}
=> {"x"=>{"bb"=>["boo"], "aa"=>[2, 3]}, "y"=>{"cc"=>["see", {"sea"=>["weed",
"sick"]}], "aa"=>[5]}}
irb(main):052:0> puts h.to_yaml

···

On Thursday 18 August 2005 00:31, basi wrote:

I think I can handle picking up keys and values here, so I'm happy with
the nesting. But now my problem is how to add extra levels of nesting
to the YAML statements, where "see" has its own substructure, something
like:

x:
  bb:
    - boo
  aa:
    - 2
    - 3
y:
  cc:
    - see
      sea:
        - weed
        - sick
  aa:
    - 5

---
x:
  bb:
    - boo
  aa:
    - 2
    - 3
y:
  cc:
    - see
    -
      sea:
        - weed
        - sick
  aa:
    - 5
=> nil

Did you mean "sea" has it's own substructure, i.e. the hash key "cc" points to
an array with two values "see" and another hash containing one entry where
the key is "sea" and the value is an array containing "weed" and "sick".

If that's your goal, you were close, all you needed was the line with the dash
on it before "sea:"

Btw, the way I figured this out was by creating the structure in ruby in IRB,
then calling "puts h.to_yaml". There are lots of braces and brackets to work
through, but the error messages tend to be better.

Ben

(Jamal Hansen) #11

I think h["y"]["cc"][1]["sea"][1] would return "sick" since "sea" is a
key in a hash in an array which contains an array. er.. yeah.

-Jamal

···

On 8/17/05, basi <basi_lio@hotmail.com> wrote:

But now I cannot get at the values ["weed", "sick"]

irb(main):056:0> h["y"]["cc"]
=> ["see", {"sea"=>["weed", "sick"]}]
irb(main):057:0> h["y"]["cc"]["sea"]
TypeError: cannot convert String into Integer
        from (irb):57:in `[]'
        from (irb):57
        from :0

(basi) #12

Thanks much. yeah, very useful.
-- basi

(basi) #13

Jamal,
Yes, that'd work for this example. In my project, I would not know
beforehand what the array index would be. I suppose one could extract
the array index from its value.

But, after a good night's sleep, I found the structure that looks like
will work for me: hash all the way down.

Thanks all,
basi