> For the complete documentation index, see [llms.txt](https://docs.membercounter.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.membercounter.app/counters-reference/replace.md).

# Replace

Text replacement, useful in conjunction with other nested counters for comprehensive text modification.

The Replace counter takes a piece of text and swaps parts of it for something else. It is built for cleaning up or rewording what another counter returns, so all of its fields take [nested counters](/using-the-dashboard/nested-counters.md).

**Availability:** every bot type. It makes no network requests, so it needs no API key and no environment variable when you [self-host](/self-hosting/configuration.md).

<figure><img src="https://1582170640-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lu2S5r7lKtu4IK5ucu9%2Fuploads%2Fgit-blob-3245768a2bf05d891f7e0b54ca612572a73d7a2f%2Fcounter-replace-options.png?alt=media" alt="Replace options"><figcaption><p>The Replace counter panel: <strong>Input text</strong> and one replacement card</p></figcaption></figure>

## Options

| Option           | What it does                              | Notes                                                            |
| ---------------- | ----------------------------------------- | ---------------------------------------------------------------- |
| **Input text**   | The text the replacements are applied to. | One value only. Takes a nested counter.                          |
| **Search for**   | The piece of text to look for.            | Plain text only, no regular expressions. Takes a nested counter. |
| **Replace with** | What each match becomes.                  | Takes a nested counter.                                          |

**Search for** and **Replace with** live together on a replacement card. Use **Add replacement** to add another card and **Remove replacement** to delete one. There is no limit on the number of cards.

{% hint style="warning" %}
**`$` has a special meaning in Replace with.** `$$` produces a single `$`, `$&` inserts the text that was matched, and `` $` `` and `$'` insert the text before or after the match. To print a literal `$$`, type `$$$$`.
{% endhint %}

## What it returns

The **Input text** with every replacement card applied, as text. With no **Input text** the result is empty text.

* **The cards run in order**, each on the result of the one before it, so card 2 can match text card 1 has just produced.
* **Matching is literal and case-sensitive**, and every occurrence is replaced, not just the first. Searching for `on` in `Online` matches nothing, because the text starts with a capital `O`.
* **Regular expressions are not supported.**

{% hint style="danger" %}
**You cannot delete text with this counter.** A card whose **Replace with** is empty is skipped instead of removing the search term, and so is a card whose **Search for** is empty. A single space is trimmed and skipped too, so the closest you can get is replacing the term with another visible character.

A nested counter that returns the number `0` also counts as empty and its card is skipped. A `0` you type in yourself is fine, because it is stored as text.
{% endhint %}

## Formatting

A Replace result is text, and the bot only formats numbers, so **Use compact notation**, **Override default locale** and **Override default digits** have no effect on it, nor on any number produced inside it.

When the cleaned-up value is a number and you want it formatted, wrap the Replace counter in a [Number](/counters-reference/number.md) counter and put the format settings there, because the bot reads the formatting from the outermost counter only. See [Formatting counters](/using-the-dashboard/customizing-counters.md).

## Refresh and caching

Replace caches nothing. The bot works it out again on every channel update, every 10 minutes on the public bot and every 5 minutes on the other bot types. Anything nested inside it keeps its own refresh behaviour, so an [HTTP](/counters-reference/http.md) counter in **Input text** still refetches on its own **Lifetime (in seconds)**. See [How counters update](/using-the-dashboard/how-counters-update.md).

## Examples

{% tabs %}
{% tab title="Cleaning a value before Number" %}
An API returns a figure with a decimal comma, such as `1234,56`. [Number](/counters-reference/number.md) cannot read that, so fix the separator first.

1. Add an [HTTP](/counters-reference/http.md) counter pointed at the endpoint and set its **Data path** so it returns just the figure.
2. Add a **Replace** counter and nest the HTTP counter in **Input text**.
3. Press **Add replacement** once and fill the card: **Search for** `,` and **Replace with** `.`, which turns `1234,56` into `1234.56`.
4. Wrap the whole Replace counter in a [Number](/counters-reference/number.md) counter so the value becomes a real number and **Format settings** apply to it.

{% hint style="warning" %}
This only works when the value has no thousands separator. A card with an empty **Replace with** is skipped, so a separator cannot be removed, and swapping it for anything else still leaves a character [Number](/counters-reference/number.md) cannot read. A value like `1.234,56` cannot be cleaned up with this counter.
{% endhint %}

<figure><img src="https://1582170640-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lu2S5r7lKtu4IK5ucu9%2Fuploads%2Fgit-blob-061c739ce520dec1c5b2111ba12ae3b11e979308%2Fcounter-replace-example-clean-number.png?alt=media" alt="Cleaning an API value with Replace before Number"><figcaption><p>The stack in the dashboard: an HTTP counter in <strong>Input text</strong>, one replacement card, and the whole Replace counter inside a Number counter</p></figcaption></figure>
{% endtab %}

{% tab title="Rewording another counter" %}
[Clock](/counters-reference/clock.md), [Date](/counters-reference/date.md) and [Reddit](/counters-reference/reddit.md) all return text you may not want word for word.

* **Input text**: a nested **Clock** counter.
* **Search for**: `AM`, **Replace with**: `in the morning`.

The match is case-sensitive, so `AM` and `am` are two different searches and need two cards.
{% endtab %}

{% tab title="Swapping a value for a label" %}
Turn a bare number into something friendlier.

* **Input text**: a nested [Members](/counters-reference/members.md) counter with **Filter by status** set to **Online + Idle + DND**.
* **Search for**: `1`, **Replace with**: `one`.

Every occurrence is replaced, so `21` becomes `2one`.
{% endtab %}
{% endtabs %}

## Errors

Replace has no errors of its own. What can still go wrong:

| What you see                                                        | What to do                                                                                                                                                                                       |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| The channel shows `ERROR!`                                          | A counter nested in **Input text**, **Search for** or **Replace with** failed, so the whole template failed. Open the channel in the dashboard and read the error to find the counter at fault.  |
| **The result for the channel name is less than 2 characters long.** | A voice or category channel name needs at least 2 characters, and an empty **Input text** is the usual reason. Fill **Input text**, or add text around the counter in the template.              |
| Nothing changes in the channel                                      | A card is skipped because **Search for** or **Replace with** is empty, or the search term does not match because of capitalisation. Fill both fields on every card and check the capitalisation. |

If it still fails, [Troubleshooting](/using-the-dashboard/troubleshooting.md) covers the common causes, and you can ask on the [support server](https://membercounter.app/support).

<details>

<summary>Technical details</summary>

* Evaluator: `packages/services/src/DataSource/DataSourceEvaluator/evaluators/replace.ts`. It starts from `String(options.text ?? "")` and loops over `options.replacements`, calling `newText.replaceAll(search, replacement)` on the running value.
* Each pass through the loop starts with `if (!search || !replacement) continue`, a falsy check on the raw value: an empty string and the number `0` both skip the card, and the counter itself never throws.
* `String.prototype.replaceAll` with a string pattern is what gives literal, case-sensitive matching of every occurrence, and it is also what expands the `$` patterns in the replacement.
* There is no cap on the number of replacement cards beyond the 50,000 tree-exploration iterations a top-level counter and everything nested in it share.
* A string result is returned untouched by the formatter, which formats only numbers. That is the mechanism behind the formatting caveat above.
* Other global limits: 60 s per top-level counter in the template, shared with everything nested inside it (`EVALUATION_TIMEOUT`), and a final result that is trimmed and then cut to 1023 characters for a topic or 99 for a channel name, with the 2-character minimum (`EVALUATION_RESULT_FOR_CHANNEL_NAME_IS_LESS_THAN_2_CHARACTERS`) checked before the cut.
* When a nested counter makes the template fail, the update job writes `jobs.updateChannels.computeError` into the name or topic: `ERROR!` in en-US, translated in the other locales.
* No network access, no cache entry and no environment variable when self-hosting.

</details>
