Springify a String

11

1

Sandbox post here.

Create a function or program that "Springifies" a string.

  • Input will be a String in Stdin, or closest alternative
  • Input will only contain printable ASCII and/or spaces
  • Output will be to Stdout, or closest alternative
  • A trailing newlines and spaces are acceptable

How to springify a String

  1. Format the String into as many ASCII spring coils as needed
  2. Pad the coils with spaces, up to the nearest coil
  3. Read off the characters, following the spring around the coils

This is an ASCII spring coil:

#
# ####
 #    #
# ####
#

Where the #s are the characters of the String

Here is an example:

abcdefghijklmnopqrstuvwxyz

becomes

a
b cdef
 g    h
i jklm
n
o
p qrst
 u    v
w xyz.
.

Where the .s replace spaces for visibility.

Then, the string is read back, following the ASCII spring downwards, around the loops, hitting the g and u twice:

1| a  <-3
 V b cdef
    g    h
4| i jklm
 V n  2->
  ...

...giving:

abgjklmhfedcginopuxyz vtsrquw (with a trailing space)

Test Cases

(quotations added to highlight trailing spaces - please ignore in terms of IO)

I: "abcdefghijklmnopqrstuvwxyz"
O: "abgjklmhfedcginopuxyz vtsrquw "

I: "!@#"
O: "!@         #   "

I: ""
O: ""

I: "12345 67890"
O: "12690  7 54368 "

I: " "
O: "               "

Note that the output length is always a multiple of 15, the length of a spring coil

This is , so the shortest answer in bytes wins.

MildlyMilquetoast

Posted 2017-01-20T01:38:52.147

Reputation: 2 907

I feel like it would've been much more of a challenge to follow the pattern along the coil. – Magic Octopus Urn – 2017-01-20T14:52:42.303

@carusocomputing you mean the inverse? – MildlyMilquetoast – 2017-01-20T16:47:30.160

http://codegolf.stackexchange.com/a/107531/59376 turns out I didn't understand the challenge as written, that's exactly the challenge ahaha. – Magic Octopus Urn – 2017-01-20T17:00:16.123

Answers

2

Jelly, 26 bytes

“4ṘƝ;þ¦Ɱ’b®¤ị
;⁶x14©¤s®ṖÇ€

TryItOnline!

How?

“4ṘƝ;þ¦Ɱ’b®¤ị - Link 1, output for a single coil: char array
           ¤  - nilad followed by link(s) as a nilad
“4ṘƝ;þ¦Ɱ’     - base 250 number, 13140239220751650
          ®   - retrieve from register (14 from main link)
         b    - convert to base, [1,2,7,10,11,12,13,8,6,5,4,3,7,9,0]
            ị - index into the char array

;⁶x14©¤s®ṖÇ€ - Main link: theString
      ¤      - nilad followed by link(s) as a nilad
 ⁶           - a space character
  x          - repeated
   14©       - place 14 into the register and yield 14
;            - concatenate theString with the 14 spaces
       s     - split into chunks of length
        ®    -     retrieve from register (14)
         Ṗ   - pop last entry from the result (removes the space only last entry of 14 chars or less)
          Ç€ - call the last link (1) as a monad for €ach
             - implicit print

Jonathan Allan

Posted 2017-01-20T01:38:52.147

Reputation: 67 804

5

Python 2, 104 102 98 Bytes

f=lambda t:''.join((t+' '*13)[ord(x)-97+y*14]for y in range(len(t)/14+1)for x in'abgjklmhfedcgin')

Thanks for the comment help!

https://tio.run/#2VDVy

Original:

t=raw_input()+' '*13
print''.join(t[ord(x)-97+y*14]for y in range(len(t)/14)for x in'abgjklmhfedcgin')

throx

Posted 2017-01-20T01:38:52.147

Reputation: 161

You can just use input() and take input in the format "<stuff>". – HyperNeutrino – 2017-01-20T03:24:35.583

Great first golf! As Alex L. points out, we allow input to be taken in quotes. You can also use a lambda to give an anonymous function rather than a program -- this is often shorter.

– xnor – 2017-01-20T03:28:36.600

Fails for the empty string test case with an IndexError. I think you may need to change 13 to 14 and pop off the final value (much like my Jelly answer). – Jonathan Allan – 2017-01-20T07:30:02.513

...changing the +13 to +14 and the +1 to +(len(t)%14>0) would do it, but there must be a shorter way. – Jonathan Allan – 2017-01-20T07:41:38.163

3

JavaScript (ES6), 79 bytes

f=
s=>s.replace(/.{1,14}/g,s=>'0169abc7543268d'.replace(/./g,c=>s['0x'+c-0]||' '))
<input oninput=o.textContent=f(this.value)><pre id=o>

Hexadecimal string shamelessly stolen from @ETHproductions.

Neil

Posted 2017-01-20T01:38:52.147

Reputation: 95 035

@ETHproductions Thanks, fixed. – Neil – 2017-01-20T17:04:53.593

That's brilliant! – Hristiyan Dodov – 2017-01-20T22:11:58.637

It's nice how the snippet lets you see the result as you type it – MildlyMilquetoast – 2017-01-22T19:26:13.367

2

JavaScript (ES7), 144 143 141 114 104 103 bytes

Thanks to ETHProductions for a 10B save!

a=>[...b=a+' '.repeat(15-a.length%15)].map((_,c)=>b['0x'+'0169abc7543268d'[c%15]-0+(c/15|0)*14]).join``

Example

f=a=>[...b=a+' '.repeat(15-a.length%15)].map((_,c)=>b['0x'+'0169abc7543268d'[c%15]-0+(c/15|0)*14]).join``
f('abcdefghijklmnopqrstuvwxyz')

Output

abgjklmhfedcginopuxyz vtsrquw 

Luke

Posted 2017-01-20T01:38:52.147

Reputation: 4 675

Very nice. You can save some bytes by converting the array to a hexadecimal string, and using +('0x'+whatever) to convert it to a number: (_,c)=>b[(c/15|0)*14+ +('0x'+'0169abc7543268d'[c%15])] – ETHproductions – 2017-01-20T12:45:19.500

Clever trick. I was already thinking of other ways to write the array, but I couldn't come up with any. I can still shorten that, by placing the hexadecimal number first and then the multiplication, making the space needless. – Luke – 2017-01-20T14:22:19.763

You could even do '0x'+'...'[c%15]-0+(c/15|0)*14 to save yourself a pair of parentheses :-) – ETHproductions – 2017-01-20T14:32:52.167

You're right. Added. – Luke – 2017-01-20T14:40:02.813

2

Retina, 71 bytes

$
13$* 
M!`.{14}|$
(.)(.)(.)(.)(.)(.)(.)(....)(.)¶
$5$8$6$4$3$2$1$5$7$9

Try it online!

Permuting strings isn't exactly concise in Retina...

Martin Ender

Posted 2017-01-20T01:38:52.147

Reputation: 184 808

2

Perl 6, 61 bytes

{S:g/(.)**1..14/{[~] $0["abgjklmhfedcgin".ords X-97]X//" "}/}

How it works

The basic structure is this:

{                                  }  # A lambda.
 S:g/(.)**1..14/{                }/   # Regex-replace segments of 1-14 characters, with:
                     $0               #   The single-char submatches captured by the parens.
                       [  ]           #   Index them using certain indices (see below).
                           X//" "}    #   Replace each undefined element with a space.
                 [~]                  #   Concatenate the characters.

The expression used to index into each 14-characters segment is "abgjklmhfedcgin".ords X- 97, which works as follows:

  1. Take the hard-coded string abgjklmhfedcgin.
  2. Get its codepoints: 97 98 103 106 107 108 109 104 102 101 100 99 103 105 110.
  3. Subtract 97 from each number: 0 1 6 9 10 11 12 7 5 4 3 2 6 8 13.

Perl 6, 64 bytes

{[~] .comb(14)».comb»[0,1,6,9..12,7,5...2,6,8,13].flat X//" "}

Try it online!

How it works

The basic structure is this:

{                                    }  # A lambda.
     .comb(14)                          # Split the argument into substrings of <= 14 chars.
              ».comb                    # Split each substring into a list of characters.
                    »[  ]               # Index each list using the same indices (see below).
                         .flat          # Flatten the resulting nested list.
                               X//" "   # Replace each undefined element with a space.
 [~]                                    # Concatenate the list to get a string again.

Same indices as above, but since we're calling .flat anyway, we can use a nested (but 1 byte shorter) expression for them: 0,1,6,9..12,7,5...2,6,8,13.

(Looks trivial, but actually relies on a parsing peculiarity of Rakudo Perl 6 that is probably a bug. To write it without exploiting that bug, one would have to add parens around the 5...2.)

smls

Posted 2017-01-20T01:38:52.147

Reputation: 4 352

2

Befunge-93, 97 bytes

<>0>v%2g3\p89:-1+*"!"!:+1<_@#`0:~
,^\,_\:98g\9p1+:76+`#v_:~^
^,,g96g98g9+67,,,,,$$_
==    =  ====

Try it online!

This is a breakdown of the source code with the various component parts highlighted.

Source code with execution paths highlighted

* The main loop starts off executing right to left, wrapping around to the right hand side of the playfield. This is where we read the first character from stdin, and terminate if it's an EOF.
* The next section makes sure any EOF characters are converted to spaces using the formula c = c + 31*!(c+1). Although this won't apply on the first iteration, it can occur on subsequent passes.
* A copy of the character is saved to temporary memory, and then a lookup of the current index is performed on the table on line four (*) to determine whether the character should be output or not.
* If the character needs to be output, we take the left branch. A swap is performed here to cancel out the swap that's going to occur next, and then a zero is pushed to force the branch right.
* If the character wasn't output, we swap it down the stack beneath the index counter (this is the swap that gets cancelled in the left branch). And in both cases we save the character to memory at the current index offset, increment the index, and check if it's greater than 13.
* If not, we read the next character from stdin and repeat the inner loop.
* If it is, we'll have finished a set of 14 characters, 7 having been output (abgjklm), and 7 remaining on the stack (cdefhin). We drop the last two, output the remaining 5 (hfedc), and then retrieve and output the special cases g, i and n from memory.
* And that brings us back to the start of the main loop, where we repeat the process again for the next 14 characters.

James Holderness

Posted 2017-01-20T01:38:52.147

Reputation: 8 298

Nice explanation, well done – MildlyMilquetoast – 2017-01-22T19:26:32.217

1

Mathematica, 77 72 bytes

Thanks to JungHwan Min for saving 5 bytes!

Join@@Partition[#,14,14,{1,1}," "][[;;,LetterNumber@"abgjklmhfedcgin"]]&

Unnamed function taking a list of characters as input and returning a list of characters. Partition[#,14,14,{1,1}," "] splits the input into sublists of length 14, padding with spaces if necessary. LetterNumber@"abgjklmhfedcgin" evaluates to {1,2,7,10,11,12,13,8,6,5,4,3,7,9,14}, which indicates the order to take each length-14 sublist in (repeating the 7th element appropriately). Then [[;;,...]] takes the elements of all the length-14 sublists in that order, and Join@@ joins the answers together.

Previous submission:

Join@@Partition[#,14,14,{1,1}," "][[All,83224017339955102~IntegerDigits~16]]&

Greg Martin

Posted 2017-01-20T01:38:52.147

Reputation: 13 940

1LetterNumber@"abgjklmhfedcgin" is a shorter way to compress the list. Also ;; instead of All saves a byte. – JungHwan Min – 2017-01-20T04:33:07.843

Great suggestions! – Greg Martin – 2017-01-20T04:41:39.490

1

Python 3, 86 bytes

lambda s:''.join(t[i%14]for t in zip(*[iter(s+' '*13)]*14)for i in b'Tq>]zQ|1/X;:L$o')

Try it online!

As a bonus, the string Tq>]zQ|1/X;:L$o is a valid CJam program that prints the unique characters on standard input, sorted. Try it online! (+ explanation)

Lynn

Posted 2017-01-20T01:38:52.147

Reputation: 55 648

0

PHP, 94 bytes

foreach(str_split($argv[1],14)as$s)for($i=0;$c=abgjklmhfedcgin[$i++];)echo$s[ord($c)-97]??" ";

splits command line argument to 14-byte chunks and loops through the translation string as index for the sub-string. Run with php -nr '<code>' <string>.

I was glad to find out that ?? also accepts the empty string (for an "invalid" index) as null.

Titus

Posted 2017-01-20T01:38:52.147

Reputation: 13 814

0

Ruby, 83 bytes

->s{v="";(s+" "*13).scan(/.{14}/){|y|"0169:;<7543268=".chars{|o|v+=y[o.ord-48]}};v}

First idea was using hex numbers, but ord-48 saves another byte (stolen from throx's answer).

G B

Posted 2017-01-20T01:38:52.147

Reputation: 11 099