g | x | w | all
Bytes Lang Time Link
042C clang240815T132524Zjdt
026Ruby240819T094433ZG B
021APLDyalog Unicode240815T145322Zakamayu
014Haskell + hgl240814T143415ZWheat Wi
007Vyxal240814T221128Zemanresu
007Pyth240815T205833ZCursorCo
019Uiua240815T205321ZjanMakos
046Bash240815T172957ZGammaFun
008Jelly240814T175309ZJonathan
045Python240814T160801ZAlbert.L
010Retina 0.8.2240814T155417ZNeil
075brainfuck240814T233338ZNitrodon
00605AB1E240814T224409ZKevin Cr
013Charcoal240814T212135ZNeil
061Python240814T183637Zshape wa
017sed 4.2.2240814T182151ZDigital
019Perl 5 p240814T163746ZXcali
044JavaScript ES6240814T150830ZArnauld
011Japt v2.0a0240814T141527Znoodle p

C (clang), 42 bytes

f(*s){*s&&wcscpy(s,wcsrchr(s,*s))+f(++s);}

Try it online!

C (clang), 40 bytes

f(*s){*wcscpy(s,wcsrchr(s,*s))&&f(++s);}

Try it online!

This one needs the following header:

#include <wchar.h>

Ruby, 26 bytes

->s{s.gsub /(.).*\1/,'\1'}

Try it online!

Non-recursive, non-iterative solution, which seems to cover all possible cases, I can't think of any counterexample.

Basically, if you have something like: a......a..a....a.a....aaa, it's going to become a, no matter what's in between, and a greedy search seems to be the best way.

APL(Dyalog Unicode), 21 bytes SBCS

Direct implementation of @Jonathan Allan's idea.

⊃¨≢⊢∘(⊢↓⍨0⊥⊢⍸⍤=⊃)⍀⍤⍴⊂

Try it on APLgolf!

⊃¨≢⊢∘(⊢↓⍨0⊥⊢⍸⍤=⊃)⍀⍤⍴⊂
  ≢⊢∘(          )⍀⍤⍴⊂     ⍝ Apply repeatedly and collect intermediate results
⊃¨                        ⍝ The first items of each intermediate result
     (⊢↓⍨0⊥⊢⍸⍤=⊃)         ⍝ Drop the prefix that ends at the last occurrence of the first item
      ⊢↓⍨                 ⍝ Drop the prefix
         0⊥               ⍝  that ends at the last
           ⊢⍸⍤=⊃          ⍝    occurrences of the first item

K (ngn/k), 19 bytes

*'-1_({||\|x=*x}_)\

Try it online!

Haskell + hgl, 31 30 29 28 14 bytes

ysk$yS$hdS<*h'

Attempt This Online!

Explanation

We are using a parser to do this ysk will repeatedly apply a parser until reaching a fixed point, it prioritizes parses towards the front of the input by default so we just need to write a parser which closes a loop.

As I point out in my comment it doesn't matter if you prioritize loops by their start or end location.

Then the loop itself is yS$hdS<*h', i.e. parse a character, parse some number of characters, parse the same character again returning only the last parse.

Reflection

This answer is pretty tight, but there was definitely some room for improvement on hgl that we see could see in earlier versions of this answer.

Of course none of these suggestions would actually end up making a shorter answer.

Vyxal, 7 bytes

hṡt)↔∩h

Try it Online!

Port of Jonathan Allan's Jelly answer, go upvote that!

Note: Due to a Vyxal bug, this uses (regex split) instead of / (regular split), so this can't handle any regex special characters. Everything else should work fine though.

    ↔   # Collect while the result is unique
---)    # Last four elements as a lambda
 ṡ      # Split
h       # On first character
  t     # And get the last item
     ∩h # Take the head of each collected string

Pyth, 7 bytes

#=ecQph

Try it online!

Uses the same principle as Jonathan Allan's Jelly answer, be sure to upvote that as well.

Explanation

#=ecQphQ    # implicitly add Q
            # implicitly assign Q = eval(input())
#           # loop until error
      hQ    # take the first element of Q (will error when Q is the empty string)
     p      # print with no trailing newline
   cQ       # split Q on instances of this character
  e         # take the last element of the split
 =  Q       # assign to Q

Uiua, 19 bytes

⍥⍣(▽≠1\+⊸=⊢⊸▽¬⊸◰)∘∞

Try it online!

Explanation

⍥⍣(▽≠1\+⊸=⊢⊸▽¬⊸◰)∘∞

⍥⍣(             )∘∞ # repeat until error (fixed point)
             ¬⊸◰    # mask of all duplicates
          ⊢⊸▽       # get the first
        ⊸=          # mask where the string equals the duplicate
      \+            # accumulative addition
                    # - positions equal to 1 are between the first 2 chars
   ▽≠1              # remove the part in between

Bash, 46 bytes

c=${1:0:1} s=${1:1}
echo $c${s:+`$0 ${s##*$c}`}

Try it online!

Print the first character, recurse with ${s##*$c}, the string removing the longest prefix ending with that first character. Recursion is guarded by ${s:+ } checking that the rest of the string is non-empty.

Jelly,  9  8 bytes

ṣḢȮ$ṪµL¿

A full program that accepts a string and prints the erased string.

Try it online! Or see the test-suite.

How?

I think this is right, if not do let me know of a counterexample.

Rather than iteratively collapsing the string between the "first pair" to become that character (as defined), we can instead iteratively remove everything between the first character and its last occurrence, inclusive (even if it only appears once) and record the first character of the input at each step.

I think this works because one of these things is true at each step:

  1. The first character doesn't reappear
  2. The "first pair" is the same as this choice
  3. This choice wraps the "first pair", so the collapsed string would be removed at a later step anyway
ṣḢȮ$ṪµL¿ - Link: list, A
       ¿ - while...
      L  - ...condition: length (is non-zero)
     µ   - ...action: monadic chain - f(Current (initially A)):
   $     -   last two links as a monad - f(Current):
 Ḣ       -     head {Current} -> first character
  Ȯ      -     print {that} (without trailing newline), and yield {that}
ṣ        -   split {Beheaded Current} at {that}
    Ṫ    -   tail

Python, 45 bytes

lambda x:re.sub(r"(.).*\1",r"\1",x)
import re

Attempt This Online!

I no longer think the non-greediness of my first attempt below is necessary nor that overlap may be a problem. This echos @Jonathan Allan's observation and probably others, too.

Python, 47 bytes

lambda x:re.sub(r"(.).*?(?=\1)","",x)
import re

Attempt This Online!

How?

Similar to other regex based answers. Things that may or may not be Python specific: re.sub only finds non-overlapping matches. Therefore we use a look ahead assertion for the second copy of the repeat element so it remains available for further matches. *? is a non greedy version of *. Here it makes sure the match stretches only to the first reoccurrence of the repeat element.

Retina 0.8.2, 12 10 bytes

(.).*\1
$1

Try it online! Link includes test cases. Explanation: Port of @noodleman's answer, but assumes @JonathanAllan's observation to save 2 bytes thanks to @att.

17 bytes to follow the challenge description exactly:

+r`\2.*((.).*)
$1

Try it online! Link includes test cases. Explanation: The r indicates right-to-left matching, so as many characters from the right as possible are matched that still allow a pair of identical characters to be matched.

brainfuck, 75 bytes

,[>[[-<<+<+>>>]<[-<->>+<]<[[-]>]<[>>>[>]<[[-]<]<]>>>]<[-<<+>>]<<<[<],]>[.>]

Try it online!

05AB1E, 6 bytes

ΔćD?¡θ

Port of @JonathanAllan's Jelly answer, so make sure to upvote that answer as well!

Try it online or verify all test cases.

Explanation:

Δ       # Loop until the result no longer changes (aka, until it's an empty string ""):
 ć      #  Extract head; push remainder-string and first character separately
        #  (which will use the implicit input-string in the first iteration)
  D?    #  Duplicate it; and pop and output this first character (without newline)
    ¡   #  Split the remainder-string on this character
     θ  #  Pop and leave just the last part for the next iteration

Charcoal, 13 bytes

WΦθ¬λ«ι≔⊟⪪θιθ

Try it online! Link is to verbose version of code. Explanation: Effectively a port of @JonathanAllan's Jelly answer.

WΦθ¬λ«

Repeat while the string has a first character.

ι

Output that character.

≔⊟⪪θιθ

Truncate the string after the last occurrence of that character by splitting and taking the last piece.

Python, 61 bytes

f=lambda s,i=0:i<len(s)and f(s[:i]+s[s.rfind(s[i]):],i+1)or s

Attempt This Online!

Goes one by one through characters of the string, collapsing the range between the current character and its final occurrence. Using abcbabadeefcbfggg as an example:

abcbabadeefcbfggg -> adeefcbfggg -> adeefcbfggg -> adefcbfggg -> adefggg -> adefg
^                     ^               ^               ^              ^
      ^               ^                ^                 ^             ^

Makes use of the same observations that Wheat Wizard and Jonathan Allan have made:

cAdBeBfAg -> cAg <=> cAdBeBfAg -> cAdBfAg -> cAg
 ^     ^                ^ ^        ^   ^
bAcAdAe -> bAe <=> bAcAdAe -> bAdAe -> bAe
 ^   ^              ^ ^        ^ ^

Ungolfed:

def f(s):
    i = 0
    while i < len(s):
        j = s.rfind(s[i])
        # remove s[i:j]
        # s:              adefcbfggg -> adefggg
        # s[i] and s[j]:     ^  ^
        # s[i:j]:            ^^^
        s = s[:i] + s[j:]
        i += 1
    return s

sed 4.2.2, 17

:
s/(.).*\1/\1/
t

Try it online!

Perl 5 -p, 19 bytes

s/(.).*\1/$1/&&redo

Try it online!

JavaScript (ES6), 44 bytes

f=s=>s==(s=s.replace(/(.).*\1/,"$1"))?s:f(s)

Try it online!

Japt v2.0a0, 11 bytes

e/(.).*\1/Ï

Try it

Explanation: Recursively replace matches of the RegEx /(.).*\1/g--which matches a character, many different characters, and that same first character--with the first character.