String subtraction

37

2

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!

Rod

Posted 2017-05-16T16:00:10.003

Reputation: 17 588

4Why 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? – Leaky Nun – 2017-05-16T16:01:40.770

@LeakyNun was a typing error, fixed – Rod – 2017-05-16T16:03:27.347

I think the empty string edge-case should probably be in the invalid section - mind you I suppose it doesn't make any difference to the result :) – Jonathan Allan – 2017-05-16T16:14:35.637

I think you should drop the terms "valid" and "invalid", as they have a common meaning on PPCG which you are clearly not intending. – Ørjan Johansen – 2017-05-16T16:22:00.350

7Damn you, 'abcde','bcd' -> 'abcde', for breaking my solution – John Dvorak – 2017-05-16T16:26:07.907

5Can we assume the strings will be regex-safe (alphanumeric + spaces)? – John Dvorak – 2017-05-16T16:27:07.603

2I'd suggest 'ababcde', 'ab''abcde' as a test case. Some naive algorithms fail on that one. – None – 2017-05-16T16:48:18.420

@ais523 good point – Rod – 2017-05-16T16:53:22.280

Wait, why 'abcde','bcd' -> 'abcde'? abcde contains bcd, and should therefore return ae. – MD XF – 2017-05-16T19:42:37.287

@MDXF the objective here is "reverse a concatenation", not just to remove a substring c: – Rod – 2017-05-16T19:45:19.690

@Rod Yeah, just noticed that. Whoops. – MD XF – 2017-05-16T19:45:35.323

2@Rod You might consider retitling the challenge "Reverse string concatenation"? – MD XF – 2017-05-16T19:52:15.740

Suggested Testcase AbcdEbcd, bcd => AbcdE and not AEbcd – Wernisch – 2017-08-09T13:30:47.610

@Wernisch I guess this is covered by 'acdbcd','cd' -> 'acdb', right? – Rod – 2017-08-09T13:33:20.743

@Rob, You are right, apologies. Had incorrectly copied that test case. – Wernisch – 2017-08-09T13:44:33.667

Answers

19

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!)

Okx

Posted 2017-05-16T16:00:10.003

Reputation: 15 025

Use currying to save a byte a->b-> – TheLethalCoder – 2017-05-16T17:08:28.760

@TheLethalCoder Thanks. – Okx – 2017-05-16T17:28:59.023

Why did you leave in the unused hashmap in your online example? – Michael – 2017-05-17T12:52:08.700

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.

– Kevin Cruijssen – 2017-05-18T09:00:51.207

@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" -> "" – Okx – 2017-05-18T09:43:27.710

@Okx Ah, good call, missed that part of the abcab + ab test case. In that case you'll indeed need First. Oh, but you can still golf it by 4 bytes by changing "(^"+b+")|("+b+"$)" to "^"+b+"|"+b+"$" if I'm not mistaken. – Kevin Cruijssen – 2017-05-18T09:48:38.247

9

JavaScript (ES6), 41 bytes

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

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

ETHproductions

Posted 2017-05-16T16:00:10.003

Reputation: 47 880

3Now, why have I never thought to use eval() for constructing RegExes before?! – Shaggy – 2017-05-16T16:35:25.607

9

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}.

user62131

Posted 2017-05-16T16:00:10.003

Reputation:

6

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>

Shaggy

Posted 2017-05-16T16:00:10.003

Reputation: 24 623

2You don't need new. – programmer5000 – 2017-05-16T18:11:29.733

@programmer500, I kinda gave up working on this when I saw ETH's version! :D Updated now. Thanks. – Shaggy – 2017-05-16T18:17:13.830

6

Retina, 21 bytes

1 byte thanks to Martin Ender.

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

Try it online!

Leaky Nun

Posted 2017-05-16T16:00:10.003

Reputation: 45 011

4

Japt, 11 9 bytes

k"^|$"¬qV

Try it online!

Luke

Posted 2017-05-16T16:00:10.003

Reputation: 4 675

4

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.

}

Brad Gilbert b2gills

Posted 2017-05-16T16:00:10.003

Reputation: 12 713

3

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

pizzapants184

Posted 2017-05-16T16:00:10.003

Reputation: 3 174

I have a question, why didn't you use Str1 as a variable? – Zacharý – 2017-08-09T20:57:21.483

@Zacharý I think I had something stored in it at the time. I don't really remember. – pizzapants184 – 2017-08-09T21:02:27.593

What does Ans even refer to on the fourth line? – Zacharý – 2017-08-09T21:30:53.197

@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. – pizzapants184 – 2017-08-09T21:34:50.333

Oh, I forgot how inString worked. Nice golf! – Zacharý – 2017-08-09T21:42:46.137

3

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

Adám

Posted 2017-05-16T16:00:10.003

Reputation: 37 779

I think you can save a byte with {0::⍺{0::⍺⋄,∘⍵⍣¯1⊢⍺}⍵⋄⍵,⍣¯1⊢⍺}. – Zacharý – 2017-08-09T20:56:01.977

@Zacharý Yes, thanks. – Adám – 2017-08-09T21:22:26.737

3

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

marcosm

Posted 2017-05-16T16:00:10.003

Reputation: 986

1Nice 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. – manatwork – 2017-05-18T07:47:48.140

3

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!

Artyer

Posted 2017-05-16T16:00:10.003

Reputation: 1 697

For -2 bytes: re.sub(c.join("^|$"),'',s,1) – Value Ink – 2017-05-17T00:33:43.773

1For -6 bytes:c+'$|^'+c – Felipe Nardi Batista – 2017-05-18T00:02:02.350

3

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"]

J42161217

Posted 2017-05-16T16:00:10.003

Reputation: 15 931

1

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?

– Not a tree – 2017-05-17T00:50:55.137

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? – Not a tree – 2017-05-17T01:13:49.337

You are allowed to take a String array as input, so you don't really need c=Characters;a=c@#;b=c@#2; – JungHwan Min – 2017-05-17T05:55:18.130

Also, l@Intersection[a,b] is l[a∩b]. – JungHwan Min – 2017-05-17T05:56:07.650

2

PHP, 54 Bytes

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

Testcases

Jörg Hülsermann

Posted 2017-05-16T16:00:10.003

Reputation: 13 026

2

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

ovs

Posted 2017-05-16T16:00:10.003

Reputation: 21 408

golfed – Felipe Nardi Batista – 2017-05-17T23:33:58.523

@FelipeNardiBatista thanks a lot! – ovs – 2017-05-18T04:26:49.887

2

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!

Jonathan Allan

Posted 2017-05-16T16:00:10.003

Reputation: 67 804

2

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.

Laikoni

Posted 2017-05-16T16:00:10.003

Reputation: 23 676

1

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>>.

TheLethalCoder

Posted 2017-05-16T16:00:10.003

Reputation: 6 930

1

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!

Giuseppe

Posted 2017-05-16T16:00:10.003

Reputation: 21 077

1What about the 'abcde','bcd' -> 'abcde' case? – Jonathan Allan – 2017-05-16T16:18:24.777

"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? – TheLethalCoder – 2017-05-16T16:18:27.953

I misread the question! Oops. Thanks for catching that! – Giuseppe – 2017-05-16T16:31:18.567

you can get 1 byte back with sprintf('^%s|%s$',b,b) – MickyT – 2017-05-17T22:36:36.977

@MickyT, thanks! fixed. – Giuseppe – 2017-05-18T19:02:10.740

1

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.

John Dvorak

Posted 2017-05-16T16:00:10.003

Reputation: 9 048

1

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.

Michael Plotke

Posted 2017-05-16T16:00:10.003

Reputation: 111

1

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.

Robert Benson

Posted 2017-05-16T16:00:10.003

Reputation: 1 339

1

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"

algmyr

Posted 2017-05-16T16:00:10.003

Reputation: 858

1

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

Renzo

Posted 2017-05-16T16:00:10.003

Reputation: 2 260

1

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!

snail_

Posted 2017-05-16T16:00:10.003

Reputation: 1 982

? {a,b->var c=a.removePrefix(b);if(a==c){c=a.removeSuffix(b)};c} – mazzy – 2018-10-02T08:33:53.100

@mazzy feel free to submit that as your own answer. – snail_ – 2018-10-02T18:01:24.877

1

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:

mazzy

Posted 2017-05-16T16:00:10.003

Reputation: 4 832

0

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$

steenbergh

Posted 2017-05-16T16:00:10.003

Reputation: 7 772

0

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!

Jeff Freeman

Posted 2017-05-16T16:00:10.003

Reputation: 221

This fails on the 'abcde' 'bcd' case. – Ørjan Johansen – 2017-05-17T23:11:47.037

I'm seeing expected results from that test case -- TIO here

– Jeff Freeman – 2017-05-18T14:25:47.997

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. – Ørjan Johansen – 2017-05-18T17:13:29.323

You are correct. I misread the instructions. Thanks for pointing it out! – Jeff Freeman – 2017-05-18T17:49:08.847

0

Lua, 71 65 bytes

Accepting suggestions

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

Try it online!

Felipe Nardi Batista

Posted 2017-05-16T16:00:10.003

Reputation: 2 345

0

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)

Wernisch

Posted 2017-05-16T16:00:10.003

Reputation: 2 534

0

sed, 56 53 bytes

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

Try it online!

Logern

Posted 2017-05-16T16:00:10.003

Reputation: 845

0

Pip, 19 bytes

Y'^.X:baRyNa?yb.'$x

Try it online!

Kinda sad this isn't shorter. The main problem is that Pip's R operator replaces all matches, not just the first one; otherwise, aR"^|$"J Xbx would work for 12 bytes.

                     a, b are command-line args; x is empty string (implicit)
    X:b              Convert b to a regex in-place
 '^.                 Prepend ^
Y                    and yank that regex into the y variable
       aR         x  In a, replace the following expression with the empty string:
         yNa?         Is there a match of y in a? I.e., does b match at the beginning of a?
             y        If so, replace y (b at the beginning of a)
              b.'$    If not, replace b with $ appended (b at the end of a)

DLosc

Posted 2017-05-16T16:00:10.003

Reputation: 21 213

0

Kotlin, 62 42 56 bytes

+12 bytes I think that type definitions are mandatory in the Kotlin lambda with 2 arguments

{a:String,b:String->Regex("^$b|$b$").replaceFirst(a,"")}

Test script:

var f = {a:String,b:String->Regex("^$b|$b$").replaceFirst(a,"")}

fun main(args: Array<String>) {
    println(f("abcde","ab"))
    println(f("abcab","ab"))
    println(f("abcde","bcd"))
}

Output:

cde
cab
abcde

Kotlin (without Regex), 62 76 bytes

{a:String,b:String->var c=a.removePrefix(b)
if(a==c){c=a.removeSuffix(b)}
c}

mazzy

Posted 2017-05-16T16:00:10.003

Reputation: 4 832