(Inspired by this challenge)

Given two input strings, one of which is exactly one character longer than the other, arrange the strings into ASCII art as if they are two halves of a zipper that is only halfway zippered. The longer word forms the bottom of the zipper, and is the first and last character of the combined zippered portion. Since that paragraph is hard to understand, take a look at some examples:



Note how paragraph (the longer word) forms the bottom zipper, and the g r a p h portion encapsulates the e r e d portion of zippered, and the z i p p and p a r a portions are offset from each other.


  • Two ASCII strings in any convenient format, with one guaranteed to be even in length and the other exactly one character longer.
  • Neither string will contain whitespace, but may contain any other printable ASCII value.
  • You can take the input in either order. Please state in your submission the input order.


The resulting ASCII art representation of the zippered words, as described above, again in any convenient format.


  • Leading or trailing newlines or whitespace are all optional, so long as the characters themselves line up correctly.
  • Either a full program or a function are acceptable. If a function, you can return the output rather than printing it.
  • If possible, please include a link to an online testing environment so other people can try out your code!
  • Standard loopholes are forbidden.
  • This is so all usual golfing rules apply, and the shortest code (in bytes) wins.





  • \$\begingroup\$ May we assume the input does not contain whitespace? \$\endgroup\$
    – DJMcMayhem
    Aug 1, 2017 at 17:33
  • \$\begingroup\$ @DJMcMayhem Yeah, that's a fair assumption. \$\endgroup\$ Aug 1, 2017 at 17:38
  • 1
    \$\begingroup\$ @Titus one guaranteed to be even in length and the other exactly one character longer. The shorter string is always even \$\endgroup\$
    – Baldrickk
    Aug 2, 2017 at 9:25

16 Answers 16


Japt, 31 28 bytes

N®¬£ç iXYm½*Ul
uUo mw
y c ·y

Test it online! Takes the shorter string first.


N®¬£ç iXYm½*Ul    First line: Set U to the result.
N®                Map each item (there's exactly 2 of them) in the input to
  ¬                 the item split into chars,
   £                with each item X and index Y mapped to
    ç                 the first input filled with spaces,
      iX              with X inserted at index
        Ym½*Ul          min(Y, 0.5 * U.length).
                  At the end each input is an array like
                  ["p    ", " p   ", "  c  ", "  g  "]
                  ["t    ", " e   ", "  s  ", "  t  ", "  s  "]

uUo mw    Second line: Set V to the result (though that's not important).
 Uo       Pop the last item (the array representing the second string) from U.
    m     Map each item by
     w      reversing.
u         Push the result to the beginning of U.
          At the end we have e.g.
          ["    t", "   e ", "  s  ", "  t  ", "  s  "]
          ["p    ", " p   ", "  c  ", "  g  "]

y c ·y    Last line: Output the result of this line.
y         Transpose: map [[A,B,C,...],[a,b,c,...]] to [[A,a],[B,b],[C,c],...].
  c       Flatten into one array. [A,a,B,b,C,c,...]
    ·     Join on newlines. Now we have the output transposed.
     y    Transpose rows with columns.

Charcoal, 33 31 bytes


Try it online! Link is to verbose version of code. Takes the shorter string first. Edit: Saved 2 bytes by tweaking the midpoint detection. Explanation:


Loop over each string in turn.


Loop over each character of the string in turn.


Print the character and move an extra square to the right.


For the first half of the string, also move the cursor down or up as appropriate.


After printing the first string, jump to the second string's starting point, and replace the first string with the second, so that it gets printed for the second loop. (The code runs on both loops, but the second time it's a no-op.)


Pyth, 35 bytes


Try it online!

  • 1
    \$\begingroup\$ That was speedy fast! \$\endgroup\$ Aug 1, 2017 at 16:22
  • \$\begingroup\$ @AdmBorkBork You can expect that to happen multiple times, especially from this answerer. \$\endgroup\$ Aug 1, 2017 at 17:15

Jelly,  27  26 bytes

-1 byte thanks to Erik the Outgolfer (use repeat, ¡, to replace if, ?, and a passed else clause ¹)


A full program that prints the result with leading white-space, as allowed in the question (or a dyadic link returning a list of characters).

Try it online!


JCḂ¡€ṚH - Link 1, get rotations: list p        e.g.: ["a1","b2","c3","d4","e5","f6","g"]
J       - range of length of p                       [ 1, 2, 3, 4, 5, 6, 7]
    €   - for €ach:
   ¡    -   repeat link:
  Ḃ     - ...# of times: modulo 2                      1  0  1  0  1  0  1
 C      - ...link: complement (1-x)                    0  2 -2  4 -4  6 -6
     Ṛ  - reverse                                    [-6, 6,-4, 4,-2, 2, 0]
      H - halve                                      [-3, 3,-2, 2,-1, 1, 0]

żµL⁶ẋ;ЀFṙ"ÇZṙÇṀ$Y - Main link: longer (odd length); shorter (even length)
                   -                           e.g.: "abcdefg", "123456"
ż                  - zip them together               ["a1","b2","c3","d4","e5","f6","g"]
 µ                 - monadic chain separation, call that p
  L                - length of p                     7
   ⁶               - literal space character         ' '
    ẋ              - repeat                          "       "
        F          - flatten p                       "a1b2c3d4e5f"
      Ѐ           - map with:
     ;             -   concatenation                 ["       a","       1","       b","       2","       c","       3","       d","       4","       e","       5","       f","       6","       g"]
           Ç       - call last link (1) as a monad with argument p
          "        - zip with (no action on left by trailing values of right):
         ṙ         -   rotate left by                ["  a     ","    1   "," b      ","     2  ","c       ","      3 ","       d","       4","       e","       5","       f","       6","       g"]
            Z      - transpose                       ["    c        ","  b          ","a            ","             "," 1           ","   2         ","     3       ","      d4e5f6g"]
                $  - last two links as a monad:
              Ç    -   call last link (1) as a monad with argument p
               Ṁ   -   maximum                       3
             ṙ     - rotate left by                  ["             "," 1           ","   2         ","     3       ","      d4e5f6g","    c        ","  b          ","a            "]
                 Y - join with newlines            '''             \n
                                                       1           \n
                                                         2         \n
                                                           3       \n
                                                          c        \n
                                                        b          \n
                                                      a            '''
                   - as full program: implicit print
  • \$\begingroup\$ C¹Ḃ? -> CḂ¡ \$\endgroup\$ Aug 2, 2017 at 9:11

Python 2, 128 119 bytes

f=lambda a,b,n=0:n/2<len(a)and' '*-~n+a[0]+'\n'+f(a[1:],b[1:],n+2)+'\n'+' '*n+b[0]or' '*n+''.join(sum(zip(b,a+' '),()))

Try it online!


V, 47 38 30 27 26 25 bytes

Finally beat the current Jelly answer \o/

Takes input with the longer word on top

Explanation coming, don't think there's much more to golf.


Try it online!


ò     ò      ' <M-r>ecursively
 Gx          ' (G)oto the last line and (x) the first character
            ' <C-O> Go back to the previous location
    p        ' (p)aste the character cut
     l       ' move one character right

x                  ' (x) the last extraneous character from the previous loop
 ã                 ' <M-c>enter the cursor
  ò                ' <M-r>ecursively
   d|              ' (d)elete to the first co(|)umn
     >H            ' (>) Indent every line from here to (H)ome (first line)
                   ' this leaves the cursor on the first line
       Ïp          ' <M-O>n a newline above this (the first) (p)aste the deleted section
                   ' this leaves the cursor on the last character
         d|        ' (d)elete to the first co(|)umn
           >G      ' (>) Indent every line from here to the end (G)
                   ' unfortunately the cursor stays on the first line
             G     ' (G)oto the last line
              Ïp   ' <M-O>n a newline above this (the last) (p)aste the deleted section
                hl ' move left and then right (break the loop at the end)

V, 79 bytes

ãl}dÍ./ &
XòYf D"0Pr -Y;D"0pr +òGï"1pÓ./&ò

Try it online!

The following should be read with plenty of sarcasm and air-quotes.

Here's an answer in my golfing-language that's good at short answers to string-based and ASCII-art challenges.

Why do I keep doing this to myself?


00000000: e36c 167d 64cd 2e2f 2026 0a58 f259 6620  .l.}d../ &.X.Yf 
00000010: 4422 3050 7220 2d59 3b44 2230 7072 202b  D"0Pr -Y;D"0pr +
00000020: f247 ef22 3170 d32e 2f26 f20a 0f16 7d64  .G."1p../&....}d
00000030: 47d3 2f26 f20a 7b6a 70f2 674a f2d3 f30a  G./&..{jp.gJ....
00000040: 7c44 c72e 2f64 0a4d d922 2d70 4272 20    |D../d.M."-pBr 
  • \$\begingroup\$ Does V have a "transpose rows with columns" command? 'Cause if not, you might wanna invest in that... \$\endgroup\$ Aug 1, 2017 at 19:00

Jelly, 28 bytes


Try it online!

Woo Jelly is actually competing in a and challenge! \o/

  • \$\begingroup\$ Mainly because every other language is having trouble as well. Speaking of which, someone might wanna go talk to V... \$\endgroup\$ Aug 1, 2017 at 18:58
  • \$\begingroup\$ Nice, I managed 27 - but maybe you can abuse leading/trailing white-space allowance too? \$\endgroup\$ Aug 1, 2017 at 20:24
  • \$\begingroup\$ @JonathanAllan Sadly I think that's impossible. Removing the will not add a trailing space but a trailing 1. And omitting anything that has to do with spaces removes the line-up of the letters. Generally, this algorithm uses indexing so that the letters get to a particular index in a column and then the rest is filled with spaces, so I think this can't be golfed any further. At least I'm glad that Jelly isn't outgolfed by CJam. ;) \$\endgroup\$ Aug 2, 2017 at 8:36
  • \$\begingroup\$ :| Jelly is golfier than Charcoal \$\endgroup\$
    – ASCII-only
    Aug 7, 2017 at 5:55

05AB1E, 26 23 bytes


Try it online!


With example input = ppcg, tests

ø                           # zip the input strings
                            # STACK: ['tp', 'ep', 'sc', 'tg']
 S                          # split to a list of characters
                            # STACK: ['t', 'p', 'e', 'p', 's', 'c', 't', 'g'
  2ä                        # divide the list into 2 parts
    `                       # push them as separate to stack
                            # STACK: ['t', 'p', 'e', 'p'], ['s', 'c', 't', 'g']
     J                      # join the second part to a single string
      Iθ«                   # append the tail of the second input
         ¸«                 # concatenate the 2 lists
                            # STACK: ['t', 'p', 'e', 'p', 'sctgs']
           v                # for each y,N (element, index) in the list
            yNú             # prepend N spaces to y
               r            # reverse the stack
                })          # end loop and wrap the stack in a list
                            # STACK: ['    sctgs', '  e', 't', ' p', '   p']
                  2ä        # split the list into 2 parts
                    R       # reverse the list
                            # STACK: [[' p', '   p'], ['    sctgs', '  e', 't']]
                     ˜»     # flatten the list and join on newlines
  • 1
    \$\begingroup\$ I tried really hard on this question like a week ago and you had to just go and... beat me. +1 for making me try a little more! \$\endgroup\$
    – nmjcman101
    Aug 9, 2017 at 13:07
  • \$\begingroup\$ @nmjcman101: I hope you can get yours down a bit more. Some friendly competition is always fun :) \$\endgroup\$
    – Emigna
    Aug 9, 2017 at 13:28

C# (.NET Core), 163 bytes

(l,s)=>{var o="";int i=0,k=s.Length;for(;i<k;)o+=i<k/2?s[i++]+"\n"+"".PadLeft(i):l[i]+""+s[i++];o+=l[i]+"\n";for(i=k/2;i>0;)o+="".PadLeft(--i)+l[i]+"\n";return o;}

Try it online!

Probably a lot of golfing to do here, but here's an initial non-LINQ attempt. Lambda function that takes the longer word first, and returns a string with the ouput.

  • 1
    \$\begingroup\$ Use currying to save a byte (l=>s=>) i.e. Func<input1, Func<input2, output>>. \$\endgroup\$ Aug 2, 2017 at 8:38

Java 8, 216 bytes

A curried lambda: takes String and returns a lambda from String to String. The parameter to the outer lambda is the shorter string.

Not being able to index into Strings with array syntax is...unfortunate.

s->t->{int l=s.length(),i=l/2;String o="",p=o,n="\n";for(;i<l;p+="  ")o=o+t.charAt(i)+s.charAt(i++);o=p+o+t.charAt(i)+n;for(;i-->0;)o=p.substring(l-i--)+s.charAt(i/2)+n+o+p.substring(l-i)+t.charAt(i/2)+n;return o;}

Ungolfed lambda

s ->
    t -> {
            l = s.length(),
            i = l / 2
            o = "",
            p = o,
            n = "\n"
        for (; i < l; p += "  ")
            o = o + t.charAt(i) + s.charAt(i++);
        o = p + o + t.charAt(i) + n;
        for (; i-- > 0; )
            o =
                + s.charAt(i / 2)
                + n
                + o
                + p.substring(l-i)
                + t.charAt(i / 2)
                + n
        return o;


l is the length of the shorter input and i is a multipurpose index, initialized to refer to the first character of the second half of the shorter input. o accumulates the result, p ultimately stores spaces for padding, and n is an alias for "\n".

The first loop interleaves the second halves of the two strings (excluding the last character of the longer input) and builds p to the proper amount of padding for the middle line.

The next line completes the middle line of output.

I would like to apologize to James Gosling for the second loop. It adds the lines above and below the middle line from the inside out. Entering the loop, i is l - 1, so one character of padding is prepended along with the last character of the first half of the shorter string. i is decremented so that the next padding (appended to the result) is a character shorter. By integer division, the same position character of the longer string is appended. This repeats, and the completed result is returned.

Cool stuff

Line 13 used to be


because without the empty string, + added the character values together and appended a numeric string. By expanding the compound assignment, the concatenation of o and t.charAt(i) is evaluated first, which yields the desired result without need for the empty string, saving 2 bytes. This is the first time I've seen a compound assignment behave differently from its expansion.


Javascript (ES6), 140 137 133 bytes

A=(a,b,c=0)=>a[c/2]?` `[d=`repeat`](c+1)+a[0]+`
`+` `[d](c)+b[0]:` `[d](c)+[...a].map((e,f)=>e+b[f])

Quite sure this can be golfed further

  • \$\begingroup\$ E.g. can't `<newline>`+` ` be merged into `<newline> `? (I don't know JS). \$\endgroup\$
    – Kaz
    Aug 2, 2017 at 0:46
  • \$\begingroup\$ @Kaz: no, because we execute the repeat method on just the space, and not the newline+space. \$\endgroup\$
    – Luke
    Aug 6, 2017 at 9:59

Mathematica, 174 bytes

(a=(c=Characters)@#;b=c@#2;T=Table;Column[Join[T[T["  ",i]<>a[[i]],{i,g=Length@a/2}],{T["  ",g+1]<>Riffle[b[[-g-1;;]],a[[-g;;]]]},Reverse@T[T["  ",i]<>b[[i+1]],{i,0,g-1}]]])&


["zippered", "paragraph"]


TXR Lisp, 126 bytes

(defun f(a b :(n 0))(if(<(/ n 2)(length a))` @{""n}@[a 0]\n@(f(cdr a)(cdr b)(+ n 2))\n@{""n}@[b 0]``@{""n}@{(zip b`@a `)""}`))

PHP, 149 129 bytes

for($r=" ";$x+1<$w=2*$e=strlen($argv[2]);++$x&1||$r[++$i*$w-1]="

Run with -nr or try it online.


Perl 5, 163 bytes

@a=map{$s=.5*length;[/./g]}<>;say(($"x(2*$_)).$a[0][$_])for 0..$s-1;print$"x(2*$s);print$a[0][$_].$a[1][$_]for$s..@{$a[1]};print$/.($"x(1+2*$s)).$a[1][$s]while$s--

Try it online!

Takes the longer string first.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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