01469: Extend ConditionalMarkup with "less" and "greater" operators

Summary: Extend ConditionalMarkup with "less" and "greater" operators
Created: 2021-09-08 02:36
Status: Open
Category: Feature
From: simon
Assigned:
Priority: 4
Version: current
OS: all

Description: ConditionalMarkup provides an "equal" operator.

This is a feature request to add a "greater" and a "less" operator to the core (or ">" and "<"). Like "equal" these would work on numbers and strings.

For completeness "equalgreater" and "equalless" could also be added (or ">=" and "<=").

While recipes do provide so DIY options these seem like basic functionality and would provide consistent markup common to all PmWikis in the core.

See also

simon September 08, 2021, at 02:39 AM

Here is a possible implementation, possibly Core candidate:

$Conditions['cmp'] = 'CondCmp($condparm)';
function CondCmp($condparm) {
  global $CondCmpFoldFunction, $StrFoldFunction;
  $fn = IsEnabled($CondCmpFoldFunction, $StrFoldFunction);
  $args = ParseArgs($fn($condparm), '()');
  $params = $args[''];
  if(count($params)<3) return false;

  while(count($params)>=3) {
    $r = strnatcmp($params[0], $params[2]);
    $op = htmlspecialchars_decode($params[1]);
    # Not an operator
    if(! preg_match('/^([=<>]=?|[<][>])$/', $op)) return false;

    if($r == 0 &&  $op == '<>') return false;
    if($r != 0 && ($op == '=' || $op == '==')) return false;
    if($r <  0 && ($op == '>' || $op == '>=')) return false;
    if($r >  0 && ($op == '<' || $op == '<=')) return false;
    $params = array_slice($params, 2);
  }
  return true;
}

Use it like this:

(:if cmp "a" <= "b":) %green%TRUE(:else:)%red%FALSE(:if:)
----
(:if cmp "a9" < "a10":) %green%TRUE(:else:)%red%FALSE(:if:)
----
(:if cmp "a9" < "a20" <= "b1":) %green%TRUE(:else:)%red%FALSE(:if:)
 TRUE

 TRUE

 TRUE

This is enabled on this page, you can test it.

Notes:

  • It starts with (:if cmp ... :)
  • Operators can be among = or ==, <, >, <=, >=, <>. Operators cannot be =<, =>.
  • Quotes are highly recommended, especially if using page variables.
  • Multiple comparisons are possible in the same expression, like the 3rd example above, checking if one element is between 2 values. In this case, the elements are compared left to right, and all need to be true to return true.
  • The comparison uses the natural sorting algorithm so "a9" is less than "a10" with the elements folded to lowercase, so "A15" and "a15" would be equal.
  • In fact, $StrFoldFunction is used to fold the strings to lowercase, and can be overridden by defining $CondCmpFoldFunction. For example, to have case-sensitive comparison, one could define $CondCmpFoldFunction = 'IsEnabled';.

Let me know if this works for you, and in what situations. I don't think I've ever needed this so far, I wonder if it should be added to the core or can stay as a recipe/extension. --Petko

Thanks, this is a great solution. I fully support it being added to the core.
My use case is I have a number of pages with an ordering in the page names (e.g. 'Nameyyyy'). I wish to only show a pagelist (say) in pages with names (say) greater than 'Name1957'.

simon

Oh, this is a rather simple comparison, could be done more efficiently with a name pattern. --Petko

(:pagelist name="Name19[5-9][7-9],Name[2-9][0-9][0-9][0-9]":) for 1957-9999

In fact we should implement name>=Name1957. --Petko

I didn't know regex style patterns ([xyz])]) could be put in pagelist parameters.
I very much like the idea of implementing, where it makes sense, operators in addition to "=" in pagelists.
While my use case this time is for a name comparison, I can see the potential benefits of the more general use case for string comparison.

simon