# Let's build a Staircase

We don't have enough (semi-)easy challenges for beginners. More and more of the easy ones are already taken. So I tried to come up with something that might be achievable by beginners, but that isn't a duplicate.

### Input:

A single string separated with your OS new-line (i.e. \r\n),
or an array with multiple strings.

### Output - The Stairs:

Remove all non-alphabetic and non-numeric symbols. So all that's left is [A-Za-z0-9]. And then 'build a stairs'; basically ordering them on length with the smallest at top and widest at the bottom.

### Challenge rules:

1. When two strings are of equal length, we merge them with each other as one big string (the order doesn't matter, so it could be from first to last or last to first, whichever of the two you prefer).
2. The rule above can stack when the merged strings are of equal length again (see test case 2).

### General rules:

• The input is STDIN and contains only ASCII characters. And the output is STDOUT.
• The case of the output must be the same as the input.
• Each submission must be a full program able to compile and run, so not just a method/function. EDIT: I'm rather new, so perhaps it's indeed better to use the default from now on, even though I prefer a full program myself. Sorry for everyone that has already posted a full program. Feel free to edit, and I'll try to not change the post mid-challenge next time.
• This is , so the shortest answer in bytes wins. I probably accept the shortest answer in a year from now.
Don't let code-golf answers discourage you from posting golfed non-codegolf languages like C# and alike! Try to come up with the shortest answer for any programming language.
• Feel free to use newer languages than this question.

### Test cases:

Input 1:

This is a sample text,
that you will have to use to build stairs.
The wood may be of excellent quality,
or pretty crappy almost falling apart and filled with termites.
Bla bla bla - some more text
Ok, that will do


Output 1:

Okthatwilldo
Thisisasampletext
Blablablasomemoretext
Thewoodmaybeofexcellentquality
thatyouwillhavetousetobuildstairs
orprettycrappyalmostfallingapartandfilledwithtermites


Input 2:

A
small
one
that
contains
equal
length
strings
for
the
special
rule


Output 2:

A                   Or alternatively:       A
length                                      length
oneforthe                                   theforone
smallequal                                  equalsmall
stringsspecial                              specialstrings
thatrulecontains                            containsrulethat


Steps explained of 2:

First order on length:

A
one
for
the
that
rule
small
equal
length
strings
special
contains


First merge:

A
oneforthe
thatrule
smallequal
length
stringsspecial
contains


Second order on length:

A
length
thatrule
contains
oneforthe
smallequal
stringsspecial


Second merge:

A
length
thatrulecontains
oneforthe
smallequal
stringsspecial


Third order on length:

A
length
oneforthe
smallequal
stringsspecial
thatrulecontains


Input 3:

Test,
test.
This
is
a
test.


Output 3:

a                   Or alternatively:       a
is                                          is
TesttestThistest                            testThistestTest


Input 4:

a
bc
d!
123


Output 4:

123     Or alternatively:    123

• contains isn't supposed to be in output 2. It gets merged with thatrule – Keatinge May 30 '16 at 10:49
• You pretty much got the exact opposite of what you wanted, it's pretty hard to do this. – Bálint May 30 '16 at 15:46
• "Feel free to use newer languages than this question" - So, if I create a language, just to solve this challenge in 0 bytes, that's technically legal, isn't it? – Bálint May 30 '16 at 18:12
• Was this challenge in the sandbox? – Bálint May 30 '16 at 18:13
• @nimi I personally indeed prefer a full program, but if you really insist I can remove it now and everyone can use the default.. I'm rather new, so perhaps it's indeed better to use the default from now on. Sorry for everyone that has already posted a full program. Feel free to edit, and I'll try to not chance the rules mid-challenge next time. – Kevin Cruijssen May 31 '16 at 6:49

## Husk, 11 bytes

ωȯmΣġLÖLmf□


Try it online!

Husk is younger than this challenge (which makes no difference officially, but still).

## Explanation

ωȯmΣġLÖLmf□  Implicit input (list of strings), say ["a","bc","d!","123"]
mf□  Keep only alphanumeric chars of each: ["a","bc","d","123"]
ωȯ           Repeat until fixed point is reached:
ÖL       Sort by length: ["a","d","bc","123"]
ġL         Group by length: [["a","d"],["bc"],["123"]]
Final result ["123","adbc"], print implicitly separated by newlines.

• When "keep only alphanumeric chars of each" is mf□, you ought to be jealous. When "group by length" is ġL, you ought to be amazed. – Erik the Outgolfer Aug 19 '17 at 11:07
• I've accepted your answer now. The new meta is that languages newer than the challenge can be used (and I've also already mentioned that in my challenge when I posted it). As I said before, nice answer! – Kevin Cruijssen Apr 20 '18 at 14:44

# Python 3, 264 Bytes

I'm not good at code golf so I'm confident this won't be the best Python 3 answer. This uses recursion and an ordered dict with all the words for each length.

from collections import*
def f(i):
d = defaultdict(list)
for l in i: x = "".join(c for c in l if c.isalnum());d[len(x)].append(x)
n = (sorted(["".join(d[z]) for z in d.keys()], key=len))
if n == i:return "\n".join(n)
return f(n)
print(f(eval(input())))


Takes input from stdin as a list, for example, test it with this list:

['A', 'small', 'one', 'that', 'contains', 'equal', 'length', 'strings', 'for', 'the', 'special', 'rule']


Will output:

A
length
oneforthe
smallequal
stringsspecial
thatrulecontains

• Great answer! A couple tips on golfing: 1) You don't need spaces around = signs, or == signs. 2) Python can detect keywords if it knows it can't be another variable name, like what you did with "import*" (ex. ")for", "return"\n""). 3) I'm pretty sure (not positive) that you don't need the parentheses around sorted(). Happy coding! – Blue May 30 '16 at 13:25
• you can use filter(str.isalnum, l) instead of the "".join part – njzk2 May 30 '16 at 17:59

# Retina, 69 63 bytes

[^\w¶]|_

{\b((.)+)¶((?.)+)\b(?(2)(?!))
$1$3
O$(.)+$#1$*  Try it online! • I think you can change the first line to [^\w¶]|_. Although I'm still not sure it is optimal. – FryAmTheEggman May 30 '16 at 14:16 # Oracle SQL 11.2, 346 bytes The lines in the input string are separated by '¤'. That way it is not necessary to create a table to use as the input. This is a sample text,¤that you will have to use to build stairs.¤The wood may be of excellent quality,¤or pretty crappy almost falling apart and filled with termites.¤Bla bla bla - some more text¤Ok, that will do A¤small¤one¤that¤contains¤equal¤length¤strings¤for¤the¤special¤rule Test,¤test.¤This¤is¤a¤test.¤  Query : WITH v AS(SELECT REGEXP_REPLACE(COLUMN_VALUE,'[^a-zA-Z0-9]')s FROM XMLTABLE(('"'||REPLACE(:1,'¤','","')||'"'))),r(s,i,l)AS(SELECT s,1,1 FROM v UNION ALL SELECT LISTAGG(s)WITHIN GROUP(ORDER BY s)OVER(PARTITION BY LENGTH(s)),ROW_NUMBER()OVER(PARTITION BY LENGTH(s)ORDER BY s),l+1 FROM r WHERE l<LENGTH(:1)AND i=1)SELECT s FROM r WHERE l=LENGTH(:1);  Un-golfed WITH v AS ( -- Splits on ¤ and keeps only alphanum characters SELECT REGEXP_REPLACE(COLUMN_VALUE,'[^a-zA-Z0-9]')s FROM XMLTABLE(('"'||REPLACE(:1,'¤','","')||'"')) ) -- Recursive view -- s : string -- i : index of the string in case of duplicates -- l : exit condition ,r(s,i,l)AS ( -- Start with every element of the input SELECT s,1,1 FROM v UNION ALL SELECT -- Concatenate elements of the same lengths LISTAGG(s)WITHIN GROUP(ORDER BY s)OVER(PARTITION BY LENGTH(s)) -- Index of elements of the same length (listagg with over generates duplicates) ,ROW_NUMBER()OVER(PARTITION BY LENGTH(s) ORDER BY s) -- exit condition ,l+1 FROM r WHERE l<LENGTH(:1) AND i=1 ) -- Keep only the elements from the last iteration (automaticaly sorted on my system) SELECT s FROM r WHERE l=LENGTH(:1)  • You can replace your regex with [\W_] – FliiFe May 30 '16 at 19:54 • @FliiFe it does not remove the ',' and '.' in the last test case – Jeto May 31 '16 at 1:46 • Weird... But you could still replace 0-9 with \d. Maybe the regex rules are different in sql than in python/php/javascript ? (js is still a special case because of lookbehinds) – FliiFe May 31 '16 at 15:02 ## Haskell, 129 bytes import Data.List import Data.Char l=length print.(foldl(const.map concat.groupBy((.l).(==).l).sortOn l)=<<(filter isAlphaNum<$>))


Accepts and prints an array of strings. If the result can be returned from the function (in contrast to printed to stdout), you can omit the print. and save 6 bytes.

How it works (note, I use x for the input parameter which of course does not appear in the pointfree version above):

 (    )=<<(     )          -- (f =<< g) x is f (g x) x, so we fold over x with a
-- starting value of:
filter isAlphaNum<$>x -- keep only alphanumeric chars in every line of x -- during folding, I ignore the the elements of x. -- However folding stops the repeatedly applied function -- after (length x) steps, which is enough for combining -- lines of equal length const -- ignore elements from x, deal only with start value sortOn l -- sort lines from shortest to longest groupBy((.l).(==).l) -- group lines of equal length map concat -- concatenate each group print -- print result after (length x) iterations  # Python 3, 184 180 bytes def f(x):l=len;m=filter;y=sorted([''.join(m(str.isalnum,i))for i in x],key=l);*z,=m(l,[''.join(i for i in y if-~j==l(i))for j in range(l(y[-1]))]);y==z and+print(*z,sep='\n')or f(z)  A function that takes input, by argument, as a list of strings and prints the result to STDOUT. Execution raises an error (due to the use of the + operator before the print statement), but not before the output has been printed. How it works def f(x): Function with input of list of strings l=len;m=filter Redefine much-used functions: len gives the length of an object and filter chooses those items from an iterable for which a function is true [''.join(m(str.isalnum,i))for i in x] Strip to leave only alphanumeric characters... y=sorted(...,key=l) ...and sort by length, into y ''.join(i for i in y if-~j==l(i)) Concatenate equal length strings... [...for j in range(l(y[-1]))] ...for all possible string lengths... *z,=(m(l,...)) ...and remove empty strings by filtering by length (the empty string has length 0, and is thus false), into z y==z and+print(*z,sep='\n')... If no change after concatenation, no more equal length strings exist, so print result to STDOUT... ...or f(z) ...else pass new list to function  Try it on Ideone # J, 48 bytes [:(/:#&>)[:(#&>,&.>//.])^:_(#~e.&AlphaNum_j_)&.>  Try it online! ## ungolfed [: (/: #&>) [: (#&> ,&.>//. ])^:_ (#~e.&AlphaNum_j_)&.>  ## explanation • (#~e.&AlphaNum_j_)&.> remove non alphanum • (#&> ,&.>//. ]) combine same length items • ^:_ keep combining until it stops changing • (/: #&>) sort by length # Javascript 198188186 179 bytes This is my second longest golfed javascript program s=>s.replace(/[^\w]|_/g,,l=0).split(/\s/g).sort(g=(a,b)=>a[m=length]-b[m]).reduce((a,b,c)=>a+(a.split(/\s/g)[c-1][m]<b[m]? : )+b).replace(/ /g,).split .sort(g).join   Probably can be golfed further • What do you use the t variable for? – gcampbell May 30 '16 at 16:16 • Ok so you can golf it by declaring y="split" and then instead of using .split() you can use [y]() – Bald Bantha May 30 '16 at 16:18 • @gcampbell That was just a leftover from testing – Bálint May 30 '16 at 16:27 • @BaldBantha I don't think that would make it shorter – Bálint May 30 '16 at 16:28 • @BaldBantha I did that with length though – Bálint May 30 '16 at 16:29 # Perl 5, 112 bytes @F=<>;while($n-@F){$n=@F;%k=();s/[^a-z0-9]//gi,$k{y///c}.=$_ for@F;@F=values%k}say$k{$_}for sort{$a<=>$b}keys%k  Try it online! # Jelly, 17 bytes f€ØWṖ¤L€Ġị⁸Ẏ€µÐLY  Try it online! Not sure why Ẏf¥€ØWṖ¤L€ĠịµÐLY doesn't work... Explanation: f€ØWṖ¤L€Ġị⁸Ẏ€µÐLY Full program µÐL Execute the following until we get a result a second time ¤ The following as a nilad ØW [A-Za-z0-9_] Ṗ Remove last element (_) f€ Filter the left argument (current result) with the above nilad € Left map L Length Ġ Group indices of same values, sort values ⁸ Left argument ị Index on ^^ and ^ € Left map Ẏ Concatenate elements Y Join on newlines (full program will display correctly)  # Pyth, 22 bytes jlDusM.glkG@Ls++GrG1UT  Try it here. Explanation: jlDusM.glkG@Ls++GrG1UT j join on newlines lD sort by length u run until duplicate result, return result (argument G, iteration number H) sM map concatenate elements .g group elements by function (argument k) l length k k G G @L left map filter on presence (extra argument first) s concatenate elements + concatenate two items + concatenate two items G G (default = lowercase alphabet) r 1 to uppercase G G U unary range [0..n) T T (default = 10)  # Pyth, 39 bytes Back to golfing ! There is the program : =Qm:d"[\W_]"kQKYLmsd.glkolNb;WnKQ=KQ=yQ;jQ  =Qm:d"[\W_]"kQLmsd.glkolNb;WnYQ=YQ=yQ;j  Test it here ! # Explanations =Qm:d"[\W_]"kQLmsd.glkolNb;WnYQ=YQ=yQ;j (Implicit) Assign evaluated imput to Q (In this case, an array) =Q Reassign a value to Q m Q map a function over Q :d"[\W_]"k Replace any special character with an empty string L ; Declare a function y(b) olNb Sort b by length .glk Group strings of same length in arrays msd Concat each inner array WnYQ ; While Y != Q (previous array is not equal to current array) =YQ Assign the current array to Y (Y=Q) =yQ Assign y(Q) to Q (Q=yQ). Here, the assigned variable name is implicit j Display the resulting array  • Try to use R and L instead of m – Leaky Nun May 30 '16 at 23:10 # Java 8, 268 bytes A void lambda accepting a mutable List<String> (i.e. implements add and remove; e.g. ArrayList). Output is printed to standard out, delimited by newline, with a trailing newline. Cast to Consumer<List<String>>. l->{int i=0,z;while(i<l.size())l.set(i,l.get(i++).replaceAll("\\W| ",""));while(l.size()>0){l.sort((s,t)->s.length()-t.length());String s=l.remove(0);for(i=0,z=s.length();l.size()>0&&l.get(0).length()==z;i++)s+=l.remove(0);if(i<1)System.out.println(s);else l.add(s);}}  Try It Online This ended up being much longer than I expected. As Kevin observed, it's more complicated than it seems at first glance. ## Ungolfed lambda l -> { int i = 0, z; while (i < l.size()) l.set(i, l.get(i++).replaceAll("\\W| ", "")); while (l.size() > 0) { l.sort((s, t) -> s.length() - t.length()); String s = l.remove(0); for ( i = 0, z = s.length(); l.size() > 0 && l.get(0).length() == z; i++ ) s += l.remove(0); if (i < 1) System.out.println(s); else l.add(s); } }  First, I reduce the input in place to letters and numbers. I then process the inputs in groups by length. I append items to the first in the list until the next length is reached, removing them as I go. If only the first element was used, that will be the only string of that length, so it's printed. Otherwise, the joined string is added to the list for another iteration. I sort the list by length each iteration before use. I started off with a lovely solution that used a priority queue to keep track of the intermediate strings. Unfortunately, java.util.PriorityQueue<String> is quite long (and using the raw type was longer), so it had to go. # Japt v2.0a1 -h, 11 bytes Input & output as arrays of strings. £=mk\W üÊmq  Try it £=mk\L üÊmq :Implicit input of string array U £ :Map m : Map U k : Remove \W : /[^A-Z0-9]/gi ü : Sort & partition by Ê : Length m : Map q : Join = : Reassign to U for next iteration :Implicit output of last element  • Although I forgot to add test cases for it at the time (will add one now), digits should also be kept in the strings (so [a-zA-Z0-9] instead of [a-zA-Z]). – Kevin Cruijssen Jan 7 '19 at 16:47 • @KevinCruijssen, fixed – Shaggy Jan 7 '19 at 16:48 # JavaScript, 119 bytes I feel like this should be much shorter ... Includes 2 leading newlines in the output. f=s=>s==(s.replace(/[^\w\n]|_/g,t=).split .sort((x,y)=>x[l=length]-y[l]).map(x=>t+=s==(s=x[l])?x: +x),t)?t:f(t)  Try it online # Perl 6, 85 bytes {.&([o] {my@a;@a[+.comb]~=$_ for $_;@a.grep(~*)}xx$_).sort(+*.comb)}o*.map:{S:g/\W//}


Try it online!

Input and outputs as lists of strings.

# Pyth, 21 bytes

jusM.glkG:R"[^\w\d]"k


Input is a list of strings. Try it online here, or verify all the test cases here.

jusM.glkG:R"[^\w\d]"kQ   Implicit: Q=eval(input()), k=""
Trailing Q inferred
R          Q   For each string in Q...
: "[^\w\d]"     ... replace non-alphanumerics...
k    ... with k (empty string)
u                       Repeat the following until a fixed point occurs, current as G:
.g  G                  Group the elements of G...
lk                   ... by length
Groups ordered by the result of the inner function, i.e. length
This means that, in the final iteration, this acts as a sort by length
sM                       Concatenate each group back into a string
j                        Join the resulting list on newlines, implicit print


# 05AB1E, 16 bytes

εžKÃ}»Δ¶¡é.γg}J»


Input as a list of strings.

Could have been 14 bytes with εžKÃ}Δé.γg}J}» if Δ would work with a list of strings as well..

Explanation:

ε   }            # Map the (implicit) input-list of strings:
žjÃ             #  Leave only the letters and digits of each string
#   i.e. ["a","bc","d!","123"] → ["a","bc","d","123"]
»           # Join the list by newlines to a single string
#  i.e. ["a","bc","d","123"] → "a\nbc\nd\n123"
Δ          # Loop until the string no longer changes:
¶¡        #  Split by newlines
#   i.e. "a\nbc\nd\n123" → ["a","bc","d","123"]
.γ }    #  Group the strings by:
g     #   Their length
#    i.e. ["a","bc","d","123"] → [["a,"d"],["bc"],["123"]]
J   #  Join each group-list to a single string
»  #  Join this list by newlines again
# (and the result is output implicitly after the loop)

$n = @" This is a sample text, that you will have to use to build stairs. The wood may be of excellent quality, or pretty crappy almost falling apart and filled with termites. Bla bla bla - some more text Ok, that will do "@  and code... ((($n -Split '\n').Replace(" ","")) -Replace '[\W]','')|Sort *h