Tricks for refactoring ugly long filters?

Here’s one way to do it. Whether this is less ugly or not is probably a matter of opinion, but it is at least less visually repetitive.

\function convert.num() [regexp[\D]] :else[range{!!title}search-replace::regexp[\d],[.]]

<$let str="8x7x6x5x4x3x2x1">
{{{ [<str>split[]] :map:flat[convert.num[]] +[join[]] }}}
</$let>

Steps:

  • split between each character
  • The filter with the :map prefix will be run once per input value. Since I needed a two-part filter to make use of the :else condition, I had to move both parts into the function/subfilter convert.num, which becomes the sole content of my :map run.
    • By default, :map runs yield one output per input value. The :flat suffix switches it to a (potentially) one-to-many relationship, which will allow us to turn one digit into many periods.
    • I like functions, personally, but you could also do this with the subfilter operator, if you prefer. You’d need to make the following changes:
      • \function convert.num()\define convert.num()
      • :map:flat[convert.num[]]:map:flat[subfilter<convert.num>]
  • Now, looking at convert.num itself…
    • [regexp[\D]] = search for a non-digit. If this filter run returns any (non-digit) value, the rest of the function will be ignored, and the character is preserved as-is.
    • :else
      • use range to generate a list of numbers beginning with 1 and ending with {!!title} (= the number). For instance, range[8] will give me 1 2 3 4 5 6 7 8.
      • Use search-replace::regexp[\d],[.] to replace each number in the range output with a period.
  • Finally, +[join[]] everything back into a single string.

And my generalized advice for long, ugly filters: if you find yourself reusing the same string of operators with different parameters, move it into a function instead!

Honestly, I might not have ordinarily bothered in a case like this, since your original is far more transparent at a glance and only ~30% longer. But this was a fun challenge, thanks!