Number/alphabetize lists

Let’s say have a note with five children, e.g., Item 1, Item 2, Item 3, Item 4, Item 5.

I want to create an action/expression that will collect the children and then insert and list them in text in the order they are in outline and prefaced with bullets (•), letters (a-b), numbers (1-2), or Roman numerals (I-II). I want to control the character following the preface, e.g., . or ). I do not want to use HTML or markdown, looking for text.

The collection is easy.
the .each is easy.
The inserting of the action is easy (Note: I’m not sure how I’d use the new expression feature with this).
Also, I’m not sure of is whether Tinderbox and an operator do the auto number generation.

Can anyone help?

Do you know this thread?

Yes, I do. Thanks. I can certainly get the outline order and put that in a template. What I’m looking for is an operator to help me produce a numbers/alphabetized list that I can insert in text, i.e., an operator that I could call that would let me feed a list and specify the preface argument, i.e., alpha, numbers, roman numerals, the separator argument, i.e., period, parenthesis, dash, etc., and the break, e.g, “,”,“\n”. I’m not sure if this operator exists. If it did, I could see something like this:

var:list vList=collect(children,$Name);
var:string vStr;
vStr=vList.list(arg1,arg2,arg3);

arg1 options: “*”, alpha",“roman”,“number”
arg2 options: a text string, e.g., “. “, ,”) “,”–”
arg3 options: ", ", “\n”, “\n”

I’d then like to pass vStrv to an attribute, ^value()^, or an expression.

There are ways to get to this, but an operator of this nature would make it much easier and repeatable.

You’re looking for [number].format(); there’s even an option for roman numerals.

Further to the last, see Number.format(decimalsNum[, widthNum, padStr]|formatStr).

The only possible missing part is there isn’t an options to format numbers as letters, e.g. 1a, 2b. Not least all sorts of edge cases emerge, what to do at 27. Or, what about locale with extra letters on the roman alphabet (Iceland (ð,þ), Færoes (ð, æ), etc.)?

Yes, I’m primarily looking for letters, not numbers.

But you can make a list of letter codes!

function codify(n:number) {
    var:list codes=['a';'b';.......];
    return codes[n];
   }
1 Like

This is much better than my on the fly solution at the meet of using a look-up list. One small tweak is needed as we’ll number from 1 and list address positions number (in Actioncode) from zero.

Here is the result, lightly re-written in aTbRef’s more verbose notation, but only so as to assist those less familiar with code. So the function code:

function fCodify(n:number) {
   var:list vCodes=[a;b;c];
   // list position starts at zero
   // so subract 1 to input n
   var:number vNum = n-1;
   return vCodes[vNum];
}

The comments aren’t needed for use, but are to guide those re-using the code. So, to get note $text with the letter b we’d use code, e.g. a stamp, like so:

$Text = fCodify(2);

In @satikusala’s scenario where he’s using a 1-based counter vCnt to track which list item he is ‘numbering’, in the in-loop line assembling per-paragraph output into a vStr variable, the code would be more like this:

 vStr = fCodify(vCnt) + //...rest of line

Using a function makes the ‘make me a letter sequence prefix’ code more portable.

1 Like

Reminder to self to add something like @Eastgate’s solution into the aTbRef article on number formatting, or make a new article on ‘numbering’ lists for presentation/output.

1 Like

Here is the meetup where we discussed this thread:

Tinderbox Meetup Saturday 21 Jul., 2024: List Ordering from Children Action Code Working Session

Cool idea. Thanks.

BTW, during the meetup, we ran into a problem trying to pass * through the action code.
We tried this if(vCntType=="bullet"){vType='* ';}; We also tried escaping the the * what an . if you and in a different characters, e.g., “§§”, the code works. Is there a way to pass an * or do we need to do a text replace, e.g., replace §§ with * at the end, like below.

var:list vList;
var:number vCnt=0;
var:string vSuffix=$Suffix;
var:string vEnd=;
var:string vStr;
var:string vCntType=$TypeList;
var:string vType;
var:string vText=$DescriptionInventory;

if(vCntType=="roman"){vType='vCnt.format("X").lowercase';};
if(vCntType=="alpha"){vType='fCodify(vCnt)';};
if(vCntType=="number"){vType='vCnt';};
if(vCntType=="bullet"){vType='§§ ';};
if($End=="LB"){vEnd='\n';};
if($End=="comma"){vEnd=', ';};

vList=collect(children,$ShortTitle);
vList.each(anItem){
   vCnt+=1;
   vStr+=eval(vType)+vSuffix+" "+anItem+vEnd;
};
vStr=vStr.replace("§§","*");
vStr="\n\n"+vStr;
$Text=vText+vStr;

BTW, it took me less than 30 seconds to incorporate the @eastgate solution. :slight_smile: Very nice.

1 Like

Why are you performing eval on vType? That’s your problem. eval("foo") is the string foo, but eval(*) is a syntax error, because it’s an attempt to multiply without establishing a multiplicand :slight_smile:

I see; that makes sense. Putting a conditional will let us use the eval when necessary but not on the *.

var:list vList;
var:number vCnt=0;
var:string vSuffix=$Suffix;
var:string vEnd=;
var:string vStr;
var:string vCntType=$TypeList;
var:string vType;
var:string vText=$DescriptionInventory+" The deliverables of this are: ";

if(vCntType=="roman"){vType='vCnt.format("X").lowercase';};
if(vCntType=="alpha"){vType='fCodify(vCnt)';};
if(vCntType=="number"){vType='vCnt';};
if(vCntType=="bullet"){vType='* ';vSuffix ="";};
if($End=="LB"){vEnd='\n';};
if($End=="comma"){vEnd=', ';};

vList=collect(children,$ShortTitle);
vList.each(anItem){
   vCnt+=1;
   if(vCntType!="bullet"){
      vStr+=eval(vType)+vSuffix+" "+anItem+vEnd;
   }else{
      vStr+=vType+vSuffix+" "+anItem+vEnd;
   };
};
vStr="\n\n"+vStr;

$Text=vText+vStr;
1 Like

Hmm, neat. I took a different approach to this in a hack I bodged together a while back; I had a prototype with a boolean property - ‘OutlineNumberise’ - which just took a simplistic numeric outline identifier - but checked parents to see if they had an existing number, and prefixed the outline number with the parent one if it was there. It mostly works, mostly. I store the computed value in $OutlinePrefix, another prototype-specific property, to save recalculating chains of values on my steam-powered 2014 MBP.

I’d then use a property (here, $ReportString) as the title for the note in outline view.

 if ( $OutlineNumberise == true  ) && ( $Prototype == “Markdown Note”) ) {
  if ( $OutlineDepth == 1 ) {
    $TopicNumber = $SiblingOrder + '.';
  } else {
    $TopicNumber = $TopicNumber(parent) + $SiblingOrder + '.';
  }
  $OutlinePrefix = ' ' + $TopicNumber; } else {
  $TopicNumber = '';
  $OutlinePrefix = '';
}

$ReportString = $OutlinePrefix + ' ' + $Name;

This would give me things like

  1. Headline
    1.1 Subhead
    1.2 Next subhead
  2. Next Headline

I’m acutely aware that by posting this here, all the places it will fail will be exposed, and for that, I give my thanks in advance.

It’s very much a for me solution, in that I didn’t need the range of options in the original question, but did need to deal with a hierarchy. As such, I have the following as the Action for this prototype:

$Prototype='Markdown Note';
 $OutlineNumberise = parent($OutlineNumberise);

which should set any created child notes to behave in a suitable manner (assuming this is all using a prototype hardcoded as ‘Markdown Note’; I’m sure there’s a cleaner way to handle this, but I’ve had no need to expend the cognitive energy on it).

I remember a few of you here helping me with this code; I will spare us all the blushes.

Hi Dave, great example. I, too, have a not architecture/outlining approach similar to yours. The context and use case for this approach is slightly different, as you can see. This is one of the things that makes Tinderbox so powerful. :slight_smile: