:TITLE: Introduction to dict: Deeply Nested Dicts
;#
;# RCSID: $Header: /home/cvs/TclTutor3/lesson/Tcl_English/Tcl_English46.lsn,v 1.2 2010/12/22 15:53:19 clif Exp $
;# Copyright (c) 1995 Clif Flynt
;# 9300 Fleming Rd.
;# Dexter, MI  48130
;# clif@cflynt.com
;# See file "NOTICE" for licensing terms.
;#
:LESSON_TEXT_START_LEVEL 0:
The dict structures support nesting one dict within another.  This
can go as deeply as the data requires.
<P>
The simplest way to add new, deeper levels to a dict is with the
<CODE>dict set</CODE> command.  Multiple keys reflect deeper
layers of nested dicts, until the final value is inserted.

<P><CODE><B>Syntax:</B>dict set <I>dictName key1 key2 ... val</I></CODE>

<PRE><CODE>
# Create a nested dict: a {b c} d {e f}
dict set dd a b c
dict set dd d e f
</CODE></PRE>
The <CODE>dict create</CODE> command can also be used to create
nested dicts by nesting the <CODE>dict create</CODE> commands as you'd
nest <CODE>list</CODE> commands to create nested lists.

<P><CODE><B>Syntax:</B>dict create <I>key1 val1 ... keyN valN </I></CODE>

<PRE><CODE>
# Create a nested dict: a {b c} d {e f}
set dd [dict create a [dict create b c] d [dict create e f]]
</CODE></PRE>

You can access elements in a nested list with the <CODE>dict get</CODE>
command.  Like the <CODE>dict set</CODE>, it accepts multiple sets
of keys to index deeper and deeper into a dict.

:TEXT_END:

:LESSON_TEXT_START_LEVEL 1:
<P>

The dict data structure supports nesting one dict within another. This
can go as deeply as the data requires.  This provides support for
things like "C" language structs that contain other structs or arrays
of structs with embedded structs, or unwrapped relational databases.
<P>

A nested dict can be used to create data structures that are logically
equivalent to this set of nested C structs:
<P>

<PRE><CODE>
struct books {
  int id;
  struct s_author *author;
  struct s_series *series;
};
struct s_author {
  char *name;
};
struct s_series {
  char *name;
  struct s_book *book;
};
</CODE></PRE>

Or this SQL Schema:

<PRE><CODE>
 table create books (
   bookid int;
   authorid int; - References author table
   seriesid int; - References series table
 );
 table create author {
   authorid int;
   name text;
 }
 table create series {
   seriesid int;
   name text;
   bookid int; - References book table
 }
 ...
</CODE></PRE>
<P>

The simplest way to add new, deeper levels to a dict is with the
<CODE>dict set</CODE> command.  Multiple keys reflect deeper
layers of nested dicts, until the final value is inserted.  Note
that the final two arguments are the final key and value pair,
earlier keys define deeper sets of nested dicts.

<P><CODE><B>Syntax:</B>dict set <I>dictName key1 key2 ... val</I></CODE>
<PRE><CODE>
# Create a nested dict: a {b c} d {e f}
dict set dd a b c
dict set dd d e f
</CODE></PRE>


The <CODE>dict create</CODE> command can also be used to create
nested dicts by nesting the <CODE>dict create</CODE> commands as you'd
nest <CODE>list</CODE> commands to create nested lists.
<P><CODE><B>Syntax:</B>dict create <I>key1 val1 ... keyN valN </I></CODE>
<PRE><CODE>
# Create a nested dict: a {b c} d {e f}
set dd [dict create a [dict create b c] d [dict create e f]]
</CODE></PRE>

You can access elements in a nested list with the <CODE>dict get</CODE>
command.  Like the <CODE>dict set</CODE>, it accepts multiple sets
of keys to index deeper and deeper into a dict.
<P>

The <CODE>dict for</CODE> command can also be used to process nested
dicts. In a nested dict, the <I>value</I> will be a dict in it's own right.

:TEXT_END:

:LESSON_TEXT_START_LEVEL 2:
<P>

Many data sets are best visualized as a set of russian dolls, with one
collection of data nested inside of another.  This ranges from physical
attributes like a library is a collection of books each of which has a
collection of words, etc to pure Computer Science  uses such as the
Ethernet packet that contains a TCP packet that contains data.
<P>

The dict data structure supports nesting one dict within another. This
can go as deeply as the data requires.  This provides support for
things like "C" language structs that contain other structs or arrays
of structs with embedded structs, or unwrapped relational databases.
<P>

A nested dict can be used to create data structures that are logically
equivalent to this set of nested C structs:

<PRE><CODE>
struct books {
  int id;
  struct s_author *author;
  struct s_series *series;
};
struct s_author {
  char *name;
};
struct s_series {
  char *name;
  struct s_book *book;
};
</CODE></PRE>

Or this SQL Schema:

<PRE><CODE>
 table create books (
   bookid int;
   authorid int; - References author table
   seriesid int; - References series table
 );
 table create author {
   authorid int;
   name text;
 }
 table create series {
   seriesid int;
   name text;
   bookid int; - References book table
 }
 ...
</CODE></PRE>
<P>

The <CODE>dict set</CODE> command accepts multiple sets of
keys, which can reference more and more deeply nested dicts.
<P>

The simplest way to add new, deeper levels to a dict is with the
<CODE>dict set</CODE> command.  Multiple keys reflect deeper
layers of nested dicts, until the final value is inserted.  Note
that the final two arguments are the final key and value pair,
earlier keys define deeper sets of nested dicts.
<P>
If the final key already exists in a dict, the existing value associated
with that key will be replaced with the new value.

<P><CODE><B>Syntax:</B>dict set <I>dictName key1 key2 ... val</I></CODE>

<PRE><CODE>
# Create a nested dict: a {b c} d {e f}
dict set dd a b c
dict set dd d e f
</CODE></PRE>

The <CODE>dict create</CODE> command can also be used to create
nested dicts by nesting the <CODE>dict create</CODE> commands as you'd
nest <CODE>list</CODE> commands to create nested lists.
<P><CODE><B>Syntax:</B>dict create <I>key1 val1 ... keyN valN </I></CODE>
<PRE><CODE>
# Create a nested dict: a {b c} d {e f}
set dd [dict create a [dict create b c] d [dict create e f]]
</CODE></PRE>

The value returned by the <CODE>dict create</CODE> command
is a dict, which can be the value to be associated with a key.
<P>

You can access elements in a nested list with the <CODE>dict get</CODE>
command.  Like the <CODE>dict set</CODE>, it accepts multiple sets
of keys to index deeper and deeper into a dict.
<P>

The <CODE>dict for</CODE> command can also be used to process nested
dicts. When using <CODE>dict for</CODE> the value associated with a
key will be a dict.
:TEXT_END:

:CODE_START:
# Build nested dict
# Keys are:
#  Unique Value
#    AUTHOR
#    SERIES
#      series name
#        book title
#          owned
#          read

# Build dict element 1 using multiple dict set commands

dict set books 1 AUTHOR "P.G. Wodehouse"
dict set books 1 SERIES "Blandings Castle" "Something Fresh" owned no
dict set books 1 SERIES "Blandings Castle" "Heavy Weather" owned yes
dict set books 1 SERIES "Jeeves & Wooster" "The Inimitable Jeeves" owned no
dict set books 1 SERIES "Jeeves & Wooster" "Carry On, Jeeves" owned yes

# Build dict element 2 using embedded dict create commands.

dict set books 2 \
  [dict create AUTHOR "Edgar Rice Burroughs"\
    SERIES \
    [dict create Barsoom \
      [dict create "Princess Of Mars" [dict create owned no read no] \
                   "Warlord Of Mars" [dict create owned yes read no]] \
          Tarzan \
      [dict create "Tarzan of the Apes" [dict create owned yes read yes] \
                   "The Return of Tarzan" [dict create owned no read yes]] \
          Venus \
      [dict create "Pirates of Venus" [dict create owned yes read yes] \
                   "Lost on Venus" [dict create owned no read yes]] \
    ] \
  ]


# Display the book titles in the Blandings Castle series.
puts "These books are in the Blandings Castle series: \
    [dict keys [dict get $books 1 SERIES {Blandings Castle}]]"

# Add a field to the Blandings Castle series for 'read'

dict set books 1 SERIES {Blandings Castle} {Something Fresh} read no
dict set books 1 SERIES {Blandings Castle} {Heavy Weather} read yes

foreach mainKey [dict keys $books] {
  set author [dict get $books $mainKey AUTHOR]
  dict for {k v} [dict get $books $mainKey SERIES] {
    # k has the name of the book (key) 
    # v is a dict with title as key 
    dict for {k1 v1} $v {
      # k1 is the title of the book.
      # v1 is a dict with 'read' and 'owned' and keys
      puts "Author: $author"
      puts "Series Title: $k"
      puts "  Book Title: $k1" 
	puts -nonewline "  "
      dict for {k2 v2} $v1 {
        # k2 will be 'owned' or 'read'
	# v2 will be 'yes' or 'no'
	puts -nonewline "$k2 $v2 "
      }
      puts ""
    }
  }
}

:TEXT_END:
