Skip to content

Unified Dot Notation System

See also: Object Access Patterns, XML and HTML Processing

Best Practices: - Use dot notation for simple property access - Use bracket notation for dynamic property names or special characters - The same access patterns work across JSON, XML, and HTML structures

Overview

Grapa provides a unified dot notation system that allows you to access data consistently across different types including JSON objects, arrays, XML documents, and HTML structures. This system uses the same syntax patterns regardless of the underlying data type.

Basic Access Patterns

JSON Object Access

/* Object property access */
user = {name:"Alice", age:30, city:"New York"};
user.name;      /* "Alice" */
user["name"];   /* "Alice" - bracket notation */
user.age;       /* 30 */

/* Object property assignment */
user.name = "Bob";          /* Change name to "Bob" */
user["age"] = 25;           /* Change age to 25 */
user.city = "Boston";       /* Change city to "Boston" */

/* Duplicate key resolution - last value wins */
user2 = {name:"Alice", age:30, name:"Bob"};
user2.name;     /* "Bob" (last value) */

user3 = {a:1, b:2, a:3, c:4, b:5};
user3.a;        /* 3 (last value) */
user3.b;        /* 5 (last value) */
user3.c;        /* 4 (only value) */

Array Access

/* Array element access */
numbers = [1, 2, 3, 4, 5];
numbers[0];     /* 1 */
numbers[1];     /* 2 */
numbers[-1];    /* 5 - negative indexing */

/* Array element assignment */
numbers[0] = 10;            /* Change first element to 10 */
numbers[-1] = 50;           /* Change last element to 50 */
numbers[2] = 30;            /* Change third element to 30 */

XML/HTML Access

/* XML element access */
xml = <root><item>1</item><item>2</item></root>;
xml[0];         /* Root element */
xml[0][0];      /* First item element */
xml[0][0][0];   /* Text content: "1" */

/* HTML structure access */
html = <html><body><div class="main"><h1>Title</h1></div></body></html>;
html[0][0][0][0][0];  /* Navigate to "Title" */

Interchangeable Notation

Dot vs Bracket Notation

Both dot notation and bracket notation work for most access patterns:

/* These are equivalent */
data = {name:"Alice", age:30};
data.name;      /* Dot notation */
data["name"];   /* Bracket notation */

/* Bracket notation is required for special characters */
data = {"user-name":"Alice", "age":30};
data["user-name"];  /* Must use brackets for hyphens */
data.age;           /* Works for simple names */

/* ⚠️ IMPORTANT: Hyphens in property names require quotes */
/* The hyphen (-) is interpreted as a subtraction operator */
config = {ab:3, "a-b":4, a-b:5};  /* a-b becomes 5 (math operation) */
config.ab;     /* 3 */
config["a-b"]; /* 4 (correct) */
config."a-b";  /* 4 (also correct) */
/* config.a-b;  ❌ This would be interpreted as config.a - b (subtraction) */

Dynamic Property Access

Bracket notation is essential for dynamic property names:

/* Dynamic property access */
data = {name:"Alice", age:30, city:"New York"};
property = "name";
data[property];     /* "Alice" */

/* Loop through properties */
properties = ["name", "age", "city"];
properties.map(op(prop){data[prop];}).echo();

/* Advanced: Recursive keys function using .getname() */
keys = op(lst){lst.reduce(op(acc,x){if(x.type()==$LIST){acc += keys(x);}else{acc += 'x'.getname();}},[]);};

obj = {name:"Alice", age:30, test:{a:1,b:2}, city:"NYC"};
keys(obj).echo();  /* Outputs: [name,age,[a,b],city] */

/* Dynamic property access with keys */
all_keys = keys(obj);
all_keys.map(op(key){obj[key].echo();});  /* Outputs all values */

Duplicate Key Resolution

When objects contain duplicate keys, Grapa follows "last value wins" behavior:

/* Duplicate keys - last value wins */
config = {debug: false, port: 8080, debug: true};
config.debug;   /* true (last value) */
config.port;    /* 8080 (only value) */

/* Multiple duplicates */
settings = {theme: "dark", timeout: 30, theme: "light", timeout: 60};
settings.theme;    /* "light" (last value) */
settings.timeout;  /* 60 (last value) */

Key Points: - Search Order: Property resolution searches from tail to head (reverse order) - Last Value Wins: When duplicate keys exist, the last occurrence takes precedence - JSON Compatibility: Matches behavior of JSON in other programming languages - Index Access Unchanged: Array-style indexing (obj[0], obj[1]) uses position-based access - Property Assignment: Assignment also targets the last occurrence of duplicate keys

XML and HTML Navigation

Element Hierarchy

XML and HTML structures can be navigated using the same indexing patterns:

/* Simple XML navigation */
xml = <root><item>1</item><item>2</item></root>;
xml[0];         /* <root> element */
xml[0][0];      /* First <item> element */
xml[0][1];      /* Second <item> element */
xml[0][0][0];   /* Text content: "1" */

/* Complex HTML navigation */
html = <html><body><div class="main"><h1>Title</h1><p>Content</p></div></body></html>;
html[0];        /* <html> element */
html[0][0];     /* <body> element */
html[0][0][0];  /* <div> element */
html[0][0][0][0]; /* <h1> element */
html[0][0][0][0][0]; /* "Title" */
html[0][0][0][1]; /* <p> element */
html[0][0][0][1][0]; /* "Content" */

Element Name Access

You can access elements by their tag names:

/* Access by element name */
xml = <root><item>1</item><item>2</item><header>Title</header></root>;
xml.item;       /* Access all 'item' elements */
xml.header;     /* Access 'header' element */

Attribute Access

XML and HTML attributes can be accessed using dot notation:

/* Attribute access */
div = <div class="main" id="content">Text</div>;
div.class;      /* Access class attribute */
div.id;         /* Access id attribute */

/* Complex attribute access */
form = <form method="post" action="/submit"><input type="text" name="username"/></form>;
form.method;    /* "post" */
form.action;    /* "/submit" */
form[0].type;   /* "text" */
form[0].name;   /* "username" */

Advanced Access Patterns

Nested Object Access

/* Deep nested access */
data = {
    user: {
        profile: {
            name: "Alice",
            address: {
                city: "New York",
                country: "USA"
            }
        }
    }
};

data.user.profile.name;           /* "Alice" */
data.user.profile.address.city;   /* "New York" */
data["user"]["profile"]["name"];  /* Bracket notation */

Array of Objects

/* Access properties in arrays of objects */
users = [
    {id:1, name:"Alice", age:30},
    {id:2, name:"Bob", age:25},
    {id:3, name:"Charlie", age:35}
];

users[0].name;   /* "Alice" */
users[1].age;    /* 25 */

/* Extract all names */
names = users.map(op(user){user.name;});
names.echo();    /* ["Alice", "Bob", "Charlie"] */

Mixed Data Types

/* Access patterns work across mixed types */
data = {
    users: [
        {name:"Alice", preferences:{theme:"dark", lang:"en"}},
        {name:"Bob", preferences:{theme:"light", lang:"es"}}
    ],
    settings: {
        api_url: "https://api.example.com",
        timeout: 30
    }
};

data.users[0].preferences.theme;  /* "dark" */
data.settings.api_url;            /* "https://api.example.com" */

Error Handling

Graceful Property Access

The system gracefully handles missing properties:

/* Missing properties return error */
data = {name:"Alice"};
data.age;       /* Returns error */
data["age"];    /* Returns error */

/* Safe access with error checking */
if (data.age.type() != $ERR) {
    data.age.echo();
} else {
    "Age not found".echo();
}

Null/Undefined Handling

/* Handle null/undefined values */
data = {name:"Alice", details:null};
data.name;      /* "Alice" */
data.details;   /* null */
data.details.age; /* Error - can't access property of null */

Performance Considerations

Efficient Access

  • Direct Access: Dot notation is slightly faster than bracket notation
  • Caching: Frequently accessed properties are cached internally
  • Lazy Loading: Properties are resolved only when accessed

Best Practices

/* Prefer dot notation for known properties */
user.name;      /* Faster */
user["name"];   /* Slower but more flexible */

/* Use bracket notation for dynamic access */
property = "name";
user[property]; /* Required for dynamic access */

/* Avoid deep nesting in loops */
/* Instead of: */
users.map(op(user){user.profile.address.city;});

/* Consider: */
users.map(op(user){
    profile = user.profile;
    profile ? profile.address.city : null;
});

⚠️ Critical: Number Method Calls

IMPORTANT: When calling methods on numbers using dot notation, you must enclose the number in parentheses:

/* ✅ Correct - Direct number method calls */
20.random();      /* Works: Random number 0-19 */
3.14.floor();     /* Works: Floor to 3 */
42.str();         /* Works: Convert to string */
145.len();        /* Works: Length of number (3) */
145.echo();       /* Works: Output the number */

Common patterns:

/* Working examples */
result = 20.random();     /* Random number 0-19 */
rounded = 3.14159.floor(); /* Floor: 3 */
text = 42.str();          /* Convert to string: "42" */

/* In functional programming */
numbers = [10.random(), 20.random(), 30.random()];
text_numbers = [1.str(), 2.str(), 3.str()];

Use Cases

1. Configuration Access

/* Access configuration settings */
config = {
    database: {
        host: "localhost",
        port: 5432,
        credentials: {
            username: "admin",
            password: "secret"
        }
    },
    api: {
        base_url: "https://api.example.com",
        timeout: 30
    }
};

db_host = config.database.host;
api_url = config.api.base_url;

2. Web Scraping

/* Extract data from HTML */
html = $file().get("page.html").str().html();
title = html[0][0][0][0][0];  /* Extract page title */
links = html[0][0].findall({name:"a"});  /* Find all links */

3. JSON Processing

/* Process JSON API responses */
response = $file().get("api.json").str().json();
users = response.data.users;
first_user = users[0];
user_name = first_user.name;

4. XML Document Processing

/* Process XML documents */
xml = $file().get("data.xml").str().xml();
items = xml[0].findall({name:"item"});
item_count = items.len();

/* Convert XML to LIST for easier processing */
xml = <root><item>1</item><item>2</item></root>;
list = xml.list();  /* Convert to LIST structure */
list.echo();        /* View converted structure */

Tips and Tricks

1. Debugging Access

/* Debug object structure */
data = {user:{name:"Alice", age:30}};
data.echo();        /* See full structure */
data.user.echo();   /* See user object */

2. Safe Navigation

/* Safe navigation pattern */
data = {user:{profile:{name:"Alice"}}};
name = data.user && data.user.profile ? data.user.profile.name : null;

3. Dynamic Property Building

/* Build property paths dynamically */
data = {a:{b:{c:"value"}}};
path = ["a", "b", "c"];
result = path.reduce(op(acc, prop){acc[prop];}, data);
result.echo();  /* "value" */

See Also