01469: Extend ConditionalMarkup with "less" and "greater" operators
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
- ConditionalExtensions A Conditional Markup extension for PmWiki 2.x
- ConditionalMarkupSamples List of default and custom conditional markup definitions
- 01209 Add more markup expressions to the core
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,
$StrFoldFunctionis 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'.
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.