Shorter than a Split Second!
APL (Dyalog 14) (31)
{1↓¨(1,(⍳⍴⍵)∊2⍳⍨¨↓+\∘.=⍨⍵)⊂0,⍵}
This is a function that takes an array and returns a nested array.
Test:
+V← {1↓¨(1,(⍳⍴⍵)∊2⍳⍨¨↓+\∘.=⍨⍵)⊂0,⍵} 2 1 1 2 3 2 2 4 5 6 7 3 7 0 5
┌───┬┬─────────────┬┬─┬┐
│2 1││3 2 2 4 5 6 7││0││
└───┴┴─────────────┴┴─┴┘
⍝ this return value is a real nested array:
⎕←'Length: ',⍴V ⋄ (⍳⍴V){⎕←'Array #',⍺,': (', ⍵, ')'}¨V
Length: 6
Array # 1 : ( 2 1 )
Array # 2 : ()
Array # 3 : ( 3 2 2 4 5 6 7 )
Array # 4 : ()
Array # 5 : ( 0 )
Array # 6 : ()
Explanation:
0,⍵
: Add a0
to the front of⍵
, for easier processing. (It does not count as an occurrence.)(
...)⊂
: Split the array according to the given bitmask. A new group starts at each1
in the bitmask.+\∘.=⍨⍵
: for each value in (the original)⍵
, find all occurrences in⍵
. Then make a running sum for each value, giving a square matrix showing for each position in⍵
how many of each value have already occurred.↓
: Split the matrix by its rows, giving for each value an array showing the amount of times it has occurred by each position.2⍳⍨¨
: In each of these arrays, find the index of the first2
.(⍳⍴⍵)∊
: For each possible index into⍵
, see if it is contained in the list of indices of second occurrences. (These start each group, except the first one.)1,
: Add an1
to the front, marking the start of the first group.
1↓¨
: Remove the first element from each group. (These are the added0
, and the second occurrence of each value.)
J, 28 24 char
Special thanks to randomra.
(1&,<;._1~1,2=+/@(={:)\)
It works like this. Over all prefixes (\
) of the input array, we look at how many (+/@
) elements of the prefix are equal to the last element (={:
) of that prefix. When this number is 2, we know this is the second occurrence of that item in the array, so we split the array there using <;._1
.
a=.2 1 1 2 3 2 2 4 5 6 7 3 7 0 5
(={:)\ a
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
1 0 0 1 0 1 0 0 0 0 0 0 0 0 0
1 0 0 1 0 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 1 0 0 0 0 0 1
+/@(={:)\ a
1 1 2 2 1 3 4 1 1 1 1 2 2 1 2
Old thing using sort tricks: (1&,<;._1~1,1=i.~(]-{)/:@/:)
.
Mathematica, 58 51 49 bytes
Rest/@SplitBy[(f@#=0;#)&/@{a}~Join~#,++f[#]==3&]&
This is an unnamed function which takes a list like
Rest/@SplitBy[(f@#=0;#)&/@{a}~Join~#,++f[#]==3&]&[{2,1,1,2,3,2,2,4,5,6,7,3,7,0,5}]
and returns a nested list like
{{2, 1}, {}, {3, 2, 2, 4, 5, 6, 7}, {}, {0}, {}}
How it works
This uses some pretty obscure magic with SplitBy
.
I'm keeping track of the occurrences of each number in a function f
. In Mathematica, you can define the value of a function for each input separately, and you don't need to specify the value for all possible inputs (it's more like a hash table on steroids).
So I start out by initialising f
to 0 for values that are present in the input with (f@#=0;#)&/@
.
Now SplitBy
takes a list and a function and "splits list into sublists consisting of runs of successive elements that give the same value when f
is applied" (note that SplitBy
does not remove any elements). But the (undocumented) catch is, that f
is called twice on each element - when comparing it to its predecessor and its successor. So if we do
SplitBy[{1,2,3,4},Print]
we don't just get each number once, but instead this prints
1
2
2
3
3
4
which is 6 calls for 3 comparisons.
We can split the list before each second occurrence, if we write a function which always returns False
but returns True
when a second occurrence is compared to the element before it. That is the third check on that element (two checks on the first occurrence, plus the first check on the second occurrence). Hence, we use ++f[#]==3&
. The nice thing is that this already returns False
again on the second check of the second occurrence, such that I can return True
for consecutive second occurrences, but still split between them. Likewise, this won't split after second occurrences, because the function already returns False
again on the second check.
Now, the question wants us to also remove those second occurrences, so we drop the first element from each list, with Rest/@
. But of course, we don't want to remove the very first element in the input, so we actually start off, by adding an element a
to the beginning of the list with {a}~Join~#
. a
is an undefined variable, which Mathematica just treats as an unknown, so it won't affect any other values of f
. This also ensures that the first actual element in the input gets its two checks like every other element.