Reputation: 95
I am trying to do what I thought would be simple in YAML. This is a simple hierarchy with repeating elements. The first example below is very close, but each node sequence element triggers the error: "bad indentation of a sequence entry". I can't see what is wrong with it.
Note I include an example further down that works, but it uses unique keys which is not what I want.
- agegrp: 1
- node : "(14, 6)"
# id: "(14, 6)"
- branch : "to 7"
id : "to 7"
tocond : 7
pr : 1.0
next : (0, 0)
- node : "(14, 7)"
# id: "(14,7)"
- branch : "to 7"
id : "to 7"
tocond : 7
pr : 0.85
next : (0, 0)
- branch : "to 8"
id : "to 8"
tocond : 8
pr : 0.15
next : (4, 4)
- agegrp : 2
- node : "(14, 6)"
# id: "(14, 6)"
- branch : "to 7"
id : "to 7"
tocond : 7
pr : 1.0
next : (0, 0)
- node : "(14, 7)"
# id: "(14,7)"
- branch : "to 7"
id : "to 7"
tocond : 7
pr : 0.85
next : (0, 0)
- branch : "to 8"
id : "to 8"
tocond : 8
pr : 0.15
next : (4, 4)
Note that the following works, but I don't want branch and node to have unique names. I want a repeating structure. I can use a dash in front of branch and node, but this causes a different problem: bad indentation of a sequence entry.
agegrp:
id: 1
node (14, 6):
# id: "(14, 6)"
branch:
id: "to 7"
tocond: 7
pr: 1.0
next: (0, 0)
node (14,7):
# id: "(14,7)"
branch to 7:
id: "to 7"
tocond: 7
pr: 0.85
next: (0, 0)
branch to 8:
id: "to 8"
tocond: 8
pr: 0.15
next: (4, 4)
There will be 5 age groups. Nodes are within agegroups. Branches are within nodes. Each branch has 3 properties. I am ok with an id item under each node, rather than as I show above.
I am confused about when I must use a leading dash and when not. I am confused about when a value is allowed and when not.
Is this even possible in YAML? TOML doesn't like hierarchies. Up to this point I have it in csv and parse that and build a dictionary in code. I thought I could use a format that would natively represent the hierarchy.
The output I would like:
Here is an example: here is one dict for an age group:
(1, 1) =>
CovidSim.Branch(5, 5, 0.2, (2, 1), "nil", "nil")
CovidSim.Branch(5, 6, 0.65, (2, 2), "nil", "mild")
CovidSim.Branch(5, 7, 0.15, (2, 3), "nil", "sick")
(2, 1) =>
CovidSim.Branch(5, 3, 0.8, (0, 0), "nil", "recovered")
CovidSim.Branch(5, 7, 0.2, (3, 3), "nil", "sick")
(2, 2) =>
CovidSim.Branch(6, 6, 1.0, (3, 2), "mild", "mild")
(2, 3) =>
CovidSim.Branch(7, 7, 0.85, (3, 3), "sick", "sick")
CovidSim.Branch(7, 8, 0.15, (3, 4), "sick", "severe")
(3, 2) =>
CovidSim.Branch(6, 3, 1.0, (0, 0), "mild", "recovered")
(3, 3) =>
CovidSim.Branch(7, 3, 0.8, (0, 0), "sick", "recovered")
CovidSim.Branch(7, 7, 0.1, (5, 3), "sick", "sick")
CovidSim.Branch(7, 8, 0.1, (4, 4), "sick", "severe")
(3, 4) =>
CovidSim.Branch(8, 3, 0.45, (0, 0), "severe", "recovered")
CovidSim.Branch(8, 8, 0.5, (4, 4), "severe", "severe")
CovidSim.Branch(8, 4, 0.05, (0, 5), "severe", "dead")
(4, 4) =>
CovidSim.Branch(8, 3, 0.85, (0, 0), "severe", "recovered")
CovidSim.Branch(8, 8, 0.1, (5, 4), "severe", "severe")
CovidSim.Branch(8, 4, 0.05, (0, 5), "severe", "dead")
(5, 3) =>
CovidSim.Branch(7, 3, 0.9, (0, 0), "sick", "recovered")
CovidSim.Branch(7, 4, 0.1, (0, 5), "sick", "dead")
(5, 4) =>
CovidSim.Branch(8, 3, 0.6, (0, 0), "severe", "recovered")
CovidSim.Branch(8, 4, 0.4, (0, 5), "severe", "dead")
The outer container is an array of 5 agegroup dicts.
(Note, this example contains some extra fields in each branch that I am reducing.)
Thanks!
Upvotes: 1
Views: 4155
Reputation: 95
Thanks for your help. I was not getting my head around YAML and creating entity groups without any name. @KeepCalmAndCarryOn: Your structure resulted in an array of nodes with an id item and a branches item at the same level (the branches item is an array of the branches, each branch being a dict of the properties. This works but doesn't display very nicely and is harder to work with (but certainly contains all the information from my example).
I prefer a strict hierarchy. I hand-coded json. Then, parsed the json to a nested julia object: an array of dicts like this:
Dict{String,Any} with 5 entries:
"4" => Dict{String,Any}("(9,6)"=>Any[Dict{String,Any}("tocond"=>6,"next"=>Any…
"1" => Dict{String,Any}("(9,6)"=>Any[Dict{String,Any}("tocond"=>6,"next"=>Any…
"5" => Dict{String,Any}("(9,6)"=>Any[Dict{String,Any}("tocond"=>6,"next"=>Any…
"2" => Dict{String,Any}("(9,6)"=>Any[Dict{String,Any}("tocond"=>6,"next"=>Any…
"3" => Dict{String,Any}("(9,6)"=>Any[Dict{String,Any}("tocond"=>6,"next"=>Any…
The output is truncated but you can see each agegroup as a key--with a dict of nodes as its value. Each dict of nodes shows the first key (a node) with an array of branches as its value. Each item in the branches array is a dict of the fields for each property of a branch.
One agegrp looks like this:
Dict{String,Any} with 8 entries:
"(9,6)" => Any[Dict{String,Any}("tocond"=>6,"next"=>Any[14, 6],"pr"=>1.0)]
"(9,5)" => Any[Dict{String,Any}("tocond"=>3,"next"=>Any[0, 0],"pr"=>0.8), Di…
"(14,6)" => Any[Dict{String,Any}("tocond"=>3,"next"=>Any[0, 0],"pr"=>1.0)]
"(9,7)" => Any[Dict{String,Any}("tocond"=>7,"next"=>Any[14, 7],"pr"=>0.85), …
"(5,5)" => Any[Dict{String,Any}("tocond"=>5,"next"=>Any[9, 5],"pr"=>0.2), Di…
"(14,8)" => Any[Dict{String,Any}("tocond"=>3,"next"=>Any[0, 0],"pr"=>0.45), D…
"(14,7)" => Any[Dict{String,Any}("tocond"=>3,"next"=>Any[0, 0],"pr"=>0.85), D…
"(19,8)" => Any[Dict{String,Any}("tocond"=>3,"next"=>Any[0, 0],"pr"=>0.9), Di…
Then, I took this Julia nested object (a bit gnarly, but with clearer structure--at least to me) and put it through YAML to create valid YAML output.
This YAML is just what I really wanted, even if it seems nasty-ish to someone who uses YAML properly. Here is just one agegrp (the others are identical in structure and follow sequentially in the YAML document):
4: # the id of an agegrp
(9,6): # the id of a node
- tocond: 6 # first property of a branch
next:
- 14
- 6
pr: 1.0
(9,5):
- tocond: 3
next:
- 0
- 0
pr: 0.8
- tocond: 7
next:
- 14
- 7
pr: 0.2
(14,6):
- tocond: 3
next:
- 0
- 0
pr: 1.0
(9,7):
- tocond: 7
next:
- 14
- 7
pr: 0.85
- tocond: 8
next:
- 14
- 8
pr: 0.15
<rest of output truncated...>
This follows the hierarchy I wanted:
top: an agegroup id
then a node (with more following after the indented children)
then a branch
then an array of the branches properties
I really messed myself up by mixing the name of the entities (agegrp, node, branch) with actual values of specific instances of each entity--like I was trying to label each thing with its type. You wouldn't do that in a Dict. I am using Julia here, but Dicts are essentially the same as in Python.
So, a bit of a frustrating foray into YAML. But, it will a lot easier to enter than the CSV I started with and much easier than hand authoring JSON. So, a successful foray!
Upvotes: 0
Reputation: 39688
Following up on your own answer: YAML does allow you to put the names of the entities into a YAML file. It does so by providing syntax for tags. Here's an example how your file could look like:
!agegrp 4:
!node [9,6]:
- !branch
tocond: 6
next:
- 14
- 6
pr: 1.0
!node [9,5]:
- !branch
tocond: 3
next:
- 0
- 0
pr: 0.8
- !branch
tocond: 7
next:
- 14
- 7
pr: 0.2
Note that I use YAML sequences for the node coordinates, since it looks like you want to parse them into numeric pairs anyway ((9,6)
would be parsed as string). You could keep the original syntax instead.
What this does is to associate the value directly after the tag with it. So 4
will be tagged !agegrp
, the sequence [9,6]
will be tagged !node
, and the mapping containing tocond
etc will be tagged !branch
. Local tags like these are application-defined; you will need to register handlers for them with most YAML implementations.
Semantically, this would mark 4
as agegrp (whatever that is), while the nodes are not part of that agegrp object – with this markup, only the key 4
is tagged as agegrp. How you process the file is up to you, of course. An alternative would be:
4: !agegrp
!node [9,6]:
…
Now, the mapping containing the nodes is marked agegrp and the 4
is just a key in a mapping (probably parsed as integer).
If you want the IDs to be part of the objects, you could shift the document to a sequence-based structure, @KeepCalmAndCarryOn already showed the usual way to do this, here is one using tags:
- !agegrp
- !id 4
- !node
- !id [9,6]
- !branch
tocond: 6
next:
- 14
- 6
pr: 1.0
- !node
- !id [9,5]
- !branch
tocond: 3
next:
- 0
- 0
pr: 0.8
- !branch
tocond: 7
next:
- 14
- 7
pr: 0.2
What this does is to flatten the internal structure of node so that the ID and the branches can be on the same level. This requires the loading code to properly distinguish between !id
and !branch
nodes when loading that YAML. This kind of structure is often used in XML which does not provide mappings like YAML does and therefore, all nested structures are sequences and different children are distinguished by their element names.
Upvotes: 1
Reputation: 9075
If you look at the YAML spec for collections
YAML’s block collections use indentation for scope and begin each entry on its own line.
Block sequences indicate each entry with a dash and space ( “- ”).
Mappings use a colon and space (“: ”) to mark each key: value pair.
Comments begin with an octothorpe (also called a “hash”, “sharp”, “pound”, or “number sign” - “#”).
so your
- agegrp: 1
is a fusion of a sequence and a map
you could do something like this
---
-
id: 1
nodes:
-
branches:
-
id: "to 7"
next: "(0, 0)"
pr: 1.0
tocond: 7
-
id: "to 7"
next: "(0, 0)"
pr: 0.85
tocond: 7
-
id: "to 8"
next: "(4, 4)"
pr: 0.15
tocond: 8
id: "(14, 6)"
If that suits your needs
Upvotes: 3