# Surjection from one string to two strings

This is the inverse of negative seven's question.

Write a program or function which, given any single, possibly-empty string of printable ASCII (codes $$$32,126]\$$) outputs or returns two strings of printable ASCII. For any two ordered, possibly empty strings $$\s_1\$$ and $$\s_2\$$, there must be third string $$\s_0\$$, which input to your program, outputs $$\s_1\$$ and $$\s_2\$$. In other words, if $$\\mathbb S\$$ is the set of all ASCII strings (including the empty string): $$\f\$$ is well-defined on $$\\mathbb S\$$: $$\(\forall s\in\mathbb S)(f(s)\in\mathbb S\times\mathbb S)\$$ $$\f\$$ is a surjection to $$\\mathbb S\times\mathbb S\$$: $$\(\forall s_1,s_2\in\mathbb S)(\exists s_0\in\mathbb S)(f(s_0)=(s_1,s_2))\$$ ## Test cases: For each of these strings (surrounding «quotes» excluded), your output should be two strings: «» «hello world» «'one' 'two' 'three' 'four'» «"one" "two" "three" "four"» «["one", "two", "three"]» «; exit()»  For each of these pairs of strings, there must be at least one string you can provide to your program such that that pair is output (surrounding «quotes» excluded):  s_1 | s_2 ---------|---------- «hello » | «world» «hell» | «o world» «hello» | « world» « » | «» «» | « » «» | « !"#%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[$^_abcdefghijklmnopqrstuvwxyz{|}~»
«\r\n»   | «\r\n»


As always, you are encouraged to add test cases pertinent to your method.

## Rules:

• Input must be a single string/character array/byte array consisting of only ASCII characters.
• Output is ordered; $$\(a,b)\neq(b,a)\$$. Output can be any ordered structure in your language, or printed with a newline (LF or CR LF) separator and optional trailing newline.
• I/O must be consistent. If one case handles input as an argument and outputs to stdout with a trailing newline, all cases must handle input as an argument and output to stdout with a trailing newline.
• Standard loopholes are forbidden.
• This is , so shorter programs are better!

As a reminder, this is only a surjection, not a bijection: Your program/function will likely map many different strings to the same output.

# Python 2, 42 bytes

lambda s:(s.split("1"+len(s)/2*"0")*2)[:2]


Try it online!

The inverse is:

def F(a,b):
return a+"1"+len(a+b)*"0"+b


A tempting idea for the inverse is to encode the pair of input strings by putting some separator string in between them. That way, we can extract the pair by splitting on this separator. The issue is that the separator may appear in the inputs string.

We get around this by making the separator longer than the total length of both strings. We use 1 followed by a number of 0's equal to the number of characters in both strings. For example,

abc, defgh -> abc100000000defgh


Note that no matter how many 0's and 1's are in the two inputs, there's no way to get another copy of the separator to appear.

We can recover the separator from the combined string by using a number of zeroes equal to half its length rounded down. I had wanted to compute this as 10**_, but Python's repr appends an annoying L to large numbers, which messes it up.

We need to the submission function still give two outputs when the input string doesn't contain the required separator. In this case, splitting just gives the original string, but we copy it twice to be compliant.

Python 2, 35 bytes (not working)

lambda s:s.split(s[:len(s)/3])[-2:]


Try it online!

This would work except it gives an error for the empty string. The idea is to have the inverse encode a,b -> sep + a + sep + b, where the separator sep has the same length as a+b so we can extract it as the first third of the string. A similar idea of putting the separator interspersed runs into the same issue.

Python 2, 43 bytes

lambda s:[s[i::2].lstrip()[1:]for i in 0,1]


Try it online!

The idea for encoding is to intersperse the two input strings with alternating characters. The problem there is that they may have different lengths. So, we pad them to the same length by prepending each one with some number of leading spaces then | (any non-space char will do). For example, abcd, ef goes to

|abcd
|ef


This can be undone for each string by stripping off all leading spaces then removing the first character. Note that any spaces or | in the actual strings are left unharmed.

Python 3.8 (pre-release), 44 bytes

lambda s:(s[(i:=s.find('_'))+1:2*i],s[2*i:])


Try it online!

Inverse:

F=lambda a,b:(len(a)+1)*"?"+"_"+a+b


For instance, F("ab","cdef") = ???_abcdef. The number of symbols before the _ tells us where to split the remainder -- it's one more than the length of the first string.

We need the submission not to error when there's no _, but this works out because Python's string find uses -1 for missing, which still gives valid slices.

• Your 35 byte solution that "Doesn't work" just has an off by one error. Your separator strings are of length 1+|a|+|b|, which is equal to |s|/3+1, since |s|=3|a|+3|b|+2. So if you change that solution to lambda s:s.split(s[:len(s)/3+1])[-2:], just adding a +1, it works fine, for 37 bytes. (Unless I'm missing something?) Try it online! Dec 14 '19 at 0:50
• @isaacg Nice find, that does work. And [1:] for [-2:] saves another byte -- I believe the split is always exactly 3 elements. You can post an answer for the bounty.
– xnor
Dec 14 '19 at 7:21
• On more thought, I realised this still doesn't work - f(F('','')) is fixed, but f('') still errors. It'll take more work to fix that. I'll keep looking. Dec 14 '19 at 18:28
• I couldn't find any way to fix the empty string problem in under 6 bytes, making it not an improvement: Try it online! Dec 14 '19 at 18:38

f(' ':a:b)=(a:)<$>f b f s=(drop 1s,"")  Try it online! Not using built-in parsing. 21 bytes (doesn't quite work) last.(("",""):).reads  Try it online! (As pointed out by @benrg, this produces unprintable characters outside the valid range on some inputs like "\1" that decode to escape characters.) The inverse is g(a,b)=show a++b  This puts the first string in quotes, escaping any characters as needed, and appends the second string. This is undone by reads, which looks for a prefix that's a string representation of the correct type (here, string), reads it, and puts it in a pair with the remainder. To make the function total, if this fails, we just return two empty strings. Note that reads only does lexing, not evaluation, so it won't mess up on weird inputs. If we may output a singleton list of a two-string pair, we can save 3 bytes: 18 bytes max[("","")].reads  Try it online! • Unfortunately the reads solutions don't work because, for instance, the input "\"\\12345\"" contains only printable ASCII but the output contains a non-ASCII character. It's a shame because they're nicely golfed. Oct 21 '19 at 8:13 • @benrg Good find. I had tried testing with backslashes but hadn't though about escapes like that. – xnor Oct 22 '19 at 7:25 # Python 2, 53 52 bytes lambda s:(s.split('_x'+s.count('x')/2*'x')+[''])[:2]  Try it online! • Excellent! Thanks for providing an inverse function as well. Oct 20 '19 at 1:52 • @GammaFunction: The inverse function was the Motivator! Oct 20 '19 at 3:31 • How do I produce the pair "First_String", "Second_String"? Oct 21 '19 at 9:01 # 05AB1E, 8 7 bytes g2÷°¡2∍  -1 byte thanks to @Grimy. Inverse (4 bytes): Jg°ý  Port of @xnor's first Python 2 approach, so make sure to upvote him as well!! Explanation: g # Get the length of the (implicit) input-string 2÷ # Integer-divide it by 2 ° # Take 10 to the power this ¡ # And split the (implicit) input-string by this integer 2∍ # Extend this list to (or keep it at) size 2 # (after which this pair is output implicitly as result) J # Join the strings in the (implicit) input-list together to a single string g # Get the length of this ° # Take 10 the power this ý # And join the strings of the (implicit) input-list with this as delimiter # (after which the resulting string is output implicitly as result)  • What a horrific language! +1 Oct 21 '19 at 17:52 • Þ2£ -> 2∍ (: Oct 28 '19 at 15:50 • @Grimmy Now that I see it I don't understand why I used Þ like that in the first place, haha. First time I use it, even though I've used ∍ at least two dozen times. Thanks! Oct 28 '19 at 20:12 # Retina 0.8.2, 33 bytes ^(("[^"]*")*),?$1¶
%^"|"("?)
$1  Try it online! Link includes test cases. Takes as input the output from my Charcoal answer to @negativeseven's question and outputs the two strings on separate lines. Explanation: ^(("[^"]*")*),?$1¶


My Charcoal answer always outputs a number of quote-surrounded strings followed by a comma. Find that comma and split the strings there. If there's no comma, the split happens after the quote-surrounded strings, if any.

%^"|"("?)
$1  Decode each string by deleting the first quote, one quote of each pair, and the last quote. (Assuming this is an encoded string, of course... otherwise this just deletes arbitrary quotes.) # Ruby, 81 bytes ->s{s.match(/^(\d+)\.(.*)/m){|m|l=m[1].to_i;[m[2][0,l],m[2][l..-1]||'']}||['',s]}  Try it online! I don't golf much in Ruby so this can surely be shortened -- tips appreciated. Idea is to to provide a meta-prefix <number>.rest of string which will split "rest of string" at number. Strings without the meta-prefix just return ["", <original string>] # Retina 0.8.2, 17 bytes ^(.*)(.*)\1\1$2¶


Try it online!

This parses the string as $$\xs_1xxs_2\$$ for the longest possible $$\x\$$ due to the greedy nature of regexes. We may choose $$\x = 10^{\max \{|s_1|, |s_2|\}}\$$ to ensure the desired parse.

# Zsh, 37 bytes

a=("${(@Q)${(z)1}}")
<<<$a[1]' '$a[2]


Try it online!

• (z) splits a string according to shell grammar
• (Q) removes quotes
• (@) prevents array joining in quotes
• " "` are needed to preserve empty elements

Finally we print the first two strings with a quoted newline between them.