Repository: pola-rs/polars
File: py-polars/src/polars/expr/string.py (Python)

Code before:
        ...     {
        ...         "lyrics": [
        ...             "Everybody wants to rule the world",
        ...             "Tell me what you want, what you really really want",
        ...             "Can you feel the love tonight",
        ...         ]
        ...     }
        ... )
        >>> mapping = {"me": "you", "you": "me", "want": "need"}
        >>> df.with_columns(
        ...     pl.col("lyrics").str.replace_many(mapping).alias("confusing")
        ... )
        shape: (3, 2)
        ┌────────────────────────────────────────────────────┬───────────────────────────────────────────────────┐
        │ lyrics                                             ┆ confusing                                         │
        │ ---                                                ┆ ---                                               │
        │ str                                                ┆ str                                               │
        ╞════════════════════════════════════════════════════╪═══════════════════════════════════════════════════╡
        │ Everybody wants to rule the world                  ┆ Everybody needs to rule the world                 │
        │ Tell me what you want, what you really really want ┆ Tell you what me need, what me really really need │
        │ Can you feel the love tonight                      ┆ Can me feel the love tonight                      │
        └────────────────────────────────────────────────────┴───────────────────────────────────────────────────┘

        Using `leftmost` and changing order of tokens in `patterns`, you can get fine control over replacement
        logic, following `Aho-Corasick crate documentation <https://docs.rs/aho-corasick/latest/aho_corasick/struct.AhoCorasickBuilder.html#method.match_kind>`_
        and its examples.

        Default behavior -- when going left to right, match first found pattern:

        >>> df = pl.DataFrame({"haystack": ["abcd"]})
        >>> patterns = {"b": "x", "abc": "y", "abcd": "z"}
        >>> df.with_columns(replaced=pl.col("haystack").str.replace_many(patterns))
        shape: (1, 2)
        ┌──────────┬──────────┐
        │ haystack ┆ replaced │
        │ ---      ┆ ---      │
        │ str      ┆ str      │
        ╞══════════╪══════════╡
        │ abcd     ┆ axcd     │
        └──────────┴──────────┘

        Adding `leftmost=True` matches pattern with leftmost start index first:

        >>> df = pl.DataFrame({"haystack": ["abcd"]})
        >>> patterns = {"b": "x", "abc": "y", "abcd": "z"}
        >>> df.with_columns(replaced=pl.col("haystack").str.replace_many(patterns))
        shape: (1, 2)
        ┌──────────┬──────────┐
        │ haystack ┆ replaced │
        │ ---      ┆ ---      │
        │ str      ┆ str      │

Review comment (suggestion):
I don't want to explicitly mention our internal implementation, or codify whatever behavior it has for when `leftmost` is not set. The only thing I'd like our documentation to mention is that when `leftmost` is not set there are no guarantees in which order overlapping matches are considered or replaced.

Code after:
        sugar.

        >>> _ = pl.Config.set_fmt_str_lengths(100)
        >>> _ = pl.Config.set_tbl_width_chars(110)
        >>> df = pl.DataFrame(
        ...     {
        ...         "lyrics": [
        ...             "Everybody wants to rule the world",
        ...             "Tell me what you want, what you really really want",
        ...             "Can you feel the love tonight",
        ...         ]
        ...     }
        ... )
        >>> mapping = {"me": "you", "you": "me", "want": "need"}
        >>> df.with_columns(
        ...     pl.col("lyrics").str.replace_many(mapping).alias("confusing")
        ... )
        shape: (3, 2)
        ┌────────────────────────────────────────────────────┬───────────────────────────────────────────────────┐
        │ lyrics                                             ┆ confusing                                         │
        │ ---                                                ┆ ---                                               │
        │ str                                                ┆ str                                               │
        ╞════════════════════════════════════════════════════╪═══════════════════════════════════════════════════╡
        │ Everybody wants to rule the world                  ┆ Everybody needs to rule the world                 │
        │ Tell me what you want, what you really really want ┆ Tell you what me need, what me really really need │
        │ Can you feel the love tonight                      ┆ Can me feel the love tonight                      │
        └────────────────────────────────────────────────────┴───────────────────────────────────────────────────┘

        Using `leftmost` and changing order of tokens in `patterns`, you can get fine control over replacement
        logic, while default behavior does not provide guarantees in case of overlapping patterns:

        >>> df = pl.DataFrame({"haystack": ["abcd"]})
        >>> patterns = {"b": "x", "abc": "y", "abcd": "z"}
        >>> df.with_columns(replaced=pl.col("haystack").str.replace_many(patterns))
        shape: (1, 2)
        ┌──────────┬──────────┐
        │ haystack ┆ replaced │
        │ ---      ┆ ---      │
        │ str      ┆ str      │
        ╞══════════╪══════════╡
        │ abcd     ┆ axcd     │
        └──────────┴──────────┘

        Note that here `replaced` can be any of `axcd`, `yd` or `z`.

        Adding `leftmost=True` matches pattern with leftmost start index first:

        >>> df = pl.DataFrame({"haystack": ["abcd"]})
        >>> patterns = {"b": "x", "abc": "y", "abcd": "z"}
        >>> df.with_columns(
        ...     replaced=pl.col("haystack").str.replace_many(patterns, leftmost=True)