39
\$\begingroup\$

Don't you love those exploded-view diagrams in which a machine or object is taken apart into its smallest pieces?

enter image description here

Let's do that to a string!

The challenge

Write a program or function that

  1. inputs a string containing only printable ASCII characters;
  2. dissects the string into groups of non-space equal characters (the "pieces" of the string);
  3. outputs those groups in any convenient format, with some separator between groups.

For example, given the string

Ah, abracadabra!

the output would be the following groups:

!
,
A
aaaaa
bb
c
d
h
rr

Each group in the output contains equal characters, with spaces removed. A newline has been used as separator between groups. More about allowed formats below.

Rules

The input should be a string or an array of chars. It will only contain printable ASCII chars (the inclusive range from space to tilde). If your language does not support that, you can take the input in the form of numbers representing ASCII codes.

You can assume that the input contains at least one non-space character.

The output should consist of characters (even if the input is by means of ASCII codes). There has to be an unambiguous separator between groups, different than any non-space character that may appear in the input.

If the output is via function return, it may also be an array or strings, or an array of arrays of chars, or similar structure. In that case the structure provides the necessary separation.

A separator between characters of each group is optional. If there is one, the same rule applies: it can't be a non-space character that may appear in the input. Also, it can't be the same separator as used between groups.

Other than that, the format is flexible. Here are some examples:

  • The groups may be strings separated by newlines, as shown above.

  • The groups may be separated by any non-ASCII character, such as ¬. The output for the above input would be the string:

    !¬,¬A¬aaaaa¬bb¬c¬d¬h¬rr
    
  • The groups may be separated by n>1 spaces (even if n is variable), with chars between each group separated by a single space:

    !  ,    A   a a a a a    b b  c       d   h  r r
    
  • The output may also be an array or list of strings returned by a function:

    ['!', 'A', 'aaaaa', 'bb', 'c', 'd', 'h', 'rr']
    
  • Or an array of char arrays:

    [['!'], ['A'], ['a', 'a', 'a', 'a', 'a'], ['b', 'b'], ['c'], ['d'], ['h'], ['r', 'r']]
    

Examples of formats that are not allowed, according to the rules:

  • A comma can't be used as separator (!,,,A,a,a,a,a,a,b,b,c,d,h,r,r), because the input may contain commas.
  • It's not accepted to drop the separator between groups (!,Aaaaaabbcdhrr) or to use the same separator between groups and within groups (! , A a a a a a b b c d h r r).

The groups may appear in any order in the output. For example: alphabetical order (as in the examples above), order of first appearance in the string, ... The order need not be consistent or even deterministic.

Note that the input cannot contain newline characters, and A and a are different characters (grouping is case-sentitive).

Shortest code in bytes wins.

Test cases

In each test case, first line is input, and the remaining lines are the output, with each group in a different line.

  • Test case 1:

    Ah, abracadabra!
    !
    ,
    A
    aaaaa
    bb
    c
    d
    h
    rr
    
  • Test case 2:

    \o/\o/\o/
    ///
    \\\
    ooo
    
  • Test case 3:

    A man, a plan, a canal: Panama!
    !
    ,,
    :
    A
    P
    aaaaaaaaa
    c
    ll
    mm
    nnnn
    p
    
  • Test case 4:

    "Show me how you do that trick, the one that makes me scream" she said
    ""
    ,
    S
    aaaaa
    cc
    dd
    eeeeeee
    hhhhhh
    ii
    kk
    mmmm
    n
    ooooo
    rr
    ssss
    tttttt
    u
    ww
    y
    
\$\endgroup\$
  • 1
    \$\begingroup\$ If we use non-ASCII symbols like "¬" as separator, can it be counted as 1 byte? \$\endgroup\$ – Leaky Nun Aug 24 '16 at 22:39
  • 5
    \$\begingroup\$ @LeakyNun No, it will be counted as it corresponds depending on the encoding used for the source code, as usual \$\endgroup\$ – Luis Mendo Aug 24 '16 at 22:40
  • \$\begingroup\$ Is a trailing newline after the last group acceptable? \$\endgroup\$ – Phaeze Aug 24 '16 at 23:10
  • \$\begingroup\$ Is a leading newline of output acceptable? \$\endgroup\$ – DJMcMayhem Aug 24 '16 at 23:16
  • 1
    \$\begingroup\$ @RohanJhunjhunwala Well done! :-) Yes, several newlines as separators is fine \$\endgroup\$ – Luis Mendo Aug 25 '16 at 21:35

49 Answers 49

1
\$\begingroup\$

Pyke, 16 9 bytes

d-}F/i*d+

Try it here!

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

PHP, 67 bytes

Space as separator

PHP5 using deprecated ereg_replace function.

for(;++$i<128;)echo ereg_replace("[^".chr($i)."]","",$argv[1])." ";

PHP7, 73 bytes

for(;++$i<128;)echo str_repeat($j=chr($i),substr_count($argv[1],$j))." ";

Using built in is worse :(

foreach(array_count_values(str_split($argv[1]))as$a=>$b)echo str_repeat($a,$b)." ";
\$\endgroup\$
1
\$\begingroup\$

Perl, 31 bytes

Includes +1 for -p

Run with input on STDIN:

explode.pl <<< "ab.ceaba.d"

explode.pl:

#!/usr/bin/perl -p
s%.%$&x s/\Q$&//g.$/%eg;y/
//s

If you don't care about spurious newlines inbetween the lines the following 24 bytes version works too:

#!/usr/bin/perl -p
s%.%$&x s/\Q$&//g.$/%eg
\$\endgroup\$
1
\$\begingroup\$

Oracle SQL 11.2, 123 bytes

SELECT LISTAGG(c)WITHIN GROUP(ORDER BY 1)FROM(SELECT SUBSTR(:1,LEVEL,1)c FROM DUAL CONNECT BY LEVEL<=LENGTH(:1))GROUP BY c;

Un-golfed

SELECT LISTAGG(c)WITHIN GROUP(ORDER BY 1)
FROM (SELECT SUBSTR(:1,LEVEL,1)c FROM DUAL CONNECT BY LEVEL<=LENGTH(:1))
GROUP BY c
\$\endgroup\$
1
\$\begingroup\$

S.I.L.O.S 265

The (non competing) code better input format is at the bottom, feel free to try it online!

def : lbl
loadLine :
a = 256
:a
:s
x = get a
z = x
z - 32
z |
if z u
a + 1
GOTO s
:u
if x c
GOTO e
:c
b = x
b + 512
c = get b
c + 1
set b c
a + 1
if x a
:e
a = 512
b = 768
l = 10
:Z
j = a
j - b
if j z
z = get a
c = a
c - 512
:3
if z C
printChar l
a + 1
GOTO Z
:C
printChar c
z - 1
GOTO 3
:z

Input for the above is a series of command line arguments representing ascii values, terminated with a zero.Try it online!.


For a more reasonable method of input we must up the byte count (and use features that were nonexistent before the writing of this challenge).
For 291 bytes we get the following code.

\
def : lbl
loadLine :
a = 256
:a
:s
x = get a
z = x
z - 32
z |
if z u
a + 1
GOTO s
:u
if x c
GOTO e
:c
b = x
b + 512
c = get b
c + 1
set b c
a + 1
if x a
:e
a = 512
b = 768
l = 10
:Z
j = a
j - b
if j z
z = get a
c = a
c - 512
:3
if z C
printChar l
a + 1
GOTO Z
:C
printChar c
z - 1
GOTO 3
:z

Feel free to test this version online!. The backslash is unnecessary but is there to show the important leading line feed.

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

ARM machine code on Linux, 60 bytes

Hex Dump:

b590 4c0d f810 2b01 b11a 58a3 3301 50a3 e7f8 6222 2704 2001 f1ad 0101 2201 237f 5ce5 b135 700b df00 3d01 d8fc 250a 700d df00 3b01 d8f4 bd90 000200bc

This function basically creates an array of size 128, and whenever it reads a character from the input string it increment's the value at that characters position. Then it goes back through the array and prints each character array[character] times.

Ungolfed Assembly (GNU syntax):

.syntax unified
.bss @bss is zero-initialized data (doesn't impact code size)
countArray:
    .skip 128 @countArray[x] is the number of occurances of x

.text
.global expStr
.thumb_func
expstr:
    @Input: r0 - a NUL-terminated string.
    @Output: Groups of characters to STDOUT
    push {r4,r7,lr}
    ldr r4,=countArray @Load countArray into r4
    readLoop:
        ldrb r2,[r0],#1 @r2=*r0++
        cbz r2,endReadLoop @If r2==NUL, break
        ldr r3,[r4,r2] @r3=r4[r2]
        adds r3,r3,#1 @r3+=1
        str r3,[r4,r2] @r4[r2]=r3
        b readLoop @while (true)
    endReadLoop:
    @Now countArray[x] is the number of occurances of x.
    @Also, r2 is zero
    str r2,[r4,#' ] @'<character> means the value of <character>
    @What that just did was set the number of spaces found to zero.
    movs r7,#4 @4 is the system call for write
    movs r0,#1 @1 is stdout
    sub r1,sp,#1 @Allocate 1 byte on the stack
    @Also r1 is the char* used for write
    movs r2,#1 @We will print 1 character at a time
    movs r3,#127 @Loop through all the characters
    writeLoop:
        ldrb r5,[r4,r3] @r5=countArray[r3]
        cbz r5,endCharacterLoop @If we're not printing anything, go past the loop
        strb r3,[r1] @We're going to print byte r3, so we store it at *r0
        characterLoop:
            swi #0 @Make system call
            @Return value of write is number of characters printed in r0
            @Since we're printing one character, it should just return 1, which
            @means r0 didn't change.
            subs r5,r5,#1
            bhi characterLoop
        @If we're here then we're done printing our group of characters
        @Thus we just need to print a newline.
        movs r5,#10 @r5='\n' (reusing r5 since we're done using it as a loop counter
        strb r5,[r1]
        swi #0 @Print the character

        endCharacterLoop:
        subs r3,r3,#1
        bhi writeLoop @while (--r3)
    pop {r4,r7,pc}
.ltorg @Store constants here

Testing script (also assembly):

.global _start
_start:
    ldr r0,[sp,#8] @Read argv[1]
    bl expstr @Call function
    movs r7,#1 @1 is the system call for exit
    swi #0 @Make system call
\$\endgroup\$
1
\$\begingroup\$

Scala, 53 bytes

def?(s:String)=s.filter(_!=' ').groupBy(identity).map{_._2.mkString}

When run with REPL:

scala> ?("Ah, abracadabra!")
res2: scala.collection.immutable.Iterable[String] = List(!, A, aaaaa, ,, bb, c, h, rr, d)

scala> print(res2.mkString("\n"))
!
A
aaaaa
,
bb
c
h
rr
d

EDIT: Forgot to filter spaces

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

Clojure, 46

#(partition-by identity(sort(re-seq #"\S" %)))

Long and descriptive function names for simplest of things, yeah.

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

PowerShell, 65

@TimmyD has the shorter answer but I don't have enough rep to comment. Here's my answer in 65 bytes.

I didn't think to use group and I didn't know that you could stick -join in front of something instead of -join"" on the end and save two characters (using that would make my method 63).

([char[]]$args[0]-ne32|sort|%{if($l-ne$_){"`n"};$_;$l=$_})-join""

My method sorts the array, then loops through it and concatenates characters if they match the preceding entry, inserting a newline if they do not.

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

Clojure (55 bytes)

(defn f[s](map(fn[[a b]](repeat b a))(frequencies s)))

Test Cases:

(f "Ah, abracadabra!")
;;=> ((\space) (\!) (\A) (\a \a \a \a \a) (\b \b) (\c) (\d) (\h) (\,) (\r \r))

(f "\\o/\\o/\\o/")
;;=> ((\\ \\ \\) (\o \o \o) (\/ \/ \/))

(f "A man, a plan, a canal: Panama!")
;;=> ((\space \space \space \space \space \space) (\!) (\A) (\a \a \a \a \a \a \a \a \a) (\c) (\, \,) (\l \l) (\m \m) (\n \n \n \n) (\P) (\p) (\:))

(f "\"Show me how you do that trick, the one that makes me scream\" she said")
;;=> ((\space \space \space \space \space \space \space \space \space \space \space \space \space \space) (\a \a \a \a \a) (\" \") (\c \c) (\d \d) (\e \e \e \e \e \e \e) (\h \h \h \h \h \h) (\i \i) (\k \k) (\,) (\m \m \m \m) (\n) (\o \o \o \o \o) (\r \r) (\S) (\s \s \s \s) (\t \t \t \t \t \t) (\u) (\w \w) (\y))
\$\endgroup\$
1
\$\begingroup\$

Javascript (ES5): 50 48 bytes

As a function:

function(i){return i.split("").sort().join(" ")}

The important code, replace the empty string with the search string (29 bytes):

"".split("").sort().join(" ")
\$\endgroup\$
1
\$\begingroup\$

Java 8, 121 120 110 103 Bytes

s->s.chars().distinct().filter(c->c!=32)
    .forEach(c->out.println(s.replaceAll("[^\\Q"+(char)c+"\\E]","")))

Above lambda can be consumed with Java-8 Consumer. It fetches distinct characters and replaces other characters of the String to disassemble each character occurrence.

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

Elixir, 70 bytes

def f(s), do: '#{s}'|>Enum.group_by(&(&1))|>Map.delete(32)|>Map.values
\$\endgroup\$
1
\$\begingroup\$

R, 67 bytes

Even though there's already an R-answer by @Frédéric I thought my solution deserves it's own answer because it's conceptually different.

for(i in unique(x<-strsplit(readline(),"")[[1]]))cat(x[x%in%i]," ")

The program prints the ascii characters in order of appearence in the string where groups are separated by two white spaces and chars within groups by one white space. A special case is if the string has whitespaces in itself, then at one specific place in the output there will be 4+number of white spaces in string white spaces separating two groups E.g:

Ah, abracadabra! => A h , a a a a a b b r r c d !

Ungolfed

Split up the code for clarity even though assignment is done within the unique functions and changed to order of evaluation:

x<-strsplit(readline(),"")[[1]]) # Read string from command line and convert into vector

for(i in unique(x){              # For each unique character of the string, create vector 
  cat(x[x%in%i]," ")             # of TRUE/FALSE and return the elements in x for which 
}                                # these are matched
\$\endgroup\$
1
\$\begingroup\$

C, 178

#define F for
#define i8 char
#define R return
z(i8*r,i8*e,i8*a){i8*b,c;F(;*a;++a)if(!isspace(c=*a)){F(b=a;*b;++b)if(r>=e)R-1;else if(*b==c)*b=' ',*r++=c;*r++='\n';}*r=0;R 0;}

z(outputArray,LastPointerOkinOutputarray,inputArray) return -1 on error 0 ok Note:Modify its input array too...

#define P printf
main(){char a[]="A, 12aa99dd333aA,,<<", b[256]; z(b,b+255,a);P("r=%s\n", b);}

/*
 178
 r=AA
 ,,,
 1
 2
 aaa
 99
 dd
 333
 <<
 */
\$\endgroup\$
1
\$\begingroup\$

Bash + coreutils, 53 50 45 bytes

sed 's: ::g;s:\(.\)\1*:& :g'<<<`fold -1|sort`

There is no separator between the characters of a group and the groups are separated by space. There is one trailing group separator, but as I understood that's acceptable.

Run:

./explode_view.sh <<< '\o /\ o /\ o k'

Output:

// \\\ k ooo 
\$\endgroup\$
1
\$\begingroup\$

Racket 243 bytes

(let*((l(sort(string->list s)char<?))(g(λ(k i)(list-ref k i))))(let loop((i 1)(ol(list(g l 0))))(cond((= i(length l))
(reverse ol))((equal?(g l(- i 1))(g l i))(loop(+ 1 i)(cons(g l i)ol)))(else(loop(+ 1 i)(cons(g l i)(cons #\newline ol)))))))

Ungolfed:

(define (f s)
  (let*((l (sort (string->list s) char<?))
        (g (λ (k i)(list-ref k i))) )
    (let loop ((i 1)
               (ol (list (g l 0))))
      (cond
        ((= i (length l)) (reverse ol))
        ((equal? (g l (- i 1)) (g l i))
         (loop (+ 1 i) (cons (g l i) ol)))
        (else (loop (+ 1 i) (cons (g l i) (cons #\newline ol))))
        ))))

Testing:

(display (f "Ah, abracadabra!"))

Output:

(  
 ! 
 , 
 A 
 a a a a a 
 b b 
 c 
 d 
 h 
 r r)
\$\endgroup\$
1
\$\begingroup\$

Ruby, 35 bytes

->s{(?!..?~).map{|x|s.scan x}-[[]]}

Tried to turn the problem upside down to save some bytes. Instead of starting with the string, start with the possible character set.

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

PowerShell - 86

I imagine I could get this smaller, but I'll work on it later. This assumes that $d contains the string to explode.

$s="";$i=0;[Array]::Sort(($a=$d-split""));$a|%{$s+=$_;if($a[++$i]-cne$_){$s+="`n"}};$s
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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