30
\$\begingroup\$

Task

Given a string as input, generate a "blurred" version of that string.

Input

Your input will be a single-line string of ASCII characters, containing a minimum of 2 alphanumeric characters

Output

A string that is the "blurred" version of the input string.

A blurred string is one where every alphanumeric character from the original string has been paired with the ones adjacent to it, and each pair is separated by a space.

Any non-alphanumeric characters (whitespace, puncuation) in the input string must be ignored when determining adjacency of alphanumeric characters, and they must not included in the blurred string.

There must be no leading or trailing whitespace in the output string.

Examples

Ab -> Ab
Abc -> Ab bc
Abcd -> Ab bc cd
E?h? -> Eh
Blurry vision -> Bl lu ur rr ry yv vi is si io on
We're #1! -> We er re e1
I'm an example! -> Im ma an ne ex xa am mp pl le
This is _not_ legible -> Th hi is si is sn no ot tl le eg gi ib bl le
(a*b*c)+5^-x -> ab bc c5 5x
??a_%,1!=z#@ -> a1 1z


This is , so fewest bytes wins!

\$\endgroup\$
12
  • 2
    \$\begingroup\$ The non-alphanumeric characters have to be ignored or can be ignored? \$\endgroup\$
    – RGS
    Apr 24, 2020 at 21:39
  • 1
    \$\begingroup\$ @RGS Thanks for the clarification question, non-alphanumeric characters must be ignored \$\endgroup\$ Apr 24, 2020 at 21:42
  • 46
    \$\begingroup\$ I feel like this challenge suffers from a lot of poorly motivated add-ons. For example removing certain characters from the string, or requiring no trailing whitespace. These additions don't really have a reason to exist or an internal logic, they just get in the way of writing an answer. I started to write an answer but found these extra conditions inflated my code by nearly three times, making them actually the bulk of the challenge. \$\endgroup\$
    – Wheat Wizard
    Apr 24, 2020 at 23:18
  • 5
    \$\begingroup\$ Here are some posts 1 2 where you can read more about why I disagree with these extraneous elements. \$\endgroup\$
    – Wheat Wizard
    Apr 24, 2020 at 23:21
  • 9
    \$\begingroup\$ Why no trailing whitespace? Why do we have to ignore non-alphanumeric characters? These just seem like a lot of extra requirements that make the challenge less fun. \$\endgroup\$
    – S.S. Anne
    Apr 25, 2020 at 20:15

61 Answers 61

25
\$\begingroup\$

Python 3, 57 55 bytes

lambda s:"".join((c+" "+c)*c.isalnum()for c in s)[2:-2]

Try it online!

How:

  • For each alpha-numeric character c in the string, replace it with c+" "+c.
    E.g: "abcd" -> "a ab bc cd d"

  • Remove the redundant first and last 2 characters:
    E.g: "a ab bc cd d" -> "ab bc cd"

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I like this answer because it's elegant, and, esoterically, very useful. I might use this if I, for some reason, decide to add bluriness (how do you even type that?) to some program. \$\endgroup\$
    – mazunki
    Apr 25, 2020 at 20:41
  • 10
    \$\begingroup\$ @mazunki bl lu ur rr ri in ne es ss \$\endgroup\$
    – S.S. Anne
    Apr 26, 2020 at 2:15
13
\$\begingroup\$

Husk, 5 bytes

wX2f□

Try it online!

Explanation

   f   Keep all items that
    □  Is an alphanumeric character.
 X     Pick all sublists
  2    With a length of 2.
w      Join the output list by spaces.
\$\endgroup\$
8
\$\begingroup\$

[sed] -E (C locale), 46 33 23 bytes

s/\W|_//g;s/\B.\B/& &/g

Try it online!

Thanks to pizzapants184 for a 13-byte improvement.

10 more bytes off thanks to Dom Hastings.


Input on stdin, and output on stdout.

If your locale isn't set to C, you can set it with: export LC_ALL=C

\$\endgroup\$
5
  • 1
    \$\begingroup\$ I think you can replace s/\B(.)\B/\1\1 /g;s/(.) / \1/g with s/\B(.)\B/\1 \1/g for 33 bytes \$\endgroup\$ Apr 25, 2020 at 5:28
  • \$\begingroup\$ @pizzapants184 Thank you -- I thought I had tried that :) . \$\endgroup\$ Apr 25, 2020 at 5:51
  • \$\begingroup\$ You can drop this to 23 bytes with a couple of minor tweaks: Try it online! \$\endgroup\$ May 10, 2020 at 13:38
  • \$\begingroup\$ It's almost a polyglot with Perl too, if the replacement string is $& $& instead of & &! \$\endgroup\$ May 10, 2020 at 13:39
  • 1
    \$\begingroup\$ @DomHastings Thank you! \$\endgroup\$ May 10, 2020 at 20:29
8
\$\begingroup\$

K (ngn/k), 27 24 22 21 bytes

" "/2':(2!"/9@Z`z"')_

Try it online!

"/9@Z`z"' binary search in the given string

2! mod 2

(2!"/9@Z`z"')_ drop non-alphanumeric chars

2': make a list of pairs of adjacent chars

" "/ join with spaces

\$\endgroup\$
7
\$\begingroup\$

Pyth, 14 bytes

jd.::Q"\W|_"k2

Try it online!

  • :Q"\W|_"k replaces each non-alphanumeric character of the input with the empty string by matching each character against the regex \W|_

  • .: --- 2 finds all substrings of length 2

  • jd joins the substrings using spaces

\$\endgroup\$
7
\$\begingroup\$

Jelly, 6 bytes

fØB;ƝK

A monadic Link accepting a list of characters which yields a list of characters.

Try it online!

How?

fØB;ƝK - Link: list of characters, S
 ØB    - base-62 characters = "01...89AB...YZab...yz"
f      - (S) filter keep if in (that)
    Ɲ  - for neighbours:
   ;   -   concatenate
     K - join with spaces
\$\endgroup\$
7
\$\begingroup\$

05AB1E, 8 7 8 bytes

-1 byte thanks to @CommandMaster

+1 byte again thanks to @KevinCruijssen finding an issue

žKÃüJðý?

Try it online!

\$\endgroup\$
3
  • 1
    \$\begingroup\$ 7 bytes: žKÃüJðý \$\endgroup\$ Apr 26, 2020 at 5:45
  • 1
    \$\begingroup\$ You forgot a trailing ?, since the challenge disallows a trailing newline (for whatever stupid reason..) \$\endgroup\$ Oct 18, 2022 at 13:58
  • \$\begingroup\$ Solid gravedigging @KevinCruijssen, I've updated \$\endgroup\$ Nov 10, 2022 at 10:46
5
\$\begingroup\$

MATL, 17 16 bytes

t8Y2m)2YC!Z{0&Zc

Try it online! Or verify all test cases.

Explanation

Consider input 'Blurry vision'.

t      % Implicit input. Duplicate
       % STACK: 'Blurry vision', 'Blurry vision'
8Y2    % Push '012...9ABC...Zabc...z' (predefined literal)
       % STACK: 'Blurry vision', 'Blurry vision', '012...9ABC...Zabc...z'
m      % Ismember: true for chars of the first string that are in the second
       % STACK: 'Blurry vision', [1 1 1 1 1 1 0 1 1 1 1 1 1]
)      % Use as logical index. This keeps only letters and numbers in the input
       % STACK: 'Blurryvision'
2YC    % Character matrix with sliding blocks of length 2 as columns
       % STACK: ['Blurryvisio';
                 'lurryvision']
!      % Transpose
       % STACK: ['Bl';
                 'lu';
                 ...
                 'on']
Z{     % Cell array of matrix rows
       % STACK: {'Bl' 'lu ... 'on'}
0&Zc   % Join with character 0 (which will be displayed as space)
       % STACK: 'Bl lu ur rr ry yv vi is si io on'
       % Implicit display
\$\endgroup\$
5
  • \$\begingroup\$ This doesn't seem to properly handle input strings that contain numbers \$\endgroup\$ Apr 25, 2020 at 0:30
  • 1
    \$\begingroup\$ @mathjunkie Thanks for pointing that out. I have corrected it \$\endgroup\$
    – Luis Mendo
    Apr 25, 2020 at 1:50
  • \$\begingroup\$ Save a byte: "@32v! \$\endgroup\$
    – Sanchises
    Apr 25, 2020 at 11:47
  • \$\begingroup\$ @Sanchises Thanks! But that produces a trailing space, which is not allowed \$\endgroup\$
    – Luis Mendo
    Apr 25, 2020 at 11:54
  • 1
    \$\begingroup\$ Oh yes overlooked the overly restrictive spec \$\endgroup\$
    – Sanchises
    Apr 25, 2020 at 16:52
5
\$\begingroup\$

TEX (INITEX), 372 bytes

\catcode`~13\let~\catcode~`{1~`}2~`#6~`\ 12~`|13\let|\expandafter\def\z{}\def\f#1{#1}\def\i#1{}\def\b#1#2{\ifx#2\e|||\i|\i\else|\f\fi{\ifcat#1a|\f\else|||\b|||#2|\i\fi}{\ifcat#2a #1#2|\b|#2\else|\b|#1\fi}}\def\t{~`011~`111~`211~`311~`411~`511~`611~`711~`811~`911~`\|12~`\~12~`\#12~`\{12~`\}12~127=12~`\%12~`\\12\read16to\x\edef\r{|\b\x\e}\immediate\write16{|\i\r\z}\end}\t

For example, the program being saved in a file named "codegolf.tex", provided with input ??a_%,1!=\z#@, initex codegolf.tex outputs:

This is TeX, Version 3.14159265 (TeX Live 2019/dev/Debian) (INITEX)
(./codegolf.tex
\x=??a_%,1!=\z#@
a1 1z
 )
No pages of output.
Transcript written on codegolf.log.

In the case of a degraded input, for example the strings ??, ?a, or a?, this implementation does not print any pair.

Explanation

Basis for a TEX program

\read16

Per this Meta answer, the input of a program written in TEX should come from standard input, which is the in register number 16.

\immediate\write16

The TEX Codegolf community prefers to output to DVI or PDF, because it is simpler than writing to the terminal. However, this requires a valid \output routine, which either requires a format (with more category codes to alter1) or is longer to define. So I write to the terminal. (\message could also have been used, but I don't like how the output is surrounded by TEX chat on the same line).

Because I output to the terminal, I also feel that no DVI file should get produced, so \immediate is required2.

\end

This is a complete program, so it must end with \end to run cleanly.

Category codes

Running on alphanumerics and ignoring punctuation is a trivial matter for TEX. All I have to do is to customize the category codes of those characters.

For this subject, I will use category code 11 ("letter") for characters that need to be blurred, and category code 12 ("other") for characters that are to be dropped. That choice comes from the defaults of (ini)TEX, where letters are already set to 11 and most of the other characters to 12.

\catcode`~13\let~\catcode

To save bytes, I make ~ an active character and set its meaning to \catcode. In the rest of the explanation, that alias will be ungolfed.

\catcode`{=1 \catcode`}=2 \catcode`#=6 \catcode`\ =12

These are required for INITEX to interpret my program correctly: { is now the begin-group character, } the end-group, # the macro argument, and is considered a normal character (so that the first spurious space of output can be removed). These choices are widely used in the TEX world and do not cost bytes3.

\catcode`|=13 \let|=\expandafter

This is an useful alias for later.

\def\t{\catcode`0=11 ... \end}
\t

This is a TEX design pattern that fools TEX's eyes. Everything that is written in the \todo macro definition will get a category code assigned, and this category code will not be reconsidered when TeX expands the macro. Most notably:

\catcode`\\=12

This makes the escape character \ a normal character when read from the user input.

\read16to\x \edef\r{|\b\x\e} \immediate\write16{|\i\r\z} \end

When reading the macro definition, TEX has decided that these were control words, and TEX will not reconsider this choice after the macro has been expanded, even if \ has lost its special meaning in the meantime.

\catcode127=12

The DEL character is contained in ASCII, so it should be valid input per the subject's rules. But in order to change its category code to 12, the DEL character has to be written, and the most efficient4 way to input it in TEX is to name it by its code point.

\catcode`0=11 ... \catcode`\\=12

Here are three groups of category code assignments: make the digits like letters, revert the changes made at the beginning of the program, and cancel other INITEX defaults (DEL, %, \, but not the new line because the subject emits the hypothesis:)

Your input will be a single-line string

The blurring command

Because I opted for terminal output, and because \write does not executes its argument (only expands it), I am forced to write the \blur macro as fully expandable.

\def\z{} \def\firstofone#1{#1} \def\ignore#1{}

As a prolog, I define three macros, which are commonly used in various fully-expandable design patterns. The first one is the empty macro, the second one is the identity macro, and the third one is the zero macro.

\def\blur#1#2{ ... }

The \blur macro is recursive5. It takes two characters at a time (because the rules say that the input is longer than two). Its definition is made of three parts.

\ifx#2\e \expandafter\expandafter \expandafter\ignore \expandafter\ignore
\else    \expandafter\firstofone
\fi

is the recursion condition: if the end-of-string marker (the undefined command \e6) is found, ignore the two other parts. (More precisely, drop the unused portion of the conditional \else...\fi at \expandafter level 0, then ignore the second part at \expandafter level 1, then ignore the third part at \expandafter level 2.) Otherwise, close the conditional and interpret the second part.

\ifcat#1a \expandafter\firstofone
\else \expandafter\expandafter \expandafter\blur
      \expandafter\expandafter \expandafter#2
      \expandafter             \ignore
\fi

handles the first character. If it is a letter or digit, go analysing a second character. Otherwise, skip the remaining of the conditional, ignore the third part, and call \blur recursively with #1 being dropped and #2 as the first character7 (an other one will get extracted from input).

\ifcat#2a ␣#1#2 \expandafter\blur \expandafter#2
\else \expandafter\blur \expandafter#1
\fi

handles the second character. If it is a letter or digit, output a space then the first part of the blur, then forget about #1 and call \blur recursively on #27. Otherwise, forget about it and call \blur recursively on #17.

Calling the blurring command on real input

\read16 to \x

will store the input into an (expandable) macro \x.

\expandafter\blur \x \e

This is how the \blur macro is called. Because \blur acts on tokens, the input contained in \x must be expanded first. The end-of-string marker follows.

\edef\result{\expandafter\blur \x \e}

The result of the expansion, including the whitespace at front, is stored into the \result macro.

\expandafter\ignore \result \z

Inside the \write16 command, the \result macro is first expanded. Then, \ignore eliminates the first token of output: the first space if the output is non-empty, or the \z macro call if the output is empty. In the first case, the \z macro will survive; but \write16 will finally expand it, and its expansion is a no-op. This is the most efficient way of handling the “no trailing space” requirement, even if it requires 24 bytes (the definition and call of \z and \return); suppressing a space at the end of the output would have been nearly impossible.

☡ Footnotes

  1. $, &, ^, _, NUL, SPC, TAB, ~ for plain TEX; 5 bytes each = 40 bytes.

  2. Without \immediate, \write adds its content to a whatsit box, thus generating a page of output.

  3. Because there are only three category code changes, it is not worth putting them in a group to save the reverting.

  4. Decimal: 127= (4); Hexadecimal: "7F= (4, harder to read); Octal: '199= (5); Escape sequence: ``\^^? (5, plus \catcode`^7 and revert)

  5. It is even tail-recursive, which in TEX has the meaning of "it does not add tokens after the recursive call", and by doing so is more memory-friendly.

  6. Because all input other than my program does not get interpreted, the \e command cannot be defined later.

  7. This would fail if #2 was a simple group of arguments, but fortunately the input cannot contain simple groups, because no character has category code 1 nor 2.

\$\endgroup\$
4
\$\begingroup\$

Retina, 13 bytes

\W|_

Lw| `..

Try it online!

Explanation

\W|_ Replace each character NOT in the regex group \W (which is A-Z,a-z,0-9,_) or a _ with nothing

Lw| `.. Compute lists (L) for each set of two characters (..) starting at all positions in the string (w) and separate the lists with a space (| )

\$\endgroup\$
3
  • 1
    \$\begingroup\$ $& is the default output for L so no need to use $. (But I didn't know you could omit the ' after L|.) \$\endgroup\$
    – Neil
    Apr 24, 2020 at 22:57
  • \$\begingroup\$ @Neil Thanks for the tip - first Retina answer so still learning and madly skimming the documentation! \$\endgroup\$
    – Jarmex
    Apr 24, 2020 at 23:26
  • \$\begingroup\$ (Technically w allows the match to start and end at all positions in the string while v only allows the longest possible matches starting at all positions. Of course it makes no difference in a case like this where there is only one possible match per position.) \$\endgroup\$
    – Neil
    Apr 25, 2020 at 9:23
4
\$\begingroup\$

C# (Visual C# Interactive Compiler), 74 bytes

This ended up very similar to the Python answer. I was trying something noticeably more interesting with Aggregate, but the terrible no trailing whitespace requirement made it too long.

s=>s.SelectMany(c=>char.IsLetterOrDigit(c)?c+" "+c:"").Skip(2).SkipLast(2)

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ This is really great - I love seeing C# answers. I originally thought of flattening the list with SelectMany, but decided not to. I ended up utilizing the c+" "+c pattern as well. I need to utilize SkipLast! \$\endgroup\$
    – KaleSurfer
    Apr 26, 2020 at 1:21
  • \$\begingroup\$ I'm also realizing that char is way more powerful than I originally thought. There's no need to do the String -> char -> String \$\endgroup\$
    – KaleSurfer
    Apr 26, 2020 at 1:26
4
\$\begingroup\$

Kotlin, 77 76 bytes

{it.filter{it.isLetterOrDigit()}.zipWithNext{a,b->"$a$b"}.joinToString(" ")}

Old solution

{it.replace("\\W|_".toRegex(),"").zipWithNext{a,b->"$a$b"}.joinToString(" ")}
\$\endgroup\$
4
\$\begingroup\$

AWK, 52 54 bytes

{gsub("\\W|_",a);for(b=$0;c=substr(b,++d,2);)$d=c}--NF

Try it online!

Fixed underscore rule goof (+2 chars), thanks to Pedro Maimere

Here's what's going on with this one...

gsub("\\W",a)

Removes all the non-alphabetic characters from the input string since $0 is the default target for gsub. Using an unitialized variable a instead of "" saves a byte.

Next the code loops through the string taking 2 character slices and assigns them to positional variables.

for(b=$0;

The setup has to save $0 to another variable, since changing the positional variables impacts the collected input in $0 as well.

c=substr(b,++d,2);

The "test" at the top of the loop does a couple of things in one statement:

  • increments d, the "current character" pointer
  • sets variable c to the next 2 character slices of the input
  • the true/false test simple has the code looping until we run off the string and get a null

The body of the loop sets the next positional variable to the slice we just took.

$d=c

After all the positional variables have been set, there's one "garbage" entry on the end with one character. So the test:

--NF

effectively drops that last positional variable and triggers the default action, which prints all the positional variables separated by a space.

\$\endgroup\$
3
  • 2
    \$\begingroup\$ Great explanation, again. \$\endgroup\$ Mar 2, 2021 at 10:20
  • 1
    \$\begingroup\$ Nice piece of code! According to the question's examples, underscores (_) are meant to be erased. Unfortunately, gawk's \W is the same as [^[:alnum:]_], so the underscores are not erased. Thus, the regex would become "\\W|_". \$\endgroup\$ Jun 5, 2021 at 0:54
  • 1
    \$\begingroup\$ Nice catch! I'll update the code to add the underscore. \$\endgroup\$
    – cnamejj
    Jun 5, 2021 at 1:05
4
\$\begingroup\$

Factor, 51 37 bytes

-14 bytes thanks to chunes

[ [ alpha? ] filter 2 clump " "join ]

Try it online!

Factor, 51 bytes

: b ( s -- s ) [ alpha? ] filter 2 clump " " join ;

Try it online!

Unbelievably Factor is competitive with Python and JavaScript :)

\$\endgroup\$
2
  • \$\begingroup\$ 37 bytes \$\endgroup\$
    – chunes
    Oct 16, 2022 at 8:22
  • \$\begingroup\$ @chunes Thanks! I have several more answers formatted this way, as a function. I didn't know then that a quotation is acceptible. \$\endgroup\$ Oct 17, 2022 at 8:38
3
\$\begingroup\$

Retina 0.8.2, 17 16 bytes

\W|_

M&!`..
¶
 

Try it online! Link includes test cases. Explanation: Now basically a port of @Jarmex's Retina 1 solution, except that M! always joins with newlines, so I have explicitly change them to spaces. Previous 17-byte approach:

\W|_

\B.\B
$& $&

Try it online! Link includes test cases. Explanation:

\W|_

Delete any non-word character and any underscore (which is the only non-alphanumeric character that counts as a word character).

\B.\B
$& $&

Duplicate each inner character and space separate the results.

\$\endgroup\$
2
  • \$\begingroup\$ Another 17-byte solution, slightly different approach: Try it online! \$\endgroup\$ Apr 24, 2020 at 23:19
  • 1
    \$\begingroup\$ @mathjunkie Actually I was just working on that approach myself, although I had it as 16 bytes because I used a pilcrow instead of \n. \$\endgroup\$
    – Neil
    Apr 24, 2020 at 23:28
3
\$\begingroup\$

JavaScript (ES6), 57 bytes

s=>s.replace(/\W|_|(.)/g,(_,c)=>c?c+' '+c:'').slice(2,-2)

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ The approach looks promising so far. \$\endgroup\$
    – Robert
    Apr 25, 2020 at 23:54
3
\$\begingroup\$

Q/KDB+, 38 bytes

Solution:

{" "sv -2_2#'next\[x inter .Q.an _52]}

Examples:

q){" "sv -2_2#'next\[x inter .Q.an _52]}"Ab"
"Ab"
q){" "sv -2_2#'next\[x inter .Q.an _52]}"Abc"
"Ab bc"
q){" "sv -2_2#'next\[x inter .Q.an _52]}"E?h?"
"Eh"
q){" "sv -2_2#'next\[x inter .Q.an _52]}"This is _not_ legible"
"Th hi is si is sn no ot tl le eg gi ib bl le"

Explanation:

{" "sv -2_2#'next\[x inter .Q.an _52]} / solution
{                                    } / lambda taking implicit x
                           .Q.an       / "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"
                                 _52   / drop element at index 52
                   x inter             / intersection of x and alphanumerics
             next\[                 ]  / scan along input
          2#'                          / take first 2 characters of each
       -2_                             / drop final two items
 " "sv                                 / join (sv) with " "
\$\endgroup\$
3
\$\begingroup\$

APL (Dyalog Unicode), 32 bytes (SBCS)

Port of Surculose Sputum's Python answer.

{¯2↓2↓⊃,/{⍵' '⍵}¨⍵∩⎕A,819⌶⎕A,⎕D}

Try it online!

\$\endgroup\$
3
\$\begingroup\$

QuadR, 18 bytes

1↓∊' ',¨2,/⍵
\W|_

Try it online!

Replaces all non-word characters and underscores (\W|_) with nothing , and then:

2,/⍵ adjacent pairs

' ',¨ prepend a space to each pair

ϵnlist (flatten)

1↓ drop the first space

\$\endgroup\$
3
\$\begingroup\$

C (gcc), 80 bytes

p,b;f(char*s){for(p=b=0;*s;s++)isalnum(*s)?p&&printf(" %c%c"+!b--,p,*s),p=*s:0;}

Try it online!

\$\endgroup\$
3
\$\begingroup\$

05AB1E, 23 19 18 bytes

žKég<F®Nè?®N>è?ð?

Try it online!

This probably could be really shorter, but I just wasn't able to find the right tools for the job. -4 bytes thanks to petStorm and a further byte thanks to Command Master.

Old Answer Explained

žj'_ммм©g<F®Nè?®N>è?' ?
žj                      # Push [a-zA-Z0-9_]
  '_м                   # Remove the "_" from the above string
     м                  # Remove all alphanum characters from the input, leaving non-alphanum chars
      м                 # Remove those non-alphanum chars from the input, leaving alphanum chars
       ©                # Put this string into the register
        g<F             # For N in range(0, len(input) - 1):
           ®Nè?         #   Index the string at position N and print
               ®N>è?    #   Index the string at position N + 1 and print
                    ' ? #   Print a space
\$\endgroup\$
2
  • \$\begingroup\$ 19 bytes. \$\endgroup\$
    – user92069
    Apr 25, 2020 at 3:31
  • \$\begingroup\$ 18 bytes \$\endgroup\$ Apr 26, 2020 at 6:24
3
\$\begingroup\$

Javascript ES6, 55 54 chars

s=>s.replace(/\W|_/g,"").replace(/.(?=(.).)/g,"$&$1 ")

Test:

f=s=>s.replace(/\W|_/g,"").replace(/.(?=(.).)/g,"$&$1 ")

console.log(`Ab -> Ab
Abc -> Ab bc
Abcd -> Ab bc cd
E?h? -> Eh
Blurry vision -> Bl lu ur rr ry yv vi is si io on
We're #1! -> We er re e1
I'm an example! -> Im ma an ne ex xa am mp pl le
This is _not_ legible -> Th hi is si is sn no ot tl le eg gi ib bl le
(a*b*c)+5^-x -> ab bc c5 5x
??a_%,1!=z#@ -> a1 1z`.split`
`.map(s=>s.split` -> `).map(([s,k])=>f(s)==k).every(x=>x))

\$\endgroup\$
1
  • \$\begingroup\$ @tsh, yep, nice. I removed +, but forgot about braces. \$\endgroup\$
    – Qwertiy
    Apr 28, 2020 at 7:42
3
\$\begingroup\$

J, 32 bytes

[:}:[:,/2,&' '\]-.-.&AlphaNum_j_

Try it online!

               -.-.&AlphaNum_j_   remove non-alphanumeric
       2,&' '\                    append space to each pair of adjacent chars 
     ,/                           join 
 }:                               remove trailing space
\$\endgroup\$
1
  • \$\begingroup\$ Nice answer, I think [:}:@,2,&' '\]-.-.&AlphaNum_j_ works for -2. \$\endgroup\$
    – Jonah
    Mar 7 at 1:41
3
\$\begingroup\$

R, 98 bytes

function(x,y=grep("[a-z0-9]",el(strsplit(x,"")),T,,T))for(i in seq(y)[-1])cat(y[i-1:0],' ',sep='')

Try it online!

Unusually for R, a for loop is significantly shorter than a matrix-based approach, mainly because it allows us to just use cat instead of the much-more-verbose Reduce(paste vectorized way to build a text string from a list of characters.

\$\endgroup\$
2
  • \$\begingroup\$ 78 bytes \$\endgroup\$
    – Giuseppe
    Oct 17, 2022 at 17:09
  • \$\begingroup\$ @Giuseppe Much better than mine: please post it yourself. \$\endgroup\$ Oct 18, 2022 at 11:12
3
\$\begingroup\$

R, 76 bytes

function(s)cat(substring(S<-gsub("[^a-z0-9]","",s,T),N<-1:(nchar(S)-1),N+1))

Try it online!

Similar to Dominic van Essen's answer, uses cat to join by spaces; uses substring which recycles (as opposed to substr which doesn't).

\$\endgroup\$
3
\$\begingroup\$

Perl 5 -p, 25 bytes

s/\W|_//g;s/\B.\B/$& $&/g

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ Nice! I'm surprised I didn't post this, given the sed answer! You can also save a byte using s/\Pl//g too! \$\endgroup\$ Mar 7 at 9:18
2
\$\begingroup\$

Charcoal, 26 bytes

≔ΦS№⁺α⁺β⭆χλιθ⪫E⊖Lθ✂θι⁺²ι¹ 

Try it online! Link is to verbose version of code. Explanation:

≔ΦS№⁺α⁺β⭆χλιθ

Filter out any character that can't be found in the upper or lower case alphabet and isn't a digit.

⪫E⊖Lθ✂θι⁺²ι¹ 

Extract all substrings of length 2 and join them together on spaces.

\$\endgroup\$
2
\$\begingroup\$

Icon, 92 bytes

procedure f(s)
t:=""
find(k:=!s,&letters++&digits--'_')&t||:=k||' '||k&\z
return t[3:-2]
end

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Red, 130 bytes

func[s][a: charset[#"0"-#"9"#"A"-#"Z"#"a"-#"z"]parse s[any[p: change
a(rejoin[p/1" "p/1])| remove skip]]take/part/last s 2 at s 3]

Try it online!

\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6), 54 76 bytes

a=>[...a.replace(/[\W_-]/g,'')].map((a,b,c)=>a+c[b+1]).slice(0,-1).join(' ')

Try it online!

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Hello and welcome to CGCC! Looks like this code isn't ignoring special characters as the spec requires (eg. ??a_%,1!=z#@ should become a1 1z). Hopefully it's an easy fix :) \$\endgroup\$ Apr 25, 2020 at 20:34
  • \$\begingroup\$ @mathjunkie Sorry, completely missed the comment saying that non-alphanumeric characters must be removed, it's fixed but it makes my answer a lot less competitive. \$\endgroup\$ Apr 25, 2020 at 20:55
  • \$\begingroup\$ You can save 2 bytes with .join` ` \$\endgroup\$
    – Ectogen
    Aug 9, 2021 at 7:06

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.