Creating a Dictionary Lookup for an attibute value?

I seem to be getting stuck creating my first dictionary lookup item.

I have an attribute called $HighlightColor with one of several values

Example
Device RGB colorspace 1 1 0 1 = yellow
Device RGB colorspace 1 0.5 0 1 = orange

I have created a custom attribute Dictionary named $ColorDictionary

Here is the code for the function i used (which may not be right, this is my first attempt at creating a function and using a dictionary)

function fColorDictionary(input){
$ColorDictionary=$ColorDictionary("Device RGB colorspace 1 1 0 1:yellow;Device RGB colorspace 1 0.5 0 1;orange ")
}

Goal: I am trying to create a lookup for my $HighlightColor to lookup and replace the RGB color value with its corresponding color

I am not sure how to link the two. I am hoping to create an edict within a prototype called pRdNote

TestingColorDictionary.tbx (599.1 KB)

Thanks in advance
Tom

Several immediate points stand out…

Creating the dictionary. You have created a used dictionary attribute $ColorDictionary. But you haven’t set it. This

$ColorDictionary=$ColorDictionary("Device RGB colorspace 1 1 0 1:yellow;Device RGB colorspace 1 0.5 0 1;orange ")

…is setting the entire value of $ColorDictionary for the note calling your function to the value of the same attribute whose $Name, improbably is "Device RGB colorspace 1 1 0 1:yellow;Device RGB colorspace 1 0.5 0 1;orange " (even down to the trailing space!). No matter, I think what you meant to use was (note the corrected ; to : in the second term):

$ColorDictionary=dictionary("Device RGB colorspace 1 1 0 1:yellow;Device RGB colorspace 1 0.5 0 1:orange ")

i.e. using the dictionary() action operator to make Dictionary-type from the supplied string—which of course is actually supplied as a list of value:key; pairs.

So not the function populates $ColorDictionary of the note calling the function with two key:value pairs: Device RGB colorspace 1 1 0 1:yellow; and Device RGB colorspace 1 0.5 0 1;orange. Still not really helping.

We either want to store this dictionary in the $ColorDictionary of a single specific note or make a dynamic Dictionary-type variable inside the function and access values via function input, i.e. a look-up wrapper.

Using an attribute

I’ll assume we’ve a note called “_config” (i.e. at $Path /_config) to hold the data. Where you put such a note and what you call it matters not except do make the name unique (thus my use of an underscore prefix), making it addressable via $Name alone. Obviously, if differing the title adjust example code below accordingly. We’ll make a Stamp “Set Colour Dictionary”, and assume it has all the key:value; data needed:

$ColorDictionary=dictionary("Device RGB colorspace 1 1 0 1:yellow;Device RGB colorspace 1 0.5 0 1:orange ");

We use it to stamp our “_config” note. I see from the demo that data notes store the dictionary’s key values on their $HighlightColor:

So for the above note to correctly get the colour value of the key “Device RGB colorspace 1 0.5 0 1” it would call this code:

$MyString = $ColorDictionary("_config")["Device RGB colorspace 1 0.5 0 1"];

Tada!:

or, via the value of $HighlightColor:

$MyString = $ColorDictionary("_config")[$HighlightColor];

Using a function

We can employ a function() to store the dictionary. Here’s our function, commented to show how:

function fColorspaceLookup(iDevice){
   // iDevice is a string input with a dictionary key value  

   // Define a Dictionary-type variable
	var:dictionary vColorDict;
   // store device-colorspace:color key:value pairs as needed
   vColorDict.extend("Device RGB colorspace 1 1 0 1:yellow;");
   vColorDict.extend("Device RGB colorspace 1 0.5 0 1:orange;");
   // dictionary now complete

   // return the colour value matching iDevice
   return vColorDict[iDevice]
}

I know I’m adding key:value pairs one at a time but there may be many and they may change over time. this approach makes things more maintainable IMO. Feel free to write code more concisely if the lack of concision offends. :slight_smile:

New we can use this code:

$MyString = fColorspaceLookup("Device RGB colorspace 1 0.5 0 1");

TaDa, again!:

or more realistically using $HighlightColor:

$MyString = fColorspaceLookup($HighlightColor);

So, which approach is better?

As we know there is no one right way! Use whichever appeals.

Here is your earlier file with my code added: (‘_config’ note, new function, stamps, and four test notes. The test notes are pairs. The first feeds the target dictionary or function a string, the second uses a the value of $HighlightColor.

Here: TestingColorDictionary.-ed.tbx (629.2 KB)

I think that covers it :slight_smile:

3 Likes

Excellent. Many thanks MarkA. I like the TaDa’s!

Once you explain it, I now get it. Here is a very brief summary of your summary. Thank you for explaining both approaches:

  1. Using the /_config approach // in order to store and update the values with a note OR
  2. Using the function approach to make a call to the function to populate the values.

my thoughts …

I am planning to move forward with the function approach. Why?

  1. More modular and able to easier call and use in other tinderbox documents.
  2. Functions are new for me, this will be good practice.

BTW everyone…the RGB colorspace format is what SKIM uses to export the color of the highlight note rather than a color.

This might be useful if anyone uses SKIM. My pdf workflow starts with PDF Expert (I like its interface and annotations for both iOS and Mac, but for export I prefer Skim because i can customize the template: .md, tsv, csv and import directly.

Lastly, the other lesson learned here is how important it is to create a test document and work out your code before going live. In this example, I have renamed the attribute twice to customize its name to remind me of its use in Skim and added a description. Thanks MarkA, you have reminded us many times of this best practice.

This function will certainly come in handy for me…

Last question, in case i wanted to create an edict in a prototype to call the function
i assume i can call the function simply with

fColorspaceLookup($HighlightColor) in order to populate (this) note.

Thanks Mark
Tom

Good. I offered both to avoid an inevitable “why not use [some other method]” follow up. Both work. Putting my personal ‘hat’ on , I think the function route is easier to maintain (the individual key:value; pairs are easily seen edited/amended. Plus the library can easily be moved to/shared with other documents or users. The function itself required no attribute for storage as the dictionary data is in the function code an assembled as needed: @eastgate might have a view as to whether that is best practice. I assume for a few terms it’s moot. If the dictionary was 100 terms, then building it each time the function is run (if indeed the function runs often) it might be a different case.

Almost, but you will need to pass the function’s return value into a local attribute to make use of it, for instance putting it in $MyColor:

$MyColor = fColorspaceLookup($HighlightColor)

QQ: I noticed

“iDevice is a string input with a dictionary key value”,

Is ‘iDevice’ as specific input variable term or is can it be any name I choose? It would be my assumption it could be any variable string name, such as "iMyInput’ but would need to be replaced, in both places

at the top
function fColorspaceLookup(iDevice){ → function fColorspaceLookup(iMyTerm){

and at the bottom
return vColorDict[iDevice] → return vColorDict[iMyTerm]
}

Correct?
Thanks
Tom

It is specific in that it names the first (and here, only) function argument. Wherever that argument is referenced within the function, the same name must be used when referencing the argument from within the function. So, you ask:

Yes. You are changing the variable name used to define the argument, so the argument name and all references to it within the function must be the same name.

Essentially, by naming an argument you are also implicitly defining a variable of that that same name that must be used within the function if desiring to get the value of the argument, as passed in by the action code.

I’ve just written a significant addition to the aTbRef article on function arguments. I’d be interested to know if it helps explain things a bit more clearly. If not it would be extremely useful to know at what point people lose track of the subject being explained.

†. Note, my expectation the article is read in full and as part of the overall section in Functions. It is simply too complex to try an answer each possible narrow question (and the resource is a reference not a tutorial).

1 Like

I will begin reading it in full today and give you some feedback. On behalf of the Tinderbox community, we are very grateful to have your kindness, generosity and work.

“it would be extremely useful to know at what point people lose track of the subject being explained.”

funny, I feel I am the poster child of people running off the tracks. :slight_smile:
Tom

1 Like

Far from it, it happens to us all.

@mwra

A while ago I cribbed some code from here, and used it to create a table that would look up a character appearing in a name attribute, and then use that to set the prototype of said note.

Now for some reason after the latest update to 9.51, it has stopped working. And so I am wondering, if it is just me and I have introduced a gremlin elsewhere, or there is something in the dictionary syntax used below that is now deprecated.

Here is what I have currently:

function fSlipperMarkLookup(iMark){

var:dictionary vSlipperTable;

vSlipperTable.extend("ⓢ:slip p;");

vSlipperTable.extend("ⓡ:riff p;");

vSlipperTable.extend("ⓟ:peg p;");

vSlipperTable.extend("ⓠ:quotation p;");

vSlipperTable.extend("ⓓ:docket p;");

vSlipperTable.extend("ⓘ:index p;");

vSlipperTable.extend("⒬:question p;");

vSlipperTable.extend("Ⓛ:lead p;");

vSlipperTable.extend("ⓕ:fragment p;");

vSlipperTable.extend("ⓜ:method p;");

vSlipperTable.extend("ⓩ:zettel p;");

vSlipperTable.extend("ⓣ:time point p;");

return vSlipperTable[iMark]

}

Then there are two other functions which reference the process.

function fBlessRawSlugs(){
fDefineSlipperType();
$Prototype=fSlipperMarkLookup($SlipperMark); 
$Name=$Name.replace("[ⓩⓣⓓⓘⓟⓠⓡⓢⓁ⒬ⓕⓜ]\s?","");
if($Prototype=="slip p"){fDisappearMapName_slip()};
}

function fDefineSlipperType(){
if($Name.contains("([ⓓⓣⓩⓘⓟⓠⓡⓢⓁ⒬ⓕⓜ])"))
{$SlipperMark=$1};
}

I am totally stumped. But it has to be said that I am also not that well-versed in the syntax of how variables and functions work, so I am quite easily stumped when it comes to this level of programming.

I am hoping that Mark or someone else with a steady eye can see what is afoot.

all best wishes,

Gavin

I’m not sure, but I have a hunch. From aTbRef here but just updated :

From v9.5.0, the Dictionary.extend() operator takes a single argument, a dictionary—using the new {} syntax of key:pair elements which will extend the current elements.

$MyDictionary = $MyDictionary.extend({pear:green});

It turns out and this looks like bug/‘feature’ that when using .extend() the ‘key:value’ string must not be quoted or it fails. As this breaks all Tinderbox notions of string being quoted, I’ll wager this is accidental breakage.

Normally, using (double) quote simply informs Tinderbox that the input is a literal string, in case the parser might think otherwise. As shown above that notion fails.

So for now, using the pattern:

vSlipperTable.extend(key:value);

Hopefully, if normality is restiored this will be allowed:

vSlipperTable.extend("ⓜ:method p;");

But, as at v9.5.1, quotes seem to break the intended outcome.

HTH :slight_smile:

The quotes may not be the problem.

I realised that the two function examples in your test file above (TestingColorDictionary.-ed.tbx) did not work unless the code was changed to “establish” the dictionary before “extending” it.

This syntax still works:


function fColorspaceLookup(iDevice){
   // iDevice is a string input with a dictionary key value  

   // Define a Dictionary-type variable
	var:dictionary vColorDict;
   // store device-colorspace:color key:value pairs as needed
   vColorDict=dictionary("Device RGB colorspace 1 1 0 1:yellow;");
   vColorDict.extend("Device RGB colorspace 1 0.5 0 1:orange;");
   // dictionary now complete

   // return the colour value matching iDevice
   return vColorDict[iDevice]
}


And this may work:


function fSlipperMarkLookup(iMark){

var:dictionary vSlipperTable;

vSlipperTable=dictionary("ⓢ:slip p;");

vSlipperTable.extend("ⓡ:riff p;");

vSlipperTable.extend("ⓟ:peg p;");

vSlipperTable.extend("ⓠ:quotation p;");

vSlipperTable.extend("ⓓ:docket p;");

vSlipperTable.extend("ⓘ:index p;");

vSlipperTable.extend("⒬:question p;");

vSlipperTable.extend("Ⓛ:lead p;");

vSlipperTable.extend("ⓕ:fragment p;");

vSlipperTable.extend("ⓜ:method p;");

vSlipperTable.extend("ⓩ:zettel p;");

vSlipperTable.extend("ⓣ:time point p;");

return vSlipperTable[iMark]

}

There are two separate issue at play here:

  • .extend syntax. Some of my examples above were wrong
  • a problem with var:dictionary objecting initialisation.

.extend()

There are now two syntax versions (the latter needing v9.5.0+). Older:

$MyDictionary.extend("key-name","pair-value");

uses 2 arguments, both quoted and with a comma between; white space before/after the comma is ignored but the comma is required.

Newer, using the { } dictionary declarative mark-up:

$MyDictionary.extend({key-name:pair-value});

No quotes needed. Colon between the key and its value is required. Whitespace around the colon is ignored. Quotes are not needed. I think this is because the { } indicate clearly enough the key:value string and the parts then split out at the colon. Pending clarification from @eastgate, assume quotes should not be used in the second form.

So, @GavinRees’ code:

vSlipperTable.extend("ⓘ:index p;");

Should be either:

vSlipperTable.extend("ⓘ","index p");
// or
vSlipperTable.extend({ⓘ:index p});

Note the semi-colon is the pair value is not needed (my fault - an earlier bad example by me!).

Next, an interesting catch by @Estomm. Something has changed since my original tests were written as the worked at the time. One issue, which @Estomm’s code resolves is that Dictionary.extend() used to work on an empty Dictionary-type var but now doesn’t. Building off the suggested revised function above, I tried a number of approaches to initialise vColorDict:

var:dictionary vColorDict; // FAIL
var:dictionary vColorDict=dictionary(); // FAIL
var:dictionary vColorDict=dictionary(""); // FAIL
var:dictionary vColorDict=dictionary(":"); // SUCCESS

So whilst we could use (using suggested @Estomm’s function and fixing some .extend() syntax):

function fColorspaceLookup(iDevice){
   // iDevice is a string input with a dictionary key value  

   // Define a Dictionary-type variable
   var:dictionary vColorDict=dictionary(":");
   // store device-colorspace:color key:value pairs as needed
   vColorDict.extend({Device RGB colorspace 1 1 0 1:yellow});
   vColorDict.extend({Device RGB colorspace 1 0.5 0 1:orange});
   // dictionary now complete

   // return the colour value matching iDevice
   return vColorDict[iDevice]
}

This makes more sense:

function fColorspaceLookup(iDevice){
   // iDevice is a string input with a dictionary key value  

   // Define a Dictionary-type variable
   var:dictionary vColorDict;
   // store device-colorspace:color key:value pairs as needed
   // first call initialises the dictionary
   vColorDict = dictionary({Device RGB colorspace 1 1 0 1:yellow});
   // subsequent additions use .extend() call, per needed key:value pair
   vColorDict.extend({Device RGB colorspace 1 0.5 0 1:orange});
   // dictionary now complete

   // return the colour value matching iDevice
   return vColorDict[iDevice]
}

I think the fact that explicitly declaring a Dictionary-type var, as at v9.5.1, fails to correctly initialise a Dictionary is a bug that will doubtless get fixed. Happily, as already discussed there is a work-around.

I’ve uploaded a suitably amended TBX here, TestingColorDictionary.-ed-3.tbx (951.9 KB), that adds a new function fColorspaceLookupNew() that incorporates the dictionary initialisation issue and uses the newer {}-based method of declaring key:value pair data.

I’ve also updated aTbRef articles on:

HTH :slight_smile:

1 Like

HTH :slightly_smiling_face: == ({Yes !! :smiley:})

Thanks for the speedy sleuthing.

Functionality restored. As you can guess from the cryptic comment above went with the newer syntax and it is all working for me now.

I understand that this is also one to watch in case the syntax receives further revision.

all the best,

Gavin

Well a mea culpa here.as I think I screwed up my initial documentation. If nothing else, 4 articles in aTbRef got a needed buff.