List attribute options

(Keith Lancaster) #1

I am setting up a prototype and have added an attribute of list type. The default options are “none” and “normal”, neither of which seem to do anything. I need to have a list that contains, for example, “Melanoma” and “Benign”, but can’t seem to determine how or where I would create this list. What I actually want is for the value to be determined by a combination of other attributes (booleans), but for now just having the dropdown would work.

(Paul Walters) #2

In the attribute inspector you can set the suggested values for a list attribute. Separate values with a semicolon. Or you can set them in the key attributes for your prototype and the values will be available to notes that use the prototype.

I’ll leave it to others to explain how to write rules to set values based on Boolean tests. You’ll want to tell the audience more about the test you’re anticipating.

(Keith Lancaster) #3

Thanks for the quick response. I had stumbled on that dialog, but had not found (although I am sure it exists) information as to how to fill in the defaults section. I appears that using a list allows me to select more than one item, when what I was actually going for is one or the other. That said, this gets me going. Thanks.

(Paul Walters) #4

Not defaults. “Suggested values”. (Though a default can also be set.)

I don’t know what that means. You can select as many, or one, or another, value from a list as you wish. It does not require anything.

Lists (and sets) will accumulate suggested values as you use in them in your documents. In the $FavoriteFruits list in one note I might add “persimmon”. In $FavoriteFruits in another note I might add “custard apple”. And by the time I get to the third note I can choose from either, or, or both “permission” and “custard apple” or I can ignore them both and add “lychee”.

The best way to figure out Tinderbox is to use it instead of reading about using it.

(Mark Anderson) #5

If I understand correctly, you have a multi-value attribute and you want a single value. In the first case, a new value re[eats/adds to existing values. In the second case the new value replaces the existing value. Assuming you want the latter you need to use String type attribute. You can still use the method discussed above to pre-populate the value list as discussed above.

Without using 'suggested value’s the pop-up list only shows the two reset values (none/normal) plus an values as currently used. So if you you added a note and gave $MyString that value ‘apple’, moving to another note the pop-up value list for $MyString would show “none, normal,apple” because at least one already uses the value ‘apple’

The none/normal values are two different empty values. ‘none’ sets the attribute to nothing (and empty string , i.e. “”). ‘normal’ re-sets inheritance. If the attribute’s default (or prototype’s value is “” that is inherit or otherwise the note inherits the default value. So if the default value of $MyString was ‘pear’, all note’s would have the $MyString value of ‘pear’. Setting a note’s $MyString to ‘none’ would mean that note would atypically have no value ofr $MyString. Setting it then to ‘normal’ would re-enable inheritance and thus the attribute default of ‘pear’ would be inherited once more.

@PaulWalters is right, it really is worth trying things out, e.g. the one in the paragraph above. Do it in a new file so you can throw it away once you’ve experienced and absorbed how it works. I’ve used Tinderbox for yours and this is still what I dod with every new feature: make a new file, add the minimum extra - if needed - to allow test and then see what happens. Was it what I intuited or not?

TL;DR - you need a String type attribute and you need to use the Inspector to set the attribute’s ‘suggested values’ (also see here).

(Bruno Moreira) #6

Hi Keith. I’ve learned a lot from experimenting with TBX, reading the documentation, but especially from the forums, others users and examples. Based on all the knowledge gathered from this thread, I’ve made this TBX for you to dissect it:

You’ll see a prototype note, and three notes that inherit that prototype. The prototype has a string-type $calculatedAttribute with “Melanoma;Benign” as suggested values (in document inspector / user) and also attributes $A and $B which are your booleans. The prototype has this rule defined:

if ($A & $B) {$calculatedAttribute="Melanoma"} else {$calculatedAttribute="Benign"};

which makes $calculatedAttribute become “Melanoma” is both $A and $B are checked (true), or “Benign” on other cases (note: I’ve disabled the rule from the prototype itself, for it to be applied only to notes which have it assigned).

You can test with the notes in the file, or create new notes and assign the prototype to them.

(Mark Anderson) #7

Thanks for that neat demo (extra credit for use of $RuleDisabled :grinning: ).

On the logic side of things this article on conditional statements might be of help if developing more complex logical arguments.

(Keith Lancaster) #8

Wow! Thank you all for the suggestions and taking the time to respond. I will try all of this out later today and let you know how it goes.

(James Fallows) #9

Thanks for this reminder. I knew there was a way to do this but had forgotten what it was.

(Mark Anderson) #10

There is upside to using the ‘suggested values’ route. In the past, as @PaulWalters describes, the (only) approach we had was to load all the values (if a List) into the prototype or, worse, for String (single value) add a note to hold each discrete value. A downside of that approach is if you you want to use values() to fetch the actual used values of an attribute, the ‘seed’ values get included and thus mess up the count.

With the ‘suggested values’ approach, unused values form the suggested list are not recorded by values().

The only thing to watch is the Inspector boxes are quite small. If you need to add 10s or 100s of suggested values, you may do better to set them out one item per line in a plain text file, check items are correct (e.g. spelling) then replace line breaks with semi-colons, select all, copy and paste to the Inspector’s suggested values box.

(Paul Walters) #11

Or you can used a code note, with $Text==standard values semi-colon delimited, and use a stamp to set $MySet in a prototype to $Text of the code note.

(James Fallows) #12

Mark A, I am going to use this as an “asking in public” moment about a process, possibly to help others along their TB discovery trail).

  • Let’s say I have an existing file with a lot of already-established values for a certain attribute. Let’s say they’re city names–London, Slough, Reading, Stoke-on-Trent, and so on.
  • I am creating a new file, and I would like to pre-populate its attribute list with existing values from the old file.
  • Is the simplest way to collect all existing values for an attribute from one file, so that I can cut-and-paste enter them into the “suggested values” box for an attribute in the new file, just to put the following code into a Rule for the parent container? $Text=collect(descendants,$Category).unique

That seems to give me a semicolon-separated list of the values, which I could then paste into the other file.
Just checking to see if this is The Approved Way. Thanks jf

(Mark Anderson) #13

Yes and no. If you want all values of $Category, for the whole document you could use values(), which returns a Set-type list, which is de-duped but whose sort order can’t be assumed. I’ll come to what you do with the list further below:


But, lets say you only want $Category values for part of the document, perhaps the descendants of the current container. Now we have to use collect() (or depending on the criteria collect_if()):


Depending on the values you’re collecting you might consider using .isort instead of .sort so as to give a case-insensitive sort.

If you just want to save the data for the next document I’d add $MyList as a KA and use either of:

$MyList = values("Category").sort
$MyList = collect(descendants,$Category).unique.sort

By passing the list into a List-type attribute we ensure it doesn’t get re-sorted. If you then click into the KA all the content (including bits off-screen) are selected and you can copy/paste to elsewhere. If less trusting, once the focus is in the value box use Cmd+A as an explicit ‘select all’ before doing the copy paste.

But, maybe you’d like to review the list of values as well, or before you move them on. You could just pass the value() or collect() call in to $Text but that gives you all the values run together with semi-colons. Easier to read is to use the .format() with a line-break delimiter. So:

$Text = $MyList.format("\n")

Now $Text contains one value per line which can make it easier to review.

Does that round things out clearly enough? I know I’ve given options, but only to pre-empt some obvious folllow-up questions. :grinning:

(James Fallows) #14

Mark A – very useful and clear, thank you!

One after-action observation, one question.

  • Observation: this pairing of code is interesting, in highlighting the difference between putting an attribute name in quotes, but without a dollar sign –“Category”–versus using no quotes but with a dollar sign, as with $Category. I forget the rule about which you use when, but I see that the proper functioning of each of these lines of code depends on which you use.
  • Question: when I am setting up the likes of $MyList to receive the results of either values or collect, does it matter whether I define $MyList to be “set” versus “string”? Or will it auto-figure it out?

Again thanks for the clear and patient explanation.

(Mark Anderson) #15

Q1. Arguments using an attribute name with no prefix. This occurs where the app wants a literal string that is the name of an attribute. values(), the value lists of $KeyAttributes and $TableHeading (others?), the column name selector for column view (and in AB view columns) and in the KA chooser panel all fall into this category. The difference:

$AttributeName is a placeholder for the value of attribute ‘AttributeName’.
`“AttributeName”’ is a string representing the actual name of attribute ‘AttributeName’.

I’d agree that in the column heads and KA selector the distinction is moot. However, what’s obvious in our mind’s eye may be less obvious for a code algorithm to divine. So, Tinderbox likes us to help it out by indicating whether we’re stating the name or using the attribute’s value.

  1. $MyList is a built-in List. I was being lazy, using the general $My… to mean an attribute of that specific type, i.e. here List type. The convention started before Tinderbox helpfully added a predefined $MyString, $MyList, etc.

So to be clear, yes values() returns a List-type and collect() as Set-type but both are essentially lists. I pass them to a List-type attribute because:

  • We’ve either sorted the output of the supplying function (or we don’t care) but we don’t want the receiving attribute to change the order of the list of values.
  • List to List, or Set to List will not change the supplied ‘list’ (as in a generic list of multiple values - sorry I need another word for list here!).
  • Set to Set, may result in a change in value order. You won’t know, but if order matters…
  • List to Set will de-dupe the list if dupes are present and may result in a list order change, etc.

Thus passing a list of either type to a List is attribute is safest.

You can pass to a string as that essentially concatenates the incoming list using semi-colons. It is the same as more explicitly doing $SomeString==$SomeList.format(";"). In the absence of a format string Tinderbox uses the normal list value delimiter. In fact, it is what you see with a Set or List when shown as a KA - all the values with a semi-colon delimiter.

If the way you originally described feels right for you, then that’s fine too as long as you’re happy with the fact that values() is always whole document scope. In most cases that’s fine but occasionally you may need collect() for more limited scope. Indeed, values() had its origin in an easy way to ‘just’ get all the unique values used in a given attribute.

(James Fallows) #16

Mark, thanks again. Really appreciate your time, patience, and lucidity.

At this point:

  • I do understand the collect() / values() distinction, which I hadn’t focused on before.
  • I understand the various ramifications of sorting, de-duping, and all the rest.
  • I now understand the obvious-once-you-point-it-out conventions about $MyList, $MySet, $MyDate, and so on. And…
  • It is conceivable that at some point I’ll internalize the “Category” / $Category distinction! For now I’ll leave it in the realm of the Albigensian heresy or other topics I know are significant but that I couldn’t quite explain to anyone else. (Seriously, thanks for your patience and generosity to the community.)

(Mark Anderson) #17

Thanks for that summary. On the “Category” / $Category, if you get to a point where not sure which for to use in action code (and UI boxes) try using these expansions as substitution tests:

“Category” => ‘a string that is the name of the attribute called Category
$Category => ‘the current value of this note’s attribute called Category

I think in most of the situations described thus far, one of the long descriptions will be an obvious non-fit.

Confusion arises because of the ad hoc convention when discussing Tinderbox in forums, tutorials, etc., of using the $-prefix form for both the cases above! IOW, using $ to distinguish between the word ‘price’ and an attribute called ‘price’. In the latter case, we might write ‘$price’ to indicate we’re talking about either of the above ways to refer to an attribute (i.e. name vs. value) given that ‘price’ is a valid, if unusual name. Indeed, part of the convention of using CamelCase names for attributes is exactly so they can’t easily be mistaken for ‘normal’ words that we might use in string values.

So, when using values() with the Category attribute we want ‘all the values of the attribute with the name Category’. So consider:

values("a string that is the name of the attribute called Category")
values("the current value of this note's attribute called Category")

The second option wouldn’t make sense, so we don’t need a $ prefix. Now collect(): e.g. ‘for every descendant of this note fetch its value for the attribute named Category

collect(descendants, a string that is the name of the attribute called Category)
collect(descendants, the current value of this note's attribute called Category)

Now the first form doesn’t make sensor makes less immediate sense, so go with the $ prefix.

There’s a confusing edge case: sometimes an attribute can hold the name of an other attribute. This lets us do things like values($MyAtributeName) which allows the attribute whose values are being sampled to vary depending on the value of $MyAtributeName. That’s confusing to a very new user ("…but values() takes a quoted string"), yet is the sort of applied use an experienced user needs. In aTbRef I try to spell out these alternates precisely so people can look in moments of confusion, especially as not all action operators allow this switcheroo of replacing a quoted string literal value with an attribute (holding a similar value).

Of course, if you do make an error, Tinderbox may just work anyway. The explanation of that is because up until about v4 you could write:

A = B

At that time the left side could only be an attribute (thus $A in current terms) and if and attribute $B existed, then $A would take the value of $B. If no attribute $B was defined at all, $A would take the value “B”. Tinderbox also knew that if A = B was in a query, then that the ‘=’ implied an equality test instead of a value assignment. Such code examples linger. I’ve tried flushing them from aTbRef, Tinderbox’s Help and the cookbook but they linger on elsewhere, e.g. in old demos and more importantly in user’s files.

However, since then, Tinderbox action code has got significantly more complex. To the user our intent is clear, in our minds eye. We simply don’t, especially non-programmers, see a possible multiplicity of syntactic ambiguities and edge cases in the code we wrote; Tinderbox can’t guess, it has to figure it out. Thus the move in later versions to $-prefixes, quoting strings, = vs. ==, etc.

A lot of words to get to this point, but I hope it gives you a rubric to help figure out when you need a $-prefix and when you don’t.

[I’ll probably move this essay to a web page and expand it a bit, but other work beckons just now]

(James Fallows) #18

Mark A, again sincere thanks for taking the time to lay this out. In principle, I understand now! Thanks again.

(Andreas Grimm) #19

@mwra may I suggest that helpful discussions like this one will be referenced within or, say, linked to from the respective entry within aTbRef

(Mark Anderson) #20

Yes, this crops up enough I ought to find a place to put it.