Challenge

Write a program or function that takes in a string s and integer n as parameters. Your program should print (or return) the string when transformed as follows:

Starting in the top-left and moving down and to the right, write s as a wave of height n. Then, from top to bottom, combine each row as a string (without spaces).

Example

Given the string "WATERMELON" and a height of 3:

The wave should look like this:

W   R   O
 A E M L N
  T   E

Then, combine the rows from top to bottom:

WRO
AEMLN
TE

So, your program should return the string "WROAEMLNTE"

Likewise, "WATERMELON" with height 4 should produce the following wave:

W     E
 A   M L
  T R   O
   E     N

Your program should then return the string "WEAMLTROEN"

Rules

Input

Input can be taken in any reasonable format. The string can be in any case you prefer. You may assume that 0 < n <= s.length

Output

Output should consist only of the transformed string (whether returned or printed to STDOUT), plus any trailing newlines.

Scoring

This is , so shortest answer in bytes wins! Standard loopholes are not allowed.

Test Cases

Input                        Output

programmingpuzzles, 5 ->     piermnlsomgzgapzru
codegolf, 3           ->     cgoeofdl
elephant, 4           ->     enlatehp
1234567, 3            ->     1524637
qwertyuiop, 1         ->     qwertyuiop
  • Can we assume n>1? Please clarify and if not add a test case – Luis Mendo Oct 16 at 13:49
  • 1
    You may assume n > 0, but n=1 is a valid case. I will update the question now. – Cowabunghole Oct 16 at 13:56
  • 2
    @Cowabunghole I know. :) Related only means it's somewhat similar and existing answers there might be helpful for this challenge. I only mention it to have them appear at the linked questions at the right. Related != duplicated. ;) – Kevin Cruijssen Oct 16 at 14:25
  • 5
    I've never seen a rail fence cipher encoded with only one rail. Just sayin' – wooshinyobject Oct 16 at 14:46
  • 1
    @Veskah Ah yes, the old double rot13 trick. – wooshinyobject Oct 17 at 12:40

26 Answers 26

MATL, 16 bytes

Zv3L)t?yn:)2$S}i

Try it online! Or verify all test cases.

Explanation

Consider inputs 5, 'programmingpuzzles'.

Zv     % Input, implicit: number n. Symmetric range
       % STACK: [1 2 3 4 5 4 3 2 1]
3L     % Push [1 -1+1j]. When used as an index, this means 1:end-1
       % STACK: [1 2 3 4 5 4 3 2 1], [1 -1+1j]
)      % Index. Removes last element
       % STACK: [1 2 3 4 5 4 3 2]
t      % Duplicate
       % STACK: [1 2 3 4 5 4 3 2], [1 2 3 4 5 4 3 2]
?      %   If non-empty and non-zero
       %   STACK: [1 2 3 4 5 4 3 2]
  y    %   Implict input: string s. Duplicate from below
       %   STACK: 'programmingpuzzles', [1 2 3 4 5 4 3 2], 'programmingpuzzles'
  n    %   Number of elements
       %   STACK: 'programmingpuzzles', [1 2 3 4 5 4 3 2], 18
  :    %   Range
       %   STACK: 'programmingpuzzles', [1 2 3 4 5 4 3 2], [1 2 3 ··· 17 18]
  )    %   Index modularly
       %   STACK: 'programmingpuzzles', [1 2 3 4 5 4 3 2 1 2 3 4 5 4 3 2 1 2]
  2$S  %   Two-input sort: stably sorts first input as given by the second
       %   STACK: 'piermnlsomgzgapzru'
}      % Else. This branch is entered when n=1. The stack contains an empty array
       %   STACK: []
  i    %   Take input
       %   STACK: [], [], 'programmingpuzzles'
       % End, implicit
       % Display stack, implicit. Empty arrays are not displayed

Haskell, 64 bytes

s#n=[c|m<-[0..n],(c,i)<-zip s.cycle$[0..n-1]++[n-2,n-3..1],i==m]

Try it online!

Husk, 6 bytes

δÖK…¢ḣ

Try it online!

Works for n = 1 as well.

Explanation

δÖK…¢ḣ  Implicit inputs, say n=4 and s="WATERMELON"
     ḣ  Range: [1,2,3,4]
    ¢   Cycle: [1,2,3,4,1,2,3,4,1,2,3,4..
   …    Rangify: [1,2,3,4,3,2,1,2,3,4,3,2..
δÖK     Sort s by this list: "WEAMLTROEN"
        Print implicitly.

The higher order function δ works like this under the hood. Suppose you have a higher order function that takes a unary function and a list, and returns a new list. For example, Ö takes a function and sorts a list using it as key. Then δÖ takes a binary function and two lists, zips the lists together, applies Ö to sort the pairs using the binary function as key, and finally projects the pairs to the second coordinate. We use K as the key function, which simply returns its first argument and ignores the second.

R, 68 bytes

function(s,n)intToUtf8(unlist(split(utf8ToInt(s),-(n:(2.9-n)-1)^2)))

Try it online!

  • -10 bytes thanks to @Giuseppe
  • -17 bytes because I was silly
  • -9 bytes and n=1 case fixed thanks to @J.Doe
  • -3 bytes thanks to @JayCe

J, 54, 29, 27 26 bytes

-1 byte thanks to hoosierEE

([\:#@[$[:}:|@i:@<:@]) ::[

Try it online!

  • @LuisMendo Hmm, once again I missed something important. Thanks! Fixed. – Galen Ivanov Oct 16 at 14:23
  • 1
    I initially missed it too, then realized and asked the OP. There should have been a test case with n=1 since the beginning – Luis Mendo Oct 16 at 14:25
  • 1
    |@i: instead of [:|i: saves a byte – hoosierEE Oct 17 at 14:30
  • @hoosierEE Yes, thanks! – Galen Ivanov Oct 17 at 14:39

Python 2, 119 108 98 92 91 97 93 91 90 bytes

lambda s,n:''.join(c*(j%(2*n-2or 1)in(i,2*n-i-2))for i in range(n)for j,c in enumerate(s))

Try it online!

-1 byte, thanks to Jonathan Frech

05AB1E (legacy), 11 8 bytes

Σ²Lû¨¾è¼

Inspired by @LuisMendo's MATL answer.
-3 bytes thanks to @Adnan because I'm a idiot.. >.>

Try it online.

Explanation:

Σ           # Sort the (implicit) input-string by:
 ²L         #  Create a list in the range [1, second input-integer]
            #   i.e. 5 → [1,2,3,4,5]
   û        #  Palindromize it
            #   i.e. [1,2,3,4,5] → [1,2,3,4,5,4,3,2,1]
    ¨       #  Remove the last item
            #   i.e. [1,2,3,4,5,4,3,2,1] → [1,2,3,4,5,4,3,2]
     ¾è     #  Index into it (with wraparound) using the counter_variable (default 0)
            #   i.e. counter_variable = 0 → 1
            #   i.e. counter_variable = 13 → 4
       ¼    #  And after every iteration, increase the counter_variable by 1

NOTE: The counter_variable is used, because in the Python Legacy version of 05AB1E, the Σ didn't had a builtin index-N, which it does have in the new Elixir rewrite version of 05AB1E. So why do I still use the Legacy version? Because in the Elixir rewrite it implicitly transforms the string to a list of characters, requiring an additional }J to transform it back into a string to output (and it also contains a bug right now where è doesn't work at all to index into the lengthened list.. :S)

  • You don't need the ¹g∍ part since 05AB1E uses cyclic indexing for è. – Adnan Oct 17 at 12:14
  • @Adnan Ah, I'm an idiot.. >.> Thanks! – Kevin Cruijssen Oct 17 at 12:24

Japt, 16 bytes

¬üÏu´VÑ aV°ÃÔc q

Test it online!

Explanation

 ¬ üÏ   u´ VÑ  aV° Ã Ô c q
Uq üXY{Yu--V*2 aV++} w c q    Ungolfed
                               Implicit: U = input string, V = size of wave
Uq                             Split U into chars.
   üXY{            }           Group the items in U by the following key function:
       Y                         Take the index of the item.
        u--V*2                   Find its value modulo (V-1) * 2.
               aV++              Take the absolute difference between this and (V-1).
                                 This maps e.g. indices [0,1,2,3,4,5,6,7,...] with V=3 to
                                                        [2,1,0,1,2,1,0,1,...]
                                 The items are then grouped by these values, leading to
                                 [[2,6,...],[1,3,5,7,...],[0,4,...]].
                     w         Reverse the result, giving [[0,4,...],[1,3,5,7,...],[2,6,...]].
                       c       Flatten.
                         q     Join back into a single string.
  • o.O That ü method is new? – Luis felipe De jesus Munoz Oct 16 at 15:10
  • Yep, added on Saturday :-) – ETHproductions Oct 16 at 15:12
  • You could take input as a character array to save a byte and output one or use the -P flag to save another 2. – Shaggy Oct 16 at 21:01

Jelly, 8 bytes

6 byter fails for height 1; two bytes used to address it ...maybe a 7 can be found?

ŒḄṖȯ1ṁỤị

A dyadic link accepting a positive integer and a list of characters which yields a list of characters.

Try it online!

How?

ŒḄṖȯ1ṁỤị - Link: positive integer N; list of characters, T
ŒḄ       - bounce (implicit range of) N -> [1,2,3,...,N-1,N,N-1,...,3,2,1]
  Ṗ      - pop off the final entry         [1,2,3,...,N-1,N,N-1,...,3,2]
   ȯ1    - OR one                          if this is [] get 1 instead
     ṁ   - mould like T (trim or repeat to make this list the same length as T)
      Ụ  - grade-up (get indices ordered by value - e.g. [1,2,3,2,1,2] -> [1,5,2,4,6,3])
       ị - index into T

JavaScript (ES6), 75 bytes

Shorter formula suggested by @MattH (-3 bytes)

Takes input as (string)(n).

s=>n=>--n?[...s].map((c,x)=>o[x=x/n&1?n-x%n:x%n]=[o[x]]+c,o=[])&&o.join``:s

Try it online!


JavaScript (ES7), 78 bytes

Saved 4 bytes thanks to @ETHproductions

Takes input as (string)(n).

s=>n=>--n?[...s].map((c,x)=>o[x=n*n-(x%(n*2)-n)**2]=[o[x]]+c,o=[])&&o.join``:s

Try it online!

  • My solution ended up being pretty similar to yours. You can save -3 bytes calculating the insert index of o with x/n&1?n-x%n:x%n instead of n*n-(x%(n*2)-n)**2. – MattH Oct 16 at 23:45
  • @MattH Nicely done. Thanks! – Arnauld Oct 17 at 5:02

MBASIC, 146 159 155 bytes

1 INPUT S$,N:DIM C$(N):P=1:D=1:FOR I=1 TO LEN(S$):C$(P)=C$(P)+MID$(S$,I,1)
2 IF N>1 THEN P=P+D
3 IF P=N OR P=1 THEN D=-D
4 NEXT:FOR I=1 TO N:PRINT C$(I);:NEXT

Updated to handle n=1

Output:

? programmingpuzzles, 5
piermnlsomgzgapzru

? codegolf, 3
cgoeofdl

? elephant, 4
enlatehp

? 1234567, 3
1524637

? WATERMELON, 4
WEAMLTROEN

? qwertyuiop, 1
qwertyuiop
  • Currently does not suppport case n=1. – wooshinyobject Oct 16 at 14:15
  • Updated to handle case n=1 – wooshinyobject Oct 16 at 15:08
  • Saved 4 bytes by cleaning up the compares. – wooshinyobject Oct 16 at 15:40

Perl 6, 49 bytes

->\n{*.comb.sort({-abs n-1-$++%(2*n-2||1)}).join}

Try it online!

Takes input as a curried function.

Explanation:

->\n{*.comb.sort({-abs n-1-$++%(2*n-2||1)}).join}
->\n{                                           }  # Take an number
     *.comb        # Turn the string into a list of chars
           .sort({                       })   # And sort them by
                           $++    # The index of the char
                              %(2*n-2||1)  # Moduloed by 2*(n-1) or 1 if n is 0
                       n-1-       # Subtract that from n-1
                   abs            # get the absolute value
                  -               # And negate to reverse the list
                                          .join  # and join the characters

The sequence that it is sorted by looks like this (for n=5):

(-4 -3 -2 -1 0 -1 -2 -3 -4 -3 -2 -1 0 -1 -2 -3 -4 -3 -2 -1)

J, 24 bytes

4 :'x\:(#x)$}:|i:<:y'::[

Try it online!

Explicit dyadic verb. Run it like 'codegolf' f 3.

How it works

4 :'x\:(#x)$}:|i:<:y'::[    x: string, y: height
4 :                         Define a dyadic verb:
               i:<:y        Generate a range of -(y-1) .. y-1
            }:|             Take absolute value and remove last
       (#x)$             1) Repeat to match the string's length
    x\:                     Sort x by the decreasing order of above
                     ::[    If 1) causes `Length Error`, return the input string instead

Normally, explicit function takes additional 5 bytes in the form of n :'...'. But if error handling is added, the difference goes down to 2 bytes due to the parens and space in (tacit)<space>::.

  • Why I always tend to use sort up?! Your explicit verb is still 3 bytes shorter. Good decision! – Galen Ivanov Oct 17 at 6:33

APL (Dyalog Classic), 23 bytes

{⍺[⍋(≢⍺)⍴(¯1↓⊢,1↓⌽)⍳⍵]}

Try it online!

K ( Kona ), 23 bytes

A translation of the J answer by Galen

{y@<(#y)#-1_(!x),|!x-1}

Powershell, 99 95 bytes

param($s,$n)$r=,''*$n
$s|% t*y|%{$r[((1..$n+$n..1)*$s.Length|gu)[$i++*($n-gt1)]-1]+=$_}
-join$r

Test script:

$f = {

param($s,$n)$r=,''*$n
$s|% t*y|%{$r[((1..$n+$n..1)*$s.Length|gu)[$i++*($n-gt1)]-1]+=$_}
-join$r

}

@(
    ,("1234567", 3            ,     "1524637")
    ,("qwertyuiop", 1         ,     "qwertyuiop")
    ,("codegolf", 3           ,     "cgoeofdl")
    ,("elephant", 4           ,     "enlatehp")
    ,("programmingpuzzles", 5 ,     "piermnlsomgzgapzru")
) | % {
    $s,$n,$e = $_
    $r = &$f $s $n
    "$($r-eq$e): $r"
}

Output:

True: 1524637
True: qwertyuiop
True: cgoeofdl
True: enlatehp
True: piermnlsomgzgapzru

Explanation

The script:

  • creates an array of rows,
  • fills rows with appropriate values,
  • and returns the joined rows.

The expression ((1..$n+$n..1)*$s.Length|gu generates a sequence like 1,2,3,3,2,1,1,2,3,3,2,1... and removes adjacent duplicates. gu is alias for Get-Unique.

  • For $n=3 the deduplicated sequence is: 1,2,3,2,1,2,3,2,1...
  • For $n=1 the deduplicated sequence is: 1

The expression $i++*($n-gt1) returns an index in the deduplicated sequence. =$i++ if $n>1, otherwise =0

Ruby, 75 65 bytes

->s,h{a=['']*h;x=-k=1;s.map{|c|a[x+=k=h-x<2?-1:x<1?1:k]+=c};a*''}

Try it online!

Takes input as an array of chars, returns string

How it wokrs:

  • Create h strings
  • For each character in the input string, decide which string to put it in based on its index (the index of the string to be modified goes up until h and then down until 0 and so on)
  • Return all the strings joined together

C, 142 134 bytes

8 bytes saved thanks to Jonathan Frech

Code:

t;i;j;d;f(s,n)char*s;{for(t=strlen(s),i=0;i<n;i++)for(j=0;j+i<t;j=d+i+(n<2))d=j-i+2*~-n,putchar(s[i+j]),i>0&i<n-1&d<t&&putchar(s[d]);}

Explanation:

// C variable and function declaration magic
t;i;j;d;f(s,n)char*s;{
    // Iterate through each "row" of the string
    for(t=strlen(s),i=0;i<n;i++)
        // Iterate through each element on the row
        // Original index iterator here was j+=2*(n-1), which is a full "zig-zag" forward
        // The (n<2) is for the edge case of n==1, which will break the existing logic.
        for(j=0; j+i<t; j=d+i+(n<2))
            // If j+i is the "zig", d is the "zag": Original index was d=j+i+2*(n-i-1)
            // Two's complement swag here courtesy of Jonathan Frech
            d=j-i+2*~-n,
            putchar(s[i+j]),
            // Short circuit logic to write the "zag" character for the middle rows
            i>0 & i<n-1 & d<t && putchar(s[d]);
}

Try it online!

Charcoal, 21 bytes

⭆NΦη¬⌊E²﹪⁺μ⎇νι±ι∨⊗⊖θ¹

Try it online! Link is to verbose version of code. Works by noting that indices \$ m \$ of the characters on line \$ i \$ satisfy the relation \$ m \pm i = 0 \pmod {2n - 2} \$. Explanation:

 N                      First input as a number
⭆                       Map over implicit range and join
   η                    Second input
  Φ                     Filter over characters
       ²                Literal 2
      E                 Map over implicit range
          μ             Character index
             ι ι        Outer index
              ±         Negate
            ν           Inner index
           ⎇            Ternary
         ⁺              Plus
                   θ    First input
                  ⊖     Decremented
                 ⊗      Doubled
                    ¹   Literal 1
                ∨       Logical Or
        ﹪               Modulo
     ⌊                  Minimum
    ¬                   Logical Not
                        Implicitly print

SNOBOL4 (CSNOBOL4), 191 bytes

	S =INPUT
	N =INPUT
	A =ARRAY(N)
	A<1> =EQ(N,1) S	:S(O)
I	I =I + -1 ^ D
	S LEN(1) . X REM . S	:F(O)
	A<I> =A<I> X
	D =EQ(I,N) 1
	D =EQ(I * D,1)	:(I)
O	Y =Y + 1
	O =O A<Y>	:S(O)
	OUTPUT =O
END

Try it online!

Takes S then N on separate lines.

Explanation:

	S =INPUT			;* read S
	N =INPUT			;* read N
	A =ARRAY(N)			;* create array of size N
	A<1> =EQ(N,1) S	:S(O)		;* if N = 1, set A<1> to S and jump to O
I	I =I + -1 ^ D			;* index into I by I + (-1)^D (D starts as '' == 0)
	S LEN(1) . X REM . S	:F(O)	;* extract the first character as X and set S to the
					;* remaining characters, jumping to O when S is empty
	A<I> =A<I> X			;* set A<I> to A<I> concatenated with X
	D =EQ(I,N) 1			;* if I == N, D=1
	D =EQ(I * D,1)	:(I)		;* if I == D == 1, D = 0. Goto I
O	Y =Y + 1			;* increment the counter
	O =O A<Y>	:S(O)		;* concatenate the array contents until last cell
	OUTPUT =O			;* and print
END

Clean, 105 84 bytes

import StdEnv,Data.List
$s n=[c\\l<-[1..n],c<-s&m<-cycle([1..n]++[n-1,n-2..2])|m==l]

Try it online!

JavaScript (Node.js), 83 bytes

c=>g=(s,d=c*2-2,r=d,n=o='')=>s&&([...s].map((c,i)=>i%d%r?n+=c:o+=c),o)+g(n,r-1,r-2)

Try it online!

Pyth, 22 21 bytes

|seMhD,V*lz+PUQP_UQzz

Takes input as n followed by s on separate lines. Try it online here, or verify all the test cases at once here.

|seMhD,V*lz+PUQP_UQzz   Implicit: Q=eval(input()), z=remaining input

             UQ         Range [0-Q)
            P           All but last from the above
                         e.g. for Q=3, yields [0,1]
               P_UQ     All but last of reversed range
                         e.g. for Q=3, yields [2,1]
           +            Concatenate the previous two results
                          e.g. for Q=3, yields [0,1,2,1]
        *lz              Repeat len(z) times
      ,V           z    Vectorised pair the above with z, truncating longer to length of shorter
                          e.g. for Q=3, z=WATERMELON, yields:
                          [[0,'W'],[1,'A'],[2,'T'],[1,'E'],[0,'R'],[1,'M'],[2,'E'],[1,'L'],[0,'O'],[1,'N']]
    hD                  Sort the above by the first element
                          Note this is a stable sort, so relative ordering between equal keys is preserved
  eM                    Take the last element of each
 s                      Concatenate into string
                          Note that if n=1, the result of the above will be 0 (sum of empty array)
|                   z   If result of above is falsey, yield z instead

Edit: saved a byte by moving the empty check to the end of processing. Previous version: seMhD,V*lz|+PUQP_UQ]0z

Red, 153 bytes

func[s n][i: v: m: 1 b: collect[foreach c s[keep/only reduce[v i c]v: v + m
if all[n > 1(i: i + 1)%(n - 1)= 1][m: -1 * m]]]foreach k sort b[prin last k]]

Try it online!

Explanation:

f: func [ s n ] [                      ; s is the string, n is the height
    i: 1                               ; index of the current character in the string
    v: 1                               ; value of the "ladder"
    m: 1                               ; step (1 or -1)
    b: collect [                       ; collect the values in a block b
        foreach c s [                  ; foreach character in the string 
            keep/only reduce [ v i c ] ; keep a block of the evaluated [value index char] 
            i: i + 1                   ; increase the index
            v: v + m                   ; calculate the value 
            if all [ n > 1             ; if height is greater than 1 and
                    i % (n - 1) = 1    ; we are at a pick/bottom of the ladder
                   ]
                [ m: -1 * m ]          ; reverse the step
        ]
    ]
    foreach k sort b [ prin last k ]   ; print the characters in the sorted block of blocks
]

I have two solutions to the problem. The first solution I did first then I thought of another way to do it that I thought would save bytes but it didn't so I included it anyway.


Solution 1

PHP, 152 144 116 bytes

<?php
for($i=0;$i<$n=$argv[2];$i++)
    for($j=$i;$s=$argv[1][$j];$j+=$n<2|(($f=!$f|!$i)?$i<$n-1?$n+~$i:$i:$i)*2)
        echo $s;
  • 8 bytes thanks to @JoKing
  • 28 bytes thanks to @Shaggy

Try it online!


Solution 2

PHP, 162 bytes

<?php
$s=$argv[0];
$n=$argv[1];
$l=strlen($s);
for($i=0;$i<$l;){
    for($j=0;$j<$n&&$i<$l;)
        $a[$j++].=$s[$i++];
    for($j=$n-2;$j>0&&$i<$l;)
        $a[$j--].=$s[$i++];
}
echo join($a);

Try it online!

  • You don't need to initialise $f and $n-1-$i can be $n-~$i. 144 bytes – Jo King Oct 17 at 3:06
  • -28 bytes on @JoKing's improvements. – Shaggy Oct 17 at 11:37
  • Oop; that breaks when n=1. This one works for the same byte count. – Shaggy Oct 17 at 12:03
  • You can also use short tags and remove the space after echo to save 5 more bytes – Shaggy Oct 17 at 21:22

Ruby, 84 bytes

->s,n{r=['']*n;[*0...n,*(2-n)..-1].map{|x|r[x.abs]+=s.slice!(0)||''}while s[0];r*''}

Try it online!

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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