21
\$\begingroup\$

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
adbc                         dabc
\$\endgroup\$
8
  • 1
    \$\begingroup\$ contains isn't supposed to be in output 2. It gets merged with thatrule \$\endgroup\$
    – Keatinge
    May 30, 2016 at 10:49
  • 2
    \$\begingroup\$ You pretty much got the exact opposite of what you wanted, it's pretty hard to do this. \$\endgroup\$
    – Bálint
    May 30, 2016 at 15:46
  • \$\begingroup\$ "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? \$\endgroup\$
    – Bálint
    May 30, 2016 at 18:12
  • \$\begingroup\$ Was this challenge in the sandbox? \$\endgroup\$
    – Bálint
    May 30, 2016 at 18:13
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ May 31, 2016 at 6:49

22 Answers 22

4
\$\begingroup\$

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"]]
  mΣ           Concatenate each group: ["ad","bc","123"]
             Final result ["123","adbc"], print implicitly separated by newlines.
\$\endgroup\$
2
  • \$\begingroup\$ 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. \$\endgroup\$ Aug 19, 2017 at 11:07
  • \$\begingroup\$ 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! \$\endgroup\$ Apr 20, 2018 at 14:44
4
\$\begingroup\$

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
\$\endgroup\$
2
  • 1
    \$\begingroup\$ 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! \$\endgroup\$
    – Blue
    May 30, 2016 at 13:25
  • \$\begingroup\$ you can use filter(str.isalnum, l) instead of the "".join part \$\endgroup\$
    – njzk2
    May 30, 2016 at 17:59
4
\$\begingroup\$

Retina, 69 63 bytes

[^\w¶]|_

{`\b((.)+)¶((?.)+)\b(?(2)(?!))
$1$3
O$`(.)+
$#1$*

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ I think you can change the first line to [^\w¶]|_. Although I'm still not sure it is optimal. \$\endgroup\$ May 30, 2016 at 14:16
3
\$\begingroup\$

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)  
\$\endgroup\$
3
  • \$\begingroup\$ You can replace your regex with [\W_] \$\endgroup\$
    – THC
    May 30, 2016 at 19:54
  • \$\begingroup\$ @FliiFe it does not remove the ',' and '.' in the last test case \$\endgroup\$
    – Jeto
    May 31, 2016 at 1:46
  • \$\begingroup\$ 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) \$\endgroup\$
    – THC
    May 31, 2016 at 15:02
3
\$\begingroup\$

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
\$\endgroup\$
2
\$\begingroup\$

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
\$\endgroup\$
2
\$\begingroup\$

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

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

jq, 78 bytes

Expects "an array with multiple strings."

def g:group_by(length);map(scan("\\w+"))|g|until(all(length<2);map(add)|g)[][]

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Javascript 198 188 186 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

\$\endgroup\$
8
  • \$\begingroup\$ What do you use the t variable for? \$\endgroup\$
    – gcampbell
    May 30, 2016 at 16:16
  • \$\begingroup\$ Ok so you can golf it by declaring y="split" and then instead of using .split() you can use [y]() \$\endgroup\$ May 30, 2016 at 16:18
  • \$\begingroup\$ @gcampbell That was just a leftover from testing \$\endgroup\$
    – Bálint
    May 30, 2016 at 16:27
  • \$\begingroup\$ @BaldBantha I don't think that would make it shorter \$\endgroup\$
    – Bálint
    May 30, 2016 at 16:28
  • \$\begingroup\$ @BaldBantha I did that with length though \$\endgroup\$
    – Bálint
    May 30, 2016 at 16:29
1
\$\begingroup\$

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!

\$\endgroup\$
1
\$\begingroup\$

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)
\$\endgroup\$
0
1
\$\begingroup\$

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)
\$\endgroup\$
1
\$\begingroup\$

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
\$\endgroup\$
1
  • \$\begingroup\$ Try to use R and L instead of m \$\endgroup\$
    – Leaky Nun
    May 30, 2016 at 23:10
1
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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
\$\endgroup\$
2
  • \$\begingroup\$ 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]). \$\endgroup\$ Jan 7, 2019 at 16:47
  • \$\begingroup\$ @KevinCruijssen, fixed \$\endgroup\$
    – Shaggy
    Jan 7, 2019 at 16:48
1
\$\begingroup\$

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

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

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.

\$\endgroup\$
1
\$\begingroup\$

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
\$\endgroup\$
1
\$\begingroup\$

Vyxal, 12 bytes

krv↔⁽LġṠÞṡ)Ẋ

Try it Online!

I/O as lists of lines

          )Ẋ # Repeat until no change...
  v          # To each...
   ↔         # Keep only characters from...
kr           # Alphanumeric chars
      ġ      # Group by...
    ⁽L       # Length
       Ṡ     # Concatenate each
        Þṡ   # Sort by length
\$\endgroup\$
1
  • 1
    \$\begingroup\$ @KevinCruijssen Done \$\endgroup\$
    – emanresu A
    Oct 21, 2022 at 7:20
1
\$\begingroup\$

Ruby, 95 93 bytes

f=->a{b=a.map{_1.gsub /\W|_/,""}.group_by(&s=:size).map{_2*""}.sort_by &s
b[a.size-1]?a:f[b]}

Attempt This Online!

\$\endgroup\$
0
\$\begingroup\$

05AB1E, 16 bytes

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

Input as a list of strings.

Try it online or verify all test cases.

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
                 #   i.e. [["a,"d"],["bc"],["123"]] → ["ad","bc","123"]
              »  #  Join this list by newlines again
                 #   i.e. ["ad","bc","123"] → "ad\nbc\n123"
                 # (and the result is output implicitly after the loop)
                 #  i.e. "123\nadbc"
\$\endgroup\$
-1
\$\begingroup\$

Powershell, Windows 10, 63 bytes

So input...

$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

That covers input/output 1, working on 2 and 3...

\$\endgroup\$
2
  • \$\begingroup\$ Welcome to PPCG! We usually don't allow input by setting a variable. You'd either have to create function that takes an argument, or take input from STDIN, a command line arg, or similar. \$\endgroup\$
    – Stephen
    Aug 20, 2017 at 17:27
  • 1
    \$\begingroup\$ Welcome to PPCG! In addition to what @StepHen said, you current answer fails for the special case. It only puts everything together and sorts once, but it doesn't merge equal-sized lines together and sort again. (See test case 2.) \$\endgroup\$ Aug 20, 2017 at 17:38

Your Answer

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

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