37
\$\begingroup\$

Objective

Create a function to reverse string concatenation

Input

Two strings (alphanumeric + spaces), where one should be subtracted for the other.

  • You can assume that the string to be subtracted will never be larger than the other one.

Output

The result from the subtraction

Subtraction

You should remove one string from the start or the end of another string. If the string is present in the start and in the end, you can only remove one, which one will be removed is up to you.
If the string isn't in the start or in the end, or isn't an exact match, it is an invalid subtraction and you should output the original string.

Test Cases

Valid Subtraction

'abcde','ab' -> 'cde'
'abcde','cde' -> 'ab'
'abab','ab' -> 'ab'
'abcab','ab' -> 'abc' or 'cab'
'ababcde','ab' -> 'abcde'
'acdbcd','cd' -> 'acdb'
'abcde','abcde' -> ''
'abcde','' -> 'abcde'
'','' -> ''

Invalid Subtraction (returns original string)

'abcde','ae' -> 'abcde'
'abcde','aa' -> 'abcde'
'abcde','bcd' -> 'abcde'
'abcde','xab' -> 'abcde'
'abcde','yde' -> 'abcde'

Invalid Input (don't need to be handled)

'','a' -> ''

This is , so the shortest code in bytes wins!

\$\endgroup\$
  • 4
    \$\begingroup\$ Why is the result of the first case not cde? What do you mean by valid? Do we need to judge the validity of the input, or do you mean that we will not receive invalid inputs? \$\endgroup\$ – Leaky Nun May 16 '17 at 16:01
  • 7
    \$\begingroup\$ Damn you, 'abcde','bcd' -> 'abcde', for breaking my solution \$\endgroup\$ – John Dvorak May 16 '17 at 16:26
  • 5
    \$\begingroup\$ Can we assume the strings will be regex-safe (alphanumeric + spaces)? \$\endgroup\$ – John Dvorak May 16 '17 at 16:27
  • 2
    \$\begingroup\$ I'd suggest 'ababcde', 'ab''abcde' as a test case. Some naive algorithms fail on that one. \$\endgroup\$ – user62131 May 16 '17 at 16:48
  • 2
    \$\begingroup\$ @Rod You might consider retitling the challenge "Reverse string concatenation"? \$\endgroup\$ – MD XF May 16 '17 at 19:52

32 Answers 32

19
\$\begingroup\$

Java 8, 46 45 44 40 bytes

-1 byte thanks to TheLethalCoder

-1 byte because I'm dumb (thanks Rod!)

-4 bytes thanks to Kevin Cruijssen

a->b->a.replaceFirst("^"+b+"|"+b+"$","")

Try it online! (includes all test cases)

A Java answer actually beats a few other practical languages. Smiles. (and now it beats JS!)

\$\endgroup\$
  • \$\begingroup\$ Use currying to save a byte a->b-> \$\endgroup\$ – TheLethalCoder May 16 '17 at 17:08
  • \$\begingroup\$ @TheLethalCoder Thanks. \$\endgroup\$ – Okx May 16 '17 at 17:28
  • \$\begingroup\$ Why did you leave in the unused hashmap in your online example? \$\endgroup\$ – Michael May 17 '17 at 12:52
  • \$\begingroup\$ You can change the First to All for -2 bytes. Because of the ^ and $ it's always either at the end or start of the String, so even with replaceAll it only replaces it once. Try it here. PS: I added the previous byte-counts striked-through to your answer, which is what usually is done after code-golf edits here on PPCG. \$\endgroup\$ – Kevin Cruijssen May 18 '17 at 9:00
  • \$\begingroup\$ @KevinCruijssen I knew about the strike-throughs, guess I just forgot this time. However, if i use All instead of First, this becomes true: "abab" + "ab" -> "" \$\endgroup\$ – Okx May 18 '17 at 9:43
9
\$\begingroup\$

JavaScript (ES6), 41 bytes

s=>t=>s.replace(eval(`/^${t}|${t}$/`),'')

Takes input via currying syntax, i.e. f("abab")("ab").

\$\endgroup\$
  • 3
    \$\begingroup\$ Now, why have I never thought to use eval() for constructing RegExes before?! \$\endgroup\$ – Shaggy May 16 '17 at 16:35
9
\$\begingroup\$

Brachylog (Try It Online!), 12 bytes

~cpĊh.∧Ċtw|w

Try it online!

Takes the string to subtract from from standard input, and the string to subtract as a command line argument.

Explanation

~cpĊh.∧Ċtw|w
~c            Split {the input} into pieces
  p           and (possibly) rearrange those pieces
   Ċ          such that there are two pieces
    h         and the first
     .        matches the command line argument
      ∧       then
         w    print
        t     the last
       Ċ      piece.
          |   If all else fails,
           w  print {the input}.
\$\endgroup\$
6
\$\begingroup\$

Retina, 21 bytes

1 byte thanks to Martin Ender.

(.*);(\1|(.*)\1$|)
$3

Try it online!

\$\endgroup\$
6
\$\begingroup\$

JavaScript (ES6), 76 70 45 41 bytes

s=>t=>s.replace(RegExp(`^${t}|${t}$`),"")

Try It

f=
s=>t=>s.replace(RegExp(`^${t}|${t}$`),"")
o.innerText=f(i.value="abcde")(j.value="ab")
i.oninput=j.oninput=_=>o.innerText=f(i.value)(j.value)
<input id=i><input id=j><pre id=o>

\$\endgroup\$
  • 2
    \$\begingroup\$ You don't need new . \$\endgroup\$ – programmer5000 May 16 '17 at 18:11
  • \$\begingroup\$ @programmer500, I kinda gave up working on this when I saw ETH's version! :D Updated now. Thanks. \$\endgroup\$ – Shaggy May 16 '17 at 18:17
4
\$\begingroup\$

Perl 6, 21 bytes

->$_,$b {S/^$b|$b$//}

Try it

Expanded:

-> $_, $b {   # pointy block lambda

  S/          # Str replace and return (implicitly against 「$_」)

  |   ^ $b    # starting with the second argument
  |     $b $  # or ending with the second argument

  //          # replace with nothing.

}
\$\endgroup\$
4
\$\begingroup\$

Japt, 11 9 bytes

k"^|$"¬qV

Try it online!

\$\endgroup\$
3
\$\begingroup\$

TI-Basic (TI-84 Plus CE), 63 bytes

Prompt Str0,Str2
inString(Str0,Str2
If Ans
sub(Str0,1,Ans-1)+sub(Str0,Ans+length(Str2),length(Str0)-Ans+1-length(Str2→Str0
Str0
\$\endgroup\$
  • \$\begingroup\$ I have a question, why didn't you use Str1 as a variable? \$\endgroup\$ – Zacharý Aug 9 '17 at 20:57
  • \$\begingroup\$ @Zacharý I think I had something stored in it at the time. I don't really remember. \$\endgroup\$ – pizzapants184 Aug 9 '17 at 21:02
  • \$\begingroup\$ What does Ans even refer to on the fourth line? \$\endgroup\$ – Zacharý Aug 9 '17 at 21:30
  • \$\begingroup\$ @Zacharý Ans refers to the last evaluated value, so in this case it refers to the value returned by inString(, which is the index of the substring Str2 in the string Str0 or 0 if the substring does not appear. An if statement does not modify the value of Ans, so on the fourth line the index is still in Ans. \$\endgroup\$ – pizzapants184 Aug 9 '17 at 21:34
  • \$\begingroup\$ Oh, I forgot how inString worked. Nice golf! \$\endgroup\$ – Zacharý Aug 9 '17 at 21:42
3
\$\begingroup\$

Mathematica, 162 bytes

(c=Characters;a=c@#;b=c@#2;l=Length;t={};If[l@Intersection[a,b]==l@b,If[MemberQ[Partition[a,l@b,1],b],t=a;Table[t=DeleteCases[t,b[[i]],1,1],{i,l@b}]],t=a];""<>t)&

test input style ["abcde","ab"]

\$\endgroup\$
  • 1
    \$\begingroup\$ Nice solution! You can save a byte by using # instead of #1 — they mean exactly the same. Also, instead of using StringJoin@t, you can cheat by joining an empty string to it with ""<>t, which automatically joins everything in t together too. Have you seen the Mathematica golfing tips page? \$\endgroup\$ – Not a tree May 17 '17 at 0:50
  • \$\begingroup\$ There are a few more things you can do to save bytes (I don't think you don't need to define t={}; at the start, for instance), but it might be easier to use a different approach entirely — have you tried using the StringReplace function? \$\endgroup\$ – Not a tree May 17 '17 at 1:13
  • \$\begingroup\$ You are allowed to take a String array as input, so you don't really need c=Characters;a=c@#;b=c@#2; \$\endgroup\$ – JungHwan Min May 17 '17 at 5:55
  • \$\begingroup\$ Also, l@Intersection[a,b] is l[a∩b]. \$\endgroup\$ – JungHwan Min May 17 '17 at 5:56
3
\$\begingroup\$

Python, 69 68 64 57 51 45 bytes

This ended up being a completely different solution with Regex.

Thanks to Value Ink for -2 bytes!
and Felipe Nardi Batista for the massive -6 bytes!

import re
lambda s,c:re.sub(c+'$|^'+c,'',s,1)

Try it online!

\$\endgroup\$
  • \$\begingroup\$ For -2 bytes: re.sub(c.join("^|$"),'',s,1) \$\endgroup\$ – Value Ink May 17 '17 at 0:33
  • 1
    \$\begingroup\$ For -6 bytes:c+'$|^'+c \$\endgroup\$ – Felipe Nardi Batista May 18 '17 at 0:02
3
\$\begingroup\$

Bash, 66 61 49 bytes

case $1 in *$2)echo ${1%$2};;*)echo ${1#$2};;esac

Try it online!

less golfed:

a=$1;
case $1 in 
    *$2)  c=${a%$2};;       
    $2*)  c=${a#$2};;
      *)  c=$1;;
esac;
echo $c

Uses case to test begining or end, and array prefix/suffix (% / #) substraction

\$\endgroup\$
  • 1
    \$\begingroup\$ Nice use of case, but longer than necessary. The 2nd and 3rd pattern could be merged into a single one: *)c=${1#$2};;. Then with only 2 branches would be shorter to echo each directly instead of using variable $c: case $1 in *$2)echo ${1%$2};;*)echo ${1#$2};;esac. Or you could keep using it, but without case: c=${1%$2};[[ $c = $1 ]]&&c=${1#$2};echo $c. \$\endgroup\$ – manatwork May 18 '17 at 7:47
3
\$\begingroup\$

APL (Dyalog), 31 30 bytes

-1 thanks to Zacharý.

This actually uses reverse (i.e. the inverse of) concatenation! Takes original string as left argument, and what to subtract as right argument.

{0::⍺{0::⍺⋄,∘⍵⍣¯1⊢⍺}⍵⋄⍵,⍣¯1⊢⍺}

Try it online!

Ungolfed:

{
    0::⍺{         ⍝ if an error happens, apply the following function on the arguments
        0::⍺          ⍝ if an error happens, return the left argument unmodified
        ,∘⍵⍣¯1⊢⍺      ⍝ inverse-append right argument on left argument
        }⍵
    ⍵,⍣¯1⊢⍺      ⍝ inverse-prepend the right argument on the left argument
}

Legend:

{} anonymous function

 left argument of the current function

 right argument of the current function

0::… if any error happens, execute this, else…

⍣¯1⊢ inverse

,∘⍵ concatenate on the right

⍵, concatenate on the left

\$\endgroup\$
  • \$\begingroup\$ I think you can save a byte with {0::⍺{0::⍺⋄,∘⍵⍣¯1⊢⍺}⍵⋄⍵,⍣¯1⊢⍺}. \$\endgroup\$ – Zacharý Aug 9 '17 at 20:56
  • \$\begingroup\$ @Zacharý Yes, thanks. \$\endgroup\$ – Adám Aug 9 '17 at 21:22
2
\$\begingroup\$

PHP, 54 Bytes

[,$x,$y]=$argv;echo preg_replace("#^$y|$y$#","",$x,1);

Testcases

\$\endgroup\$
2
\$\begingroup\$

Python 2, 68 bytes

def f(s,c):v=len(c);print[s[v:],s[:-v],s][[s[:v],s[-v:],c].index(c)]

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Haskell, 49 bytes

f s a b|s==b=a|a/=b,h:t<-a=f(s++[h])t b|1<3=s
f""

Try it online! Usage: f"" "abcdef" "ab". Alternatively, define (-)=f""and use like "abcdef" - "ab".

This regex-free solution works by recursively splitting the string in all its pre- and postfixes and checking whether the string to be substracted matches one of them.

\$\endgroup\$
2
\$\begingroup\$

Python 2, 72 65 bytes

a,b=input()
l=len(b)
print[[a,a[:-l]][b==a[-l:]],a[l:]][b==a[:l]]

Try it online!

-7 bytes thanks to @FelipeNardiBatista

\$\endgroup\$
  • \$\begingroup\$ golfed \$\endgroup\$ – Felipe Nardi Batista May 17 '17 at 23:33
  • \$\begingroup\$ @FelipeNardiBatista thanks a lot! \$\endgroup\$ – ovs May 18 '17 at 4:26
1
\$\begingroup\$

C#, 88 bytes

s=>r=>s.StartsWith(r)?s.Substring(r.Length):s.EndsWith(r)?s.Substring(0,s.IndexOf(r)):s;

Compiles to a Func<string, Func<string, string>>.

\$\endgroup\$
1
\$\begingroup\$

Ruby (lambda expression), 29 bytes

->a,b{a.sub /^#{b}|#{b}$/,""}

Yay for regex interpolation! Requires regex-safe subtrahends, but that's okay as per the challenge.

\$\endgroup\$
1
\$\begingroup\$

Tcl, 37 bytes

proc s {a b} {regsub "^$b|$b$" $a {}}

Try it online! (now running all tests)

Tcl is straightforward. proc s {a b} defines a function named s which takes parameters a and b. regsub substitutes {}, which is an empty string, for the value of b when it's at the start or end of a. The return is implicit.

\$\endgroup\$
1
\$\begingroup\$

C, 96 bytes

It's common knowledge that string manipulation in C is cumbersome, as an extension golfing would be borderline masochistic. Sounds alright to me.

f(a,b,t,l)char**a,*b,*t;{t=*a;l=strlen(b);bcmp(t,b,l)?bcmp(t+=strlen(t)-l,b,l)||(*t=0):(*a+=l);}

One of the less readable programs I've written. Takes two inputs (despite how the function looks), a char** pointing to the string to deconcatenate and a char* which is the string to remove. The input pointer is edited in place and becomes the output (who cases about memory leaks anyway).

Example usage:

char *a = malloc(6);
strcpy(a, "abcde");
char *b = malloc(4);
strcpy(b, "abc");
f(&a,b);
printf("%s\n", a); // "de"
\$\endgroup\$
1
\$\begingroup\$

AWK, 21 32 bytes

{sub("^"$2"|"$2"$",z,$1);$0=$1}1

Try it online!

Original submission naively replaced text within first string, not just at beginning or end.

{sub($2,z,$1);$0=$1}1

Try it online !

Originally tried without the braces, but it required tricks to print out empty lines and or no-matches which ended up adding more bytes than this version.

\$\endgroup\$
1
\$\begingroup\$

R, 20 42 41 bytes

pryr::f(sub(sprintf('^%s|%s$',b,b),'',a))

-1 byte thanks to MickyT!

Returns an anonymous function (which has arguments in the order b,a). Computes the string difference a-b. sub is a simple substitution that swaps the first occurrence of the pattern with, in this case, the empty string ''. Constructs the regex with sprintf to match only at the beginning and end of string. Requires the pryr package to be installed.

On the TIO link, uses the more verbose function(a,b) definition for the function for four more bytes.

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ What about the 'abcde','bcd' -> 'abcde' case? \$\endgroup\$ – Jonathan Allan May 16 '17 at 16:18
  • \$\begingroup\$ "sub is a simple substitution that simply swaps the first occurrence of b in a": Will this swap if the second string is in the middle of the first string? \$\endgroup\$ – TheLethalCoder May 16 '17 at 16:18
  • \$\begingroup\$ I misread the question! Oops. Thanks for catching that! \$\endgroup\$ – Giuseppe May 16 '17 at 16:31
  • \$\begingroup\$ you can get 1 byte back with sprintf('^%s|%s$',b,b) \$\endgroup\$ – MickyT May 17 '17 at 22:36
  • \$\begingroup\$ @MickyT, thanks! fixed. \$\endgroup\$ – Giuseppe May 18 '17 at 19:02
1
\$\begingroup\$

Common Lisp, 121 bytes

(lambda(x y)(cond((equal(#1=subseq x 0 #3=(length y))y)(#1#x #3#))((equal(#1#x #2=(-(length x)#3#))y)(#1#x 0 #2#))(t x)))

Try it online!

The usual wordy Common Lisp!

Ungolfed version:

(defun f(x y)
  (cond ((equal (subseq x 0 (length y)) y)               ; if x starts with y
         (subseq x (length y)))                          ; return rest of x
        ((equal (subseq x (- (length x) (length y))) y)  ; if x ends with x
         (subseq x 0 (- (length x) (length y))))         ; return first part of x
        (t x)))                                          ; else return x
\$\endgroup\$
1
\$\begingroup\$

Kotlin, 91 bytes

{a,b->val v=b.length
if(a.startsWith(b))a.drop(v)else if(a.endsWith(b))a.dropLast(v)else a}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ ? {a,b->var c=a.removePrefix(b);if(a==c){c=a.removeSuffix(b)};c} \$\endgroup\$ – mazzy Oct 2 '18 at 8:33
  • \$\begingroup\$ @mazzy feel free to submit that as your own answer. \$\endgroup\$ – snail_ Oct 2 '18 at 18:01
1
\$\begingroup\$

Powershell, 34 40 bytes

+6 bytes when Invalid Subtraction test cases added

param($s,$t)$s-replace"^$t(?!.*$t$)|$t$"

Comment:

The regexp expression ^$t|$t$ does not work as expected: it replaces both matches instead one (flag g always on). So, we are forced to use the negative lookahead group.

Test script:

$f = {
    param($s,$t)$s-replace"^$t(?!.*$t$)|$t$"
}

@(
    ,('abcde','ab', 'cde')
    ,('abcde','cde', 'ab')
    ,('abab','ab', 'ab')
    ,('abcab','ab', 'abc', 'cab')
    ,('ababcde','ab', 'abcde')
    ,('acdbcd','cd', 'acdb')
    ,('abcde','abcde', '')
    ,('abcde','', 'abcde')
    ,('','', '')

    ,('abcde','ae', 'abcde')
    ,('abcde','aa', 'abcde')
    ,('abcde','bcd', 'abcde')
    ,('abcde','xab', 'abcde')
    ,('abcde','yde', 'abcde')

    ,('','a', '')
) | % {
    $s,$t,$e = $_
    $r = &$f $s $t
    "$($r-in$e): $r"
}

Output:

True: cde
True: ab
True: ab
True: abc
True: abcde
True: acdb
True:
True: abcde
True:
\$\endgroup\$
0
\$\begingroup\$

QBIC, 57 bytes

Whegh, this is a mess in QBIC/QBasic...

B=@ `+B┘x=instr(;,;)~x|?_t_sB,x-1|+_sB,x+_lC|,_lB|||\?B

B=@ `+B          Prepend a string to B$. Thisis a hack to avoid errors with 
                 removing substrings stating at index 1
┘                Line-break in QBasic output
       (;,;)     Read the string (B$) and the to-be-removed substring (C$)
x=instr          And make x to be the starting index of the first C$ in B$
~x|              IF X <> 0 (ie C$ is present in B$)
?                PRINT
 _t                trimmed version (drops the prepended space)
  _sB,x-1|+        of a substring from 1 to x (the start of C$) -1
  _sB,x+_lC|,_lB   and the rest of the string, starting after C$
                     _l takes the length of a string
  |||              End TRIM, end Substring, end Length
\?B              When missing C$, just print B$
\$\endgroup\$
0
\$\begingroup\$

Lua, 71 65 bytes

Accepting suggestions

a,b=...p='(.*)'print(a:match('^'..b..p)or a:match(p..b..'$')or a)

Try it online!

\$\endgroup\$
0
\$\begingroup\$

I initially misread the instructions. Thanks, Ørjan Johansen for pointing out my mistake!

PowerShell, 46 51 bytes

Function F($o,$a){([regex]"^$a").replace($o,'',1);}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ This fails on the 'abcde' 'bcd' case. \$\endgroup\$ – Ørjan Johansen May 17 '17 at 23:11
  • \$\begingroup\$ I'm seeing expected results from that test case -- TIO here \$\endgroup\$ – Jeff Freeman May 18 '17 at 14:25
  • \$\begingroup\$ It's a listed test case of the OP and the result should be abcde - bcd does not occur at either end of the string. \$\endgroup\$ – Ørjan Johansen May 18 '17 at 17:13
  • \$\begingroup\$ You are correct. I misread the instructions. Thanks for pointing it out! \$\endgroup\$ – Jeff Freeman May 18 '17 at 17:49
0
\$\begingroup\$

Excel, 129 bytes

=IFERROR(IF(FIND(B1,A1)=1,SUBSTITUTE(A1,B1,"",1),IF(FIND(B1,A1,LEN(A1)-LEN(B1))>LEN(A1)-LEN(B1),LEFT(A1,LEN(A1)-LEN(B1)),A1)),A1)
\$\endgroup\$
0
\$\begingroup\$

sed, 56 53 bytes

s/^(.*),(.*)\1$/\2/
s/^(.*),\1(.*)$/\2/
s/.*,(.*)/\1/

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.