Multidemensional dictionary

I’m just playing with multidimensional dictionaries in TBX. For example:

var:dictionary myParameters;
myParameters["par1"] = "the 1st value";
myParameters["par2"] = 2;
myParameters["par3"] = "the 3rd value";
myParameters["par4"] = "key 1 (L2):value 1;key 2 (L2):value 2;key 3 (L2):key 1 (L3):value 1";
testDicts(myParameters);

and a primitive function to access the dicts on each level:

function testDicts(theParameters:dictionary) {
	var:string singleValue;
	var:dictionary singleDictionary;

	if(!theParameters.empty){
		theParameters.keys.each(singleKey){
			singleValue = theParameters[singleKey];
			if(singleValue.contains(":")) {
				singleDictionary = dictionary(singleValue);
				testDicts(singleDictionary);
			} else {
				doDebugLog_db("Key: " + singleKey + " = " + singleValue);
			}
		};
	};
};

this will return (doDebugLog_db just passes the string to $Text of a note):

Key: par2 = 2
Key: par3 = the 3rd value
Key: key 1 (L3) = value 1
Key: key 1 (L2) = value 1
Key: par1 = the 1st value
Key: key 2 (L2) = value 2

My questions are:

  • is there a better way to deal with multidimensional dictionaries in TBX?
  • why are the elements get processed in this order?

Thanks a lot!

Have you checked out the v9.5.0+ [] notation for nested lists? See here (scroll to the bottom).

[Edit: it’s nearer the top of the article now as I’ve updated the page]

1 Like

Oh - no - I missed nested lists - that’s very useful - for my current problem I need keys/dictionaries - but still this is a nice option.
On your help page you may add an explanation about accessing list elements with square brackets:

var:list myTestList = [1;[a;b];3];
myTestList[1][0] results in "a".

Yes, will do.

Done, see List-Type Attributes, in the new section Accessing nested lists.

1 Like

@webline, re nested dictionaries, did you look at the bottom of this article and {...} notation? (needs v9.5.0+)

I’d assume the design intent wasn’t to build enormous nested content, but I’m not sure if anyone’s really used it for real world nesting dictionaries, though I imagine it should work.

yes I read your article - funny: the curly brackets are optional. My little function is working without them and still retrieves the nested list.

The curly brackets are usually optional, but I believe there are ambiguous cases where they’re necessary, and they sometimes make code less confusing.

Can someone share some practical examples of when/why you’d want to use a nested list?

Oh - several - for example if you would like to pass a variable list of parameters to a function. You could do it with a list or dictionary (one dimension). Like:

var:dictionary myParams;

myParams[“city”] = “Boston”;
myParams[“street”] = “Avenue A”;
myParams[“zip”] = “12543”;

callMyFunction(myParams);

If I don’t know before how many parameters are possible this is a nice way to pass them in a universal way. With .each you could then process them in the function.

So why a 2nd dimension? For example if I don’t know the number of parameters and need this universal approach - what to do if you need three (or more) attributes for each parameter?

For example you would like to pass all cars available for sale. You need the ID, the price, the brand and the year of build for each car:

myParams[“12345”] = “550”; // brand and year are missing

so you could pass:

myParams[“12345”] = “price:550;brand:mercedes;year:1965”;
myParams[“35645”] = “price:250;brand:dodge;year:1978”;

Will give you (I added the brackets because it is easier to read):

12345:{price:550;brand:mercedes;year:1965};35645:{price:250;brand:dodge;year:1978}

in the receiving function you could use:

theParameters.keys.each(singleKey){
singleValue = theParameters[singleKey];
myPrice = singleValue[“price”];
myBrand = singleValue[“brand”];

}

or use the recursive method shown in the first post. You even don’t have to know how many levels of dictionaries are there or how many elements each consists of :wink:
I hope my explanation doesn’t sound to weird :wink:

1 Like

Thanks. I sorta get it. Your example, however, seems more “dictionary” key lookup related than nested list related. It makes sense though. Nice way to compact a lot of information in a small place. Once you know the pattern it will be really easy to retrieve what you need for any record.

list… I was still thinking about my dict sample… :slight_smile:

Creating a matrix is one of the most useful applications of nested lists.

Hate to bother you with such a simple question, but if you could provide an example of what you mean by this, that would be awesome.

assume you have 4 destinations

var:list myMatrix = [[0, 1, 2],
                     [3, 4, 5],
                     [6, 7, 8],
                     [9, 10, 11]];

you now have one variable to store all the values and you can access each of the values easily: myMatrix[1][1] → 4

Think about your mileage log:

var:list tripMileage = [[1.8, 4, 6, 0.45],
                        [1.575, 3.5, 5, 0.45],
                        [8.145, 18.1, 18, 0.45],
                        [5.805, 12.9, 14, 0.45]];

if you need the duration for Courtesy Economy Inn To Loft Inn: tripMileage[2][2]
And it is easy and fast to create or manipulate this structure (-> each…) and you could expand the structure to as many elements you need…

1 Like

Thanks. But, I can’t get these examples to work in a TBX file. Also, when I past the above they create linked notes because of the ziplink operator. Hard to test. I really don’t understand why I’m not getting this. :frowning: But I will, eventually.

sorry - didn’t test the sample myself - was more of a theoretical thing. At least you will have to replace “,” with “;” to separate the list items!

did a fast test - there seems to be an issue with list:

var:list tripMileage = [[1.8; 4; 6; 0.45];[1.575; 3.5; 5; 0.45];[8.145; 18.1; 18; 0.45];[5.805; 12.9; 14; 0.45]];
$Text = tripMileage[1][1];

will not return nothing.

var:list tripMileage = [1;[1.8; 4; 6; 0.45];[1.575; 3.5; 5; 0.45];[8.145; 18.1; 18; 0.45];[5.805; 12.9; 14; 0.45]];

will do it.

1 Like

This great, but to quibble the above are nested lists not nested dictionaries.

Note , for @eastgate I can reproduce the bug where the first list item is a [ ] enclosed list:

var:list myMatrix = "[0;1;2];[3;4;5];[6;7;8];[9;10;11]";
$Text = myMatrix[1];

The result is 1, a parsing error, when the expected contents of list items #2 (0-indexed) is 3;4;5.

Politely, @detlefs examples above don’t use Tinderbox syntax. Both lists (List & Set) and Dictionary are in-string delimited strings. Thus

// List/Set:
$MyListing = "[a;b;c];1;[2;3;4;];d";

Notice, @dtelef, no commas! Tinderbox using semicolons as list delimiters.

No tested but

// list containing dictionaries :
$MyListing = "1;{foo:ant;bar:[1;2];baz:bee};5;[2;3;4;];d";

This is congruent with Tinderbox listing syntax as documented in Release Notes,
but I’ve no validated parsing examples. But for instance, logically this should return “ant” but returns nothing (i.e. ""):

var:list myMatrix = "1;{foo:ant;bar:[1;2];baz:bee};5;[2;3;4;];d";
$Text = myMatrix[1]["foo"];

I ought, IME to return the value of key “foo” in the dictionary that is the contents of list items #2.

An evident mistake in posts above is using JavaScript/ or JSON syntax for what are Tinderbox lists.

However, I think this is pushing the boundaries of what has been deeply tested For instance, in theory the syntax allows N levels of nesting, but I’ve not seen any report of anyone actually testing start. The chick-and-egg problem for testers, with no disrespect to the developer, is that with no unambiguous deign statement in Release Notes it is not clear what to test. Tinderbox’s design is to use silent fails. the downside is that isn’t, when black-box testing, a definitive answer. Did the test fail because there was no result or because the process failed, silently? Perhaps, action code is reaching the complexity where a debug mode-with error messages—is needed?

Thanks. @mwra, would love it if you can explain this to me later. I’m still pretty confused.

That’s what Michael asked for - I was focused on dictionaries but Michael was looking for lists… it was Michaels fault :innocent:

May I add a reference to your own text:

$MyTestList = [1;[a;b];3];

I don’t like the idea to use the string syntax for lists.

I know - a lot of words in the thread for a small problem, sorry for that - but sometimes to read the stuff may help: “sorry - didn’t test the sample myself - was more of a theoretical thing. At least you will have to replace “,” with “;” to separate the list items!” :face_with_hand_over_mouth:

Anyhow - I was just looking for a nice architecture to use nested dictionaries in TBX. They work for me and I like them.
Nested lists or dictionaries are very useful patterns but I would not mix them. I use those containers most of the time to create more universal functions for my code. I need consistency in my data structure for that task.

1 Like

@satikusala how about a real world example - using dictionaries:

Here is a function where I use a dictionary (flat, not nested) to pass a variable count of parameters to a function. The function will return weather data - and sometimes I need more and sometimes less parameters:

function callOpenWeatherAPI_Main_db(theParameters:dictionary){
	var:string baseURL = "https://api.openweathermap.org/data/3.0/onecall?";
	var:string curlURL = 'curl -s "' + baseURL;

	if(!theParameters.empty){
		theParameters.keys.each(singleKey){
			curlURL += "&" + singleKey + "=" + theParameters[singleKey];
		};
	};

	curlURL += '"';
	var:string curlRet = runCommand(curlURL);

	return curlRet;
};

Now let’s call this function in a stamp for example:

var:dictionary myParameters;

myParameters["lat"]     = $LocLatitude;
myParameters["lon"]     = $LocLongitude;
myParameters["units"]   = "metric";
myParameters["lang"]    = "de";
myParameters["exclude"] = "minutely,hourly,daily";
myParameters["appid"]   = $OpenWeatherAPIKey("gPreferences");


var:string myResult = callOpenWeatherAPI_Main_db(myParameters);
$Text = myResult;

US and English are the default. So you could simply drop two lines:

myParameters["units"]   = "metric";
myParameters["lang"]    = "de";

and there is no need to alter the function (if you like to test it: you need an API key from OpenWeather).

Detlef

1 Like