# Split string on first occurrence of each character

Related.

Given a printable ASCII string, split it into a list of non-empty strings with a new sub-string beginning every time a character, which has not previously been seen in the same case, occurs.

### Examples

"mississippi" → ["m","i","ssissi","ppi"]

"P P & C G" → ["P"," P ","& ","C ","G"]

"AAA" → ["AAA"]

"" → []


Anecdote: The result will have between 0 and 95 elements. The 95th sub-string will necessarily continue until the end because at that point, all printable ASCII characters have begun a sub-string, so every additional character will have occurred before and thus cannot cause a new sub-string to begin.

• An example containing " and ' seems like a good idea. – Emigna Feb 19 '18 at 18:15
• Would ""[""] be acceptable? – Arnauld Feb 19 '18 at 18:18
• @Emigna That just messes with the example output format without bringing any further clarity. – Adám Feb 19 '18 at 18:22
• If outputting as a newline-separated string, can there be a leading/trailing newline? – wastl Feb 19 '18 at 21:32
• @wastl Uh, I'll permit it in this case because it cannot indicate empty segments, although it does clash with my earlier ruling of [""] to be invalid. Sigh. – Adám Feb 19 '18 at 21:56

# Jelly, 4 bytes

QƤĠị


Try it online!

## Explanation

QƤĠị  Input is a string, say s = "adam"
Ġ   Group indices by equal values: [[1],[2,3],[4]]
ị  Index into s: ["a","da","m"]


The internal representation of the strings, which the TIO link displays, is slightly different.

## Retina, 9 bytes

q1,.

# 05AB1E, 11 bytes

ÙSk¥sg¸«£õK


Try it online!

Explanation

Ù             # remove duplicates in input
S            # split to a list of characters
k           # get the (first) index of each character in the input
¥          # calculate delta's
sg¸«      # append the length of the input
£     # split the list into pieces of these sizes
õK   # remove empty string (for the special case "" -> [])

• For anyone coming across this answer, ¸« can be ª in the new version of 05AB1E. – Kevin Cruijssen Mar 8 at 12:43

# C,  75  65  63 bytes

Thanks to @Digital Trauma for saving 10 bytes and thanks to both @gastropner and @l4m2 for saving a byte each!

f(char*s){for(int l[128]={};*s;putchar(*s++))l[*s]++||puts(l);}


Prints a leading newline.

Try it online!

Without a leading newline (71 bytes):

f(char*s){int l[128]={};for(l[*s]=1;*s;putchar(*s++))l[*s]++||puts(l);}


Try it online!

• 64 bytes – gastropner Feb 21 '18 at 15:30
• @gastropner Clever trick; thanks! – Steadybox Feb 21 '18 at 15:34
• {0} => {} ? – l4m2 Mar 9 '18 at 23:02
• @l4m2 Yep, thanks! – Steadybox Mar 10 '18 at 0:16

# Perl 6,  58 52  40 bytes

{$/={};.comb.classify({$+=!$/{$_}++}).sort».value».join}


Try it

*.comb.classify({$+=!(%){$_}++}).sort».value».join


Try it

*.classify({$+=!(%){$_}++}).sort».value


Try it
(input is a list of characters, and output is a list of lists of characters)

## Expanded:

*                   # parameter for WhateverCode lambda

.classify(        # classify that list
{
$# anonymous scalar state variable (accumulator) += # increment it if: ! # Bool invert the following ( % # anonymous hash state variable ){$_ }++ # look to see if the character was seen already
}
).sort\           # sort the Pairs by key (makes the order correct)
».value           # get the value from each Pair


The output from classify is

{ # Hash
1 => ['m'],
2 => ['i'],
3 => ['s','s','i','s','s','i'],
4 => ['p','p','i'],
}


And .sort just turns it into:

[
1 => ['m'],
2 => ['i'],
3 => ['s','s','i','s','s','i'],
4 => ['p','p','i'],
]


».value removes the keys

[
['m'],
['i'],
['s','s','i','s','s','i'],
['p','p','i'],
]

• Why would the keys ever be out of order? Is insertion order not tracked like a HashMap vs. a LinkedHashMap in Java where the order is based on memory vs. insert order? – Magic Octopus Urn Feb 20 '18 at 17:18
• @MagicOctopusUrn No version of Perl has had ordered Hashes. In fact Perl 5 version 18 made Hashes more randomized which helps make a certain type of denial of service attack less possible, and has also caused buggy user code to expose it's buggy behaviour more often. Now someone could (and likely has) implement a class which does keep track, but that would take more than 5 characters to load and use. – Brad Gilbert b2gills Feb 20 '18 at 20:12

# J, 7 bytes

~:<;.1]


Try it online!

# Explanation

Nub sieve's chance to shine!

~: <;.1 ]
]  Input
~:         Nub sieve (1 if the character is the first instance in string)
;.1    Split input on 1s in nub sieve
<       And box each

• I was about to post exactly the same (not surprisingly) answer, it's good that I glanced your submission before that :) – Galen Ivanov Feb 19 '18 at 19:48
• @GalenIvanov I -- and I imagine most other J golfers, too -- relish the chance to use nub sieve or self-classify. – cole Feb 19 '18 at 20:18

# APL (Dyalog Unicode), 8 bytesSBCS

(≢¨∪\)⊆⊢


Try it online!

• But, but… Oh my. – Adám Feb 20 '18 at 18:06
• I suspected you might have posted this challenge because of the new primitive (⊆). Evidently not :) – ngn Feb 20 '18 at 20:15
• It looks like an embarrassed Kirby holding a baby bottle. – Magic Octopus Urn Feb 20 '18 at 20:24
• for everyone who needs to look up "Kirby" like I did - it's an anthropomorphic pink ball from a Japanese video game – ngn Feb 20 '18 at 20:41

# 05AB1E, 8 bytes

Ùvyy¶ì.;


Try it online!

Always will output 1 preceding newline, which is constant and not indicative of a split, the 10-byte alternative that does not output a preceding newline is Ùvyy¶ì.;}¦, you can try that here. According to Adam a preceding or trailing newline is acceptable.

Input      = mississippi                               | Stack
-----------#-------------------------------------------+----------------------------------
Ù          # Push unique letters of first input.       | ['misp']
v         # Iterate through each unique letter.       | []
yy       # Push 2 copies of the letter (or yD)       | ['m','m']
¶      # Push a newline char.                      | ['m','m','\n']
ì     # Prepended to the letter.                  | ['m','\nm']
.;   # Replace first instance with '\n + letter' | ['\nmississippi']


After each iteration we get:

['\nmississippi'] > ['\nm\nississippi'] > ['\nm\ni\nssissippi'] > ['\nm\ni\nssissi\nppi']


Which is:

m
i
ssissi
ppi

• Nice! Beat me by a fair margin ;) – Emigna Feb 28 '18 at 13:57
• @Emigna this was sitting as a comment on your answer for 2 days then I just posted it b/c no response haha :P. – Magic Octopus Urn Feb 28 '18 at 14:06
• Weird, haven't seen any notification on that. Different enough for its own answer though :) – Emigna Feb 28 '18 at 14:14
• @Emigna well, I mean, I deleted it haha. – Magic Octopus Urn Feb 28 '18 at 15:56
• Skipping the loop saves a byte ÙSD¶ì.;. Not sure why we didn't think of that before :P – Emigna Mar 8 at 12:10

# Haskell, 39 bytes

foldl(\s c->s++['\n'|all(/=c)s]++[c])""


Try it online!

Inserts a newline symbol before every character that appears for the first time, resulting in a newline-separated string, with a leading newline. Prepend lines. to produce a list.

(""%)
_%[]=[]
p%s|(a,b)<-span(elems!!0:p)s=a:(a++p)%b


Try it online!

Repeatedly takes the prefix the first character plus the non-unique characters that follow it.

• @WheatWizard Oops, yes, lines. – xnor Feb 21 '18 at 4:42
• Might want to do tail.lines to remove the extra empty string now that I think about it. – Wheat Wizard Feb 21 '18 at 6:06

# APL (Dyalog), 9 bytes

Thanks, Erik the Outgolfer for saving 1 byte!

⊢⊂⍨⍳∘≢∊⍳⍨


Try it online!

# Explanation:

⍳⍨: For each character, get the index of its first occurrence. e.g mississippi -> 1 2 3 3 2 3 3 2 9 9 2

⍳∘≢: The range from 1 to the length of the input.

∊: Membership. e.g 1 2 3 4 5 6 7 8 9 10 11∊1 2 3 3 2 3 3 2 9 9 2 -> 1 1 1 0 0 0 0 0 1 0 0

⊢⊂⍨: Partition the input string with new partitions starting at 1s in the vector above

• 9 bytes (monadic fg and monadic f∘g behave the same) – Erik the Outgolfer Feb 19 '18 at 18:37
• Why ∊ instead of =? – Adám Feb 20 '18 at 9:32
• At the time of writing, I hadn't considered that the indices would be in the correct positions. Although it is clear that they are – H.PWiz Feb 20 '18 at 17:01

# Japt, 11 bytes

¬â r@=iRUbY


Test it online!

### Explanation

This was inspired by Magic Octopus Urn's 05AB1E solution.

¬â r@=iRUbY    Implicit: U = input string
¬â             Split U into chars, and keep only the first occurrence of each.
r@          Reduce; for each char Y in this string...
UbY      Find the first index of Y in U.
iR         Insert a newline at this index in U.
=           Set U to the result.
As reduce returns the result of the last function call, this gives the
value of U after the final replacement, which is implicitly printed.

• Japt is having an identity crisis here, it's calling itself Ruby for some reason. iRUbY! – Magic Octopus Urn Feb 21 '18 at 15:37

# JavaScript (ES6), 37 bytes

Saved 7 bytes: a leading newline was explicitly allowed (Thanks @Shaggy!)

Takes input as an array of characters. Outputs a newline-separated string.

s=>s.map(c=>s[c]=s[c]?c:
+c).join


### Test cases

let f =

s=>s.map(c=>s[c]=s[c]?c:
+c).join

;[
[..."mississippi"],
[..."P P & C G"],
[..."AAA"],
[...""]
]
.forEach(s => console.log('{' + f(s) + '}\n'))

## brainfuck, 66 bytes

,[>+[<[>+<<-<+>>-]>[>]<<[[+]++++++++++.>>>]<]<[>+<-]>>>[>>]<<-.>,]


Formatted:

,
[
>+
[
<[>+< <-<+>>-]
>[>]
<<[[+]++++++++++.>>>]
<
]
<[>+<-]
>>>[>>]
<<-.>,
]


Try it online

The leading newline in the output (which is only printed if the input is non-empty) can be removed at the cost of 5 bytes by replacing the body x of the main (outermost) loop with .>,[x].

# Jelly, 6 bytes

ŒQœṗ⁸Ḋ


Try it online!

# K4, 19 bytes

Solution:

$[#x;(*:'.=x)_;,]x:  Examples: q)k)$[#x;(*:'.=x)_;,]x:"mississippi"
,"m"
,"i"
"ssissi"
"ppi"
q)k)$[#x;(*:'.=x)_;,]x:"P P & C G" ,"P" " P " "& " "C " ,"G" q)k)$[#x;(*:'.=x)_;,]x:"AAA"
"AAA"
q)k)$[#x;(*:'.=x)_;,]x:"Adam" ,"A" ,"d" ,"a" ,"m" q)k)$[#x;(*:'.=x)_;,]x:""
,[""]


Explanation:

8 bytes is just to handle ""...

$[#x;(*:'.=x)_;,]x: / the solution x: / save input as variable x$[  ;         ; ]   / $[condition;true;false] #x / length of x ("" has length 0, i.e. false) _ / cut right at left indices ( ) / do together =x / group x into key/value (char!indices) . / return value (get indices) *:' / first (*:) each , / enlist, "" => [""]  # Python 2, 81 74 bytes def f(s):d=sorted(map(s.find,set(s)));print map(lambda a,b:s[a:b],d,d[1:])  Try it online! • Save two with list(set(map(s.find,s))) – Jonathan Allan Feb 19 '18 at 19:41 • @JonathanAllan it's a misleading side-effect, set do not keep order, counter-proof -> s='c'*6+'a'*100+'b' – Rod Feb 19 '18 at 19:59 • I know we cannot rely on it in future implementations but I believe given ordered integers we maintain order in the set due to the hash of an integer being the integer (as you have shown the same is not true for other objects -- can you find a word that does not work with my alternative?). – Jonathan Allan Feb 19 '18 at 22:07 • @JonathanAllan not true either – Rod Feb 20 '18 at 0:14 • Ah, fair enough, my belief was false! – Jonathan Allan Feb 20 '18 at 0:22 # Python 2, 47 bytes lambda s:reduce(lambda r,c:r+'\n'[c in r:]+c,s)  Try it online! Outputs a newline-separated string. Barely beats the program version: Python 2, 48 bytes r='' for c in input():r+='\n'[c in r:]+c print r  Try it online! # Perl, 30 bytes Includes +1 for p Give input without trailing newline on STDIN. Output is also without trailing newline: echo -n adam | perl -pE 's%.%$v{$&}+++!pos?$&:$/.$&%eg'; echo


If you don't care about leading and trailing newlines this 25 (+3 for -p because the code contains ') also works:

#!/usr/bin/perl -p
s%%$/x!$v{$'&~v0}++%eg  • Great solution as always! Based on the test cases provided, you don't need to name your hash, you can do ${$&}++. It's not as robust, but might suffice for this challenge? Also, there's been a consensus on meta that perl -p doesn't need an additional byte, you just need to have the header as Perl with -p  instead of just Perl. I'm trying to remember to do that myself... – Dom Hastings Feb 20 '18 at 10:08 • @DomHastings The annecdote about at most 95 possible strings quite strongly implies that 1 is valid, in which case the v is needed. Regarding the counting, I mostly follow codegolf.meta.stackexchange.com/a/7539/51507 which to me is the most consistent meta post about counting perl. – Ton Hospel Feb 20 '18 at 13:04 • Always nice to learn from the master. Specifically, in this case, &~v0 for grabbing the first character. Thank you for joining this site and sharing your long expertise. – msh210 Mar 6 '18 at 21:54 • You can use Strawberry Perl, which uses " instead of ' with -e, and then you can count the -ep as +1 rather than +3. (Tested.) – msh210 Mar 6 '18 at 22:06 # JavaScript, 6154 52 bytes Takes input as an array of characters. s=>s.map(x=>a[x]?a[y]+=x:a[x]=a[++y]=x,a=[],y=-1)&&a  ## Try it o.innerText=JSON.stringify((f= s=>s.map(x=>a[x]?a[y]+=x:a[x]=a[++y]=x,a=[],y=-1)&&a )([...i.value=""]));oninput=_=>o.innerText=JSON.stringify(f([...i.value])) <input id=i><pre id=o></pre> # R, 94 87 bytes function(s,n=nchar(s),g=substring)g(s,d<-which(!duplicated(g(s,1:n,1:n))),c(d[-1]-1,n))  Try it online! Returns a (possibly empty) list of substrings. Thanks to Michael M for saving 7 bytes! • function(s,n=nchar(s),g=substring)g(s,d<-which(!duplicated(g(s,1:n,1:n))),c(d[-1]-1,n)) would be shorter - and of course a bit uglier... – Michael M Feb 19 '18 at 20:25 • Why substring instead of substr? – plannapus Feb 20 '18 at 9:48 • @MichaelM Very nice! I still have to add the if(n) in there because substring throws an error for empty string input. – Giuseppe Feb 20 '18 at 12:20 • @plannapus substr returns a vector of length equal to its first input while substring returns one of length equal to the longest of its inputs. – Giuseppe Feb 20 '18 at 12:22 • @Giuseppe: Dropping the "if(n)" in R 3.4.3 maps the empty input string "" to the empty output string "", which should be fine(?) – Michael M Feb 20 '18 at 14:51 # Stax, 8 bytes ç↓‼►▐NVh  Run and debug online The ascii representation of the same program is this. c{[Ii=}(m  For each character, it splits when the index of the current character is the current position. c copy the input { }( split the string when the result of the enclosed block is truthy [ duplicate the input string under the top of the stack I get the character index of the current character i= is it equal to the iteration index? m print each substring  # ><>, 22 17 14 bytes -1 byte thanks to Emigna i:::a$1g?!o1po


Try it online!

Prints a leading and trailing newline.

It keeps track of which letters have already appeared by putting a copy of the character at that corresponding spot on the second row, and printing a newline if the value fetched from that position was not 1. Ends in an error when it tries to print -1

• Great use of g/p! 16 bytes – Emigna Mar 8 at 12:22

## Haskell, 62 bytes

r#c|any(elem c)r=init r++[last r++[c]]|1<2=r++[[c]]
foldl(#)[]


Try it online!

# JavaScript (ES6), 68 bytes

s=>s.map(c=>o[c]?t+=c:(t&&m.push(t),t=o[c]=c),t='',o=m=[])&&[...m,t]


Takes input as a list of characters.

Test cases:

let f=
s=>s.map(c=>o[c]?t+=c:(t&&m.push(t),t=o[c]=c),t='',o=m=[])&&[...m,t]

console.log(f([...'mississippi']));
console.log(f([...'P P & C G']));
console.log(f([...'AAA']));
console.log(f([...'']));

• I had a similar solution and asked if [""] was acceptable for the last test case. But it's not. :-( – Arnauld Feb 19 '18 at 23:27
• Oh, well, you got a much better solution anyway : ) – Rick Hitchcock Feb 20 '18 at 0:36

# PHP, 317 bytes

function SplitOnFirstUnique($s){$len = strlen($s);$output = [];
$newstring = ''; for ($i=0; $i <$len ; $i++) {$newstring = $newstring.$s[$i]; if(!in_array($s[$i] ,$output  )){
$output[] =$newstring;
$newstring = ''; } } return$output;
}


Try it online!

• Hello, and welcome to PPCG! I've edited your post to our standard format, and added a link to Try It Online so other people can test your code. The aim of Code Golf is to write the shortest code possible, and I can see a couple of ways to make this shorter, like using shorter variable names and leaving out some of the whitespace. You can check out the general tips and PHP tips pages for some more ideas. – Not a tree Feb 20 '18 at 6:34

# Red, 79 bytes

func[s][foreach c next unique/case append s"^@"[print copy/part s s: find s c]]


Try it online!

## Ungolfed:

f: func [s] [
b: next unique/case append s "^@"  ; append null to the end of the string, than
; find the unique characters and
; store all except the first to b
foreach c b [                      ; for each character in b
print copy/part s s: find s c  ; print the part of the string to
; where the character is found and
; set the beginning of the string to that position
]
]


# SNOBOL4 (CSNOBOL4), 11591 77 bytes

	N =INPUT
S	N LEN(1) . Y	:F(END)
S =S Y
N SPAN(S) . OUTPUT REM . N	:(S)
END


Try it online!

Prints the substrings separated by newlines.

Explanation:

line S (for SPLIT) doesn't actually split, but instead extracts the first character of N and saves it (.) to Y. On Failure, it jumps to END. The match should only fail when N is the empty string. Thus, when the input is empty, it jumps directly to END and outputs nothing.

S = S Y concatenates Y onto S.

SPAN(S) greedily matches a run of characters in S, and sends it (.) to OUTPUT, setting (.) N to the REMaining characters of N (if there are any). Then it jumps back to S.

# PowerShell, 73 bytes

{$r=@();$h=@{};[char[]]$ARGS[0]|%{if(!($h[$_]++)){$r+=""};$r[-1]+=$_};$r}  ## Usage PS> & {$r=@();$h=@{};[char[]]$ARGS[0]|%{if(!($h[$_]++)){$r+=""};$r[-1]+=$_};$r} "mississipi" | ConvertTo-Json -Compress
["m","i","ssissi","pi"]

• you can save some bytes - Try it online! – mazzy Mar 8 at 4:51

# Ruby, 6562 58 bytes

->s,*a{s.size.times{|i|(i==s.index(c=s[i])?a:a[-1])<<c}
a}


Try it online!

A lambda accepting a string and returning an array of strings.

Approach: For each index, either append the character at that index in s to the result array, or to the last string in the result array. String#index returns the index of the first instance of the argument.

-2 bytes: Initialize a as a splat argument instead of on its own line. Thanks, Value Ink!

-1 byte: Use c=s[i]...c instead of s[i]...s[i]. Thanks, Value Ink!

-4 bytes: Use .times instead of .map

# Java 8, 193169155 151 bytes

s->{for(int l=s.length(),i=0,j;i<l;i++)if(s.indexOf(s.charAt(i))==i){for(j=i;++j<l&&s.indexOf(s.charAt(j))!=j;);System.out.println(s.substring(i,j));}}


-14 bytes thanks to @raznagul (for something obvious I somehow missed myself..)
-3 bytes thanks to @O.O.Balance (again for something obvious I somehow missed myself.. :S)

Explanation:

Try it online.

s->{                    // Method with String parameter and no return-type
for(int l=s.length(), //  The length of the input-String
i=0,j;        //  Index integers
i<l;i++)          //  Loop i from 0 to l (exclusive)
if(s.indexOf(s.charAt(i))==i){
//   If the character at index i hasn't occurred yet:
for(j=i;++j<l     //    Inner loop j from i to l (exclusive),
&&s.indexOf(s.charAt(j))!=j;);
//     as long as the character at index j has already occurred
System.out.println(//    Print:
s.substring(i,j));}}
//     The substring of the input from index i to j (exclusive)

• I don't think you need the if(l<1). If l is 0 the loop shouldn't be executed anyway as 0<0 is false. – raznagul Feb 20 '18 at 14:46
• @raznagul Not sure how I missed that, but you're complete right!.. >.> – Kevin Cruijssen Feb 20 '18 at 14:56
• You're setting i=0 twice. You can save 3 bytes by dropping the second one: for(;i<l;i++)` – O.O.Balance Jul 18 '18 at 19:27
• @O.O.Balance Not sure how that happened.. :S But thanks for noticing! :) – Kevin Cruijssen Jul 19 '18 at 8:23