Creating an $Amount attribute & formatting its value

In computer science a string is essentially "… a string is traditionally a “…a string is traditionally a sequence of characters…” (per Wikipedia). Those characters might be letters, numbers, emoji, symbol characters, etc. What we lay folk might describe colloquially as ‘text’.

So a string-type attribute holds a sequence of characters. In Tinderbox a String-type attribute holds one value, insofar as what every characters you type in, it treats them a one pieve of text, so to speak.

But, you say, in Tinderbox I write a list as Hat;Lantern;Map;Latern. Yes, and if stored as:

  • a String, Tinderbox understands it as the single value Hat;Lantern;Map;Latern even if when shown like that makes no sense when read.
  • a List, Tinderbox understands the semi-colon as representing a list item delimiter. Thus it finds not one value, but four: Hat, Lantern, Map, and Latern.
  • placed in a set, which de-dupe lists, detects and stores only three items. Why? Because Lantern is repeated in the source data so only one copy is saved, i.e. the attribute stores Hat;Lantern;Map.

So if entering a list (List or Set) attribute value manually, you type the characters as if a String but adding a semicolon between every discrete item.

This is why attribute data types exist. It helps signal intent:

$MyNumber = 4; $MyNumber = $MyNumber + 4; → gives a result of 8, the sum of 4 added to 4.

$MyString = 4; $MyString = $MyString + 4; → gives a result of 44, a string composed of the character 4 twice in sequence.

If you are unclear in you instructions (as to type/intent), Tinderbox will ‘guess’. In most cases correctly, but when it doesn’t, you see peoplle arrive here reporting they got an unexpected effect.

Why, since you say “since I’ll be referencing one amount per note”? That implies you want a single amount value per note, i.e. a single-value attribute.

If the source data is (not more than) one cost per imported note, initially put “$2,500.50” into a String type attribute. If you need to get a number out of that, this will work (remember the attribute names I use here are examples):

$AmountString = "$2,500.50";
$AmountNumber = $AmountString.replace("^[^\d]+","").replace(",","");
// $AmountNumber now holds the number `2500.5`
$AmountNumber = $AmountNumber * 3;
// $AmountNumber now holds the number `7501.5`
$AmountString = $AmountNumber.format("$");
// $AmountString now holds the string `$7,501.50`

So the one piece of data you really need is the numerical part of the code in Dollars (or Dollars and Cents if needed). As the example above shows if you have the number, it is possible to generate a ‘readable’ formatted string if needed.

If the worry is reading on screen, I’d simply show the Number version in Displayed Attributes. OK, $2,500.50 will appear as 2500.5. This is because a Number shows up to c.6 decimal places but omits trailing zeros. So ‘0.5.0’ appears as ‘0.5’. Still if you know the attribute holds a Dollar cost, the mind’s eye easily reads 25005 as $2,500.50.

HTH.

To back up and give more context to the actual project, I’m annotating campaign finance reports — which include donations and expenditures — and so each annotated note comes with one amount.

Because there are many notes, there are therefore multiple $Amount values (though some are identical dollar amounts). That’s why I imagined the data type might work best as a list — if that makes any sense. Having said all of that, I’d be happy to embrace your approach using a string data type instead. But let me know if you think that would work best, now that you and others better understand the background and context of what I’m aiming to do.

Thanks!

Assumption/constraint #1

As you point out each note holds one amount. Therefore each source note’s $Amount attribute needs to store a single number. Leaving aside, for a moment, what data format is used for $Amount…

Assumption/constraint #2

This suggests you will want to do mathematical operations (most likely summing) on the source notes’ costs ($Amount) values.

So, #1 suggests you don’t need a multi-value attribute and #2 suggests a Number-type is most relevant. Given that the implications of #1 and #2 do not conflict (Number-type is single-value), were it my project I’d use a Number type attribute for $Amount.

Meanwhile, for report purposes I’d use $Amount.format("$") internally and ^value($Amount.format("$"))^ for export to get a properly print-formatted currency figure. In other words, even if $Amount when seen in Displayed Attributes or column view may look like 2500.5 you know know it’s easy to render it as $2,500.50 where needed. But, to restate the underlying constraints, we’re storing the source figure as Number type so we can do mathematical ops on it.

Where I think you are getting confused is how you then handle/store data created from the starting $Amount. For instance—and using ‘MyString’, ‘MyList’, etc. as hints as to data type used (i.e. don’t blindly copy/paste), we may start with a lot of numbers—costs—in $MyNumber. Say we want to to get a listing of the costs for the child notes of a container. We could use code like:

$MyList = collect(children,$Amount);

Why a List and not a Set? Well, if 2500.5 turned up in two separate notes, a Set would de-dupe that and we’d ‘lose’ a figure we need in the listing. A List preserves all items on the order listed. The list’s size allows us to know the number of list items:

$MyNumber = collect(children,$Amount).count;

In this case the number will be the same as the container’s $ChildCount. If there were 10 child notes, in the last example above $MyNumber would be 10. But, what if we’d make a list of of non-zero contributions:

$MyList = collect_if(children,$Amount>0,$Amount);
$MyNumber = $MyList.count;

Now we find the number is perhaps 6 instead of the original 10, if only six child notes had an $Amount value set.

Notice in the code examples about how $Amount only shows as a source (on the right side of expressions) and we use other attribute (types) to hold computed values created from the $Amount data.

“But”, you say, “what if I want to ‘Case X’ to store the total contributions (i.e. sum of its child note $Amount), don’t I use $Amount for that?”. You could, after all this is a sum of $Amount figures. You might use a code like this:

$Amount = sum(children,$Amount)

using sum() as we know $Amount is a Number-type. The container’s $Amount now correctly holds that sum. But, then we decide to find out how many discrete contribution costs there are. Simplest would be to find all the notes with a $Amount value and take the count of found items. Dang, that will fail as our containers are using $Amount to store sub-totals of costs. So, because we’ve thought ahead, we might think it wise to make a new user Number attribute $CaseAmount and use this code instead of the above:

$CaseAmount = sum(children,$Amount)

Now, if you interrogate $Amount, without adding any filters (Extra scope/query terms) you know that you will only ever get back figures relating to campaign finance sums and not any calculated figures based upon them.

Before you ask, there is no hard-and-fast rule as to when to add another attribute; but the act of adding an extra attribute doesn’t affect performance . We all vary in approach facility with abstracting process. Some folk, in the above scenario, may prefer to re-use $Amount for different purposes in different notes because they are happy to apply appropriate filtering to their finds and queries. At the other end, if the latter is difficult, make a new attribute for each discrete significant strand of detail (q.v. $Amount and $CaseAmount in the example above). But, there is no right or wrong single answer, you the user should decide to fit your work style and facility with coding & automation.

In the same vein, if you find it hard to mentally read 2500.5 on screen and ‘see’ $2,500.50', that you could make a String type attribute AmountString` and in your source notes show it as a Displayed Attribute and use this code (as a rule or edict - or when doing initial OnAdd):

$AmountString = $Amount.format("$");

The result of that? If $Amount holds 2500.5, $AmountString holds (on a US locale Mac) $2,500.50.

Lastly, don’t forget (as we’ve tripped of this before), that my use above of sandbox attribute names in example code like ‘MyString’ or ‘MyList’ aren’t a requirement that you must use same-named attributes but a hint as to the type of attribute. So, where I use ‘MyString’ you should read that as ‘use a String type attribute’. Ideally use you own attributes and with names that make sense to you, in the way ‘Amount’ is more helpful in your project than 'MyNumber"—assuming ‘Amount’ is a Number-type attribute! :wink:

HTH :slight_smile:

1 Like

One edge case springs to mind. So, you’ve done the above, imported several hundred notes. All is well but you realise some notes actually contain more than one contribution value. Why doesn’t matter but you now face a problem with regard to how to set $Amount.

At this point I’d ask myself “is this note truly one ‘event’?”. IOW, are there two—or more separate contributions here that need discrete annotation? At this point you might choose to go back and alter the source notes. But, for now, let’s consider that not possible for now. Instead, answering the preceding question…

No, is this is not truly one ‘event’. If so, I’d clone the note, with one copy per discrete contribution value ($Amount) within the original note’s data. Now rename/edit each to reflect the split and annotate in every one the source note—future self forgets this and in a year’s time you’ll be scratching your head trying to figure the original source, likewise other team members may not guess this step unless you annotate it.

Yes, is this is truly one ‘event’. Here, I’d add the contribution values and put the sum in $Amounts. Again, to future self or for other team members annotate in the note that you’ve done this: e.g. the $Amount value reflects the sum of 3 different costs found in the current $Text.

This again shows how making one’s process ‘rules’ too hard-edged generally backfires. Luckily, Tinderbox is very flexible.

If you were tempted to add an $Amount2, $Amount3, etc., That is possible but it makes the previous calculations difficult. If going that route, I’d also make an $Amount1 and an edict:

$Amount=$Amont1+$$Amount2+$Amount3;

Though possible this is perhaps sub-par as if you then find to need an $Amount4 you’d need to re-do all the edicts. But, as ever there is more than one way.

In reality, one factor is a case of how easy it is to fix the data at source (i.e. upstream, outside Tinderbox) and re-import. Obviously, if you’ve already done a lot of work in Tinderbox before discovering such errors re-importing a ‘cleaner’ set of source data becomes a less palatable option. This also reinforces the fact that is starting your TBX, or the main work in your TBX by importing data, it is well worth doing a several tests looking for, and fixing edge cases before moving onto work done only within Tinderbox.

1 Like

Thank you so much for such thoughtful, detailed answers, @mwra. I deeply appreciate your help and generosity, as per usual. You’ve given me a lot to process and consider.

I hadn’t actually thought of using $Amount values to do mathematical operations. But reading through your explanations about how I can use $Amount values makes me think that my original thinking was too shortsighted, and that it’s best to use #Amount as a numbers value type just in case I’d like to use any $Amount values for prospective mathematical functions.

I also like the idea of using $AmountString as a way to format $Amount values, and might pursue that, too.

Right now, I need to be sure I can get the annotated data into the $Amount values cleanly through the process you helped me with before (i.e., MarginNote > OmniOutliner > Tinderbox). I managed to set up date formats properly, but I’m now having trouble rendering and importing number values (and categories / attributes). But that’s a problem that I’ve got to sort out!

Thanks very much again; I’m very grateful for your help. I’ll try my hand at some of the things you’ve laid out, and report back.

If I recall your using an OMPL pipeline from MarginNote to OmniOutliner (to make clean/usable OPML) then to Tinderbox.

I’ve not time to look that the whole system, and I’ve moved Mac since we worked on the pipeline (so I’ve no old files). Thus a quick questions: how is the ‘amount’ data included in the OPML? If your OmniOutliner-generated OPML has an ‘Amount’ column that will map to, or make an, Amount attribute. If you pre-create the attribute in the TBX before import and set it as Number type, that will avoid the possibility of import generating a string-type attribute. Basically, if the OPML attribute name matches (case-sensitive, IIRC) an existing attribute, the OPML attribute value is placed in the Tinderbox above. If not a new attribute is created in Tinderbox and these generally default to a String type.

HTH

1 Like

Thank you, @mwra. You’re right: in theory it should work, but I haven’t figured out how to assign a number to Amount that works with my MarginNote > OmniOutliner process. But that’s not your problem; that’s for me to figure out! I think the script I’m using just needs to be tweaked, but it’s outside of my knowledge base.

Anyway, I manually included the numbers into the Amount fields within OmniOutliners, exported the file as an OPML, dragged and dropped it into Tinderbox, and it worked. So, that’s good. Thanks again for your help!

2 Likes

Really glad to hear your making progress. Having been data-munging for years, I feel your pain. somtimes, a bit of manual review/fixing is better, 'cheaper (time/energy/sense of humour)!