Remove final characters from string recursively - What's the best way to do this?
<p>I am reading lines from a file one at a time and before i store each, i wanna modify them according to the following simple rule:</p>

<ul>
<li>if the last character is not any of, e.g., <code>{'a', 'b', 'c'}</code> store the line.</li>
<li>if that is not the case, remove the character (pop-like) and check again.</li>
</ul>

<p>What i currently have (felt like the obvious thing to do) is this:</p>

<pre><code>bad_chars = {'a', 'b', 'c'}


def remove_end_del(line_string, chars_to_remove):
    while any(line_string[-1] == x for x in chars_to_remove):
        line_string = line_string[:-1]
    return line_string

example_line = 'jkhasdkjashdasjkd|abbbabbababcbccc'
modified_line = remove_end_del(example_line, bad_chars)
print(modified_line)  # prints -&gt; jkhasdkjashdasjkd|
</code></pre>

<p>Which of course works, but the string slicing\reconstruction seems a bit too excessive to my untrained eyes. So i was wondering a couple of things:</p>

<ol>
<li>is there a better way to do this? like a <code>pop</code> type of function for strings?</li>
<li>how is <code>rstrip()</code> or <code>strip()</code> in general implemented? is it also with a <strong>while</strong>?</li>
<li>would it be worthwhile making <code>rstrip()</code> recursive for this example?</li>
<li>Finally, how much better is the following:</li>
</ol>

<hr>

<pre><code>def remove_end_del_2(line_string, chars_to_remove):
    i = 1
    while line_string[-i] in chars_to_remove:
        i += 1
    return line_string[:-i+1]
</code></pre>

<p>Any comment on any of the points made above would be appreciated âº.</p>

<p><strong>Note: the separator ("|") is only there for visualization.</strong></p>

<p>Another nearly fast approach to <code>re.sub</code> albeit more intuitive (<em>it sounds like the <code>pop</code> you're asking for</em>) is <a href="https://docs.python.org/2/library/itertools.html#itertools.dropwhile" rel="nofollow"><code>itertools.dropwhile</code></a>:</p>

<blockquote>
  <p>Make an iterator that drops elements from the iterable as long as the
  predicate is true;</p>
</blockquote>

<pre><code>&gt;&gt;&gt; ''.join(dropwhile(lambda x: x in bad_chars, example_line[::-1]))[::-1]
'jkhasdkjashdasjkd|'
</code></pre>

<p>However, it appears <code>rstrip</code> was made and more suited for a task as this.</p>

<hr>

<p>Some timings:</p>

<pre><code>In [4]: example_line = 'jkhasdkjashdasjkd|abbbabbababcbccc'

In [5]: bad_chars = {'a', 'b', 'c'}
</code></pre>

<hr>

<pre><code>In [6]: %%timeit
   ...: re.sub(r'[%s]+$' % ''.join(bad_chars), '', example_line)
   ...:
100000 loops, best of 3: 5.24 Âµs per loop
</code></pre>

<hr>

<pre><code>In [7]: %%timeit
   ...: ''.join(dropwhile(lambda x: x in bad_chars, example_line[::-1]))[::-1]
   ...:
100000 loops, best of 3: 5.72 Âµs per loop
</code></pre>

<hr>

<pre><code>In [10]: %%timeit
   ....: remove_end_del(example_line, bad_chars)
   ....:
10000 loops, best of 3: 24.1 Âµs per loop
</code></pre>

<hr>

<pre><code>In [11]: %%timeit
   ....: example_line.rstrip('abc')
   ....:
1000000 loops, best of 3: 579 ns per loop
</code></pre>

<hr>

<pre><code>In [14]: %%timeit
   ....: remove_end_del_2(example_line, bad_chars)
   ....:
100000 loops, best of 3: 4.22 Âµs per loop
</code></pre>

<p><strong><code>rstrip</code> wins!</strong></p>

python