14

Is there a pattern that can match repeated elements that appear more than twice in a row?

For the sake of example let's say I want to use pattern matching to delete subsequent duplicate:

l = {1,2,2,3,4};

l/.{b___,x_,x_,r___}->{b,r}

({1,3,4})

But

l = {1,2,2,2,3,4};

l/.{b___,x_,x_,r___}->{b,r}

({1,2,3,4})

I can try to use Repeated, but it doesn't seem to work:

l = {1, 2, 2, 2, 3, 4};

l /. {b___, x_, (x_) .., r___} -> {b, r}

({1,2,3,4})

Am I using it wrong?

Whelp
  • 1,715
  • 10
  • 21

7 Answers7

16

Use the Longest pattern command to find the longest repeated sequence that matches the pattern.

l = {1, 2, 2, 2, 3, 4};

l /. {b___, x_, Longest[(x_) ..], r___} :> {b, r}

({1, 3, 4})

TimRias
  • 3,160
  • 13
  • 17
  • That's helpful. However, when I try it on l={1,1,1,2} the result is {1,1,2}, and not {2} as I would expect. – Whelp Feb 04 '21 at 14:59
  • 1
    l={1,1,1,2} returns {2} when I test it. Are you sure that both the b and r have three blanks? – TimRias Feb 04 '21 at 15:02
  • My bad, I had missed the "repeated" on the Longest pattern. It does work! – Whelp Feb 04 '21 at 15:18
13

Another possibility is to use SequenceReplace:

SequenceReplace[{1, 2, 2, 2, 3, 4}, {x_, x_ ..} -> Sequence[]]

{1, 3, 4}

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
11

Try the following:

l = {1, 2, 2, 2, 3, 4};
l /. {b___, x_, (x_) .., r___} -> {{b}, {r}}
(* {{1}, {2, 3, 4}} *)

and you see that that r is ill-defined. Therefore we must restrict r:

l //. {b___, Repeated[x_, {2, 10}], Shortest@r___} :> {b, r}
(*{1, 3, 4}*)
J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
Daniel Huber
  • 51,463
  • 1
  • 23
  • 57
  • Very nice debugging trick. I have already given the tick mark, but this would deserve one as well. – Whelp Feb 04 '21 at 15:27
8

You might also consider using the two-argument form of Repeated for finer control:

{1, 2, 2, 3, 4}  /. {a___, Repeated[x_, {2, ∞}], b___} :> {a, b}
{1, 3, 4}
SequenceReplace[{1, 2, 2, 2, 3, 4}, {Repeated[x_, {2, ∞}]} -> Sequence[]]
{1, 3, 4}
kglr
  • 394,356
  • 18
  • 477
  • 896
7

Not pattern matching, but one other way:

Cases[Split@l2, {x_}:>x]

{1, 3, 4}

user1066
  • 17,923
  • 3
  • 31
  • 49
4
Cases[{a_, 1} :> a] @ Tally @ list

{1, 3, 4}

Or:

Keys @ Select[SameAs @ 1] @ Counts @ list

{1, 3, 8}

eldo
  • 67,911
  • 5
  • 60
  • 168
2

Using Select and Count:

l = {1, 2, 2, 2, 3, 4};

Select[l, Count[l, #] == 1 &]

({1, 3, 4})

Also, using ReplaceAll:

f = # /. x_Integer :> If[Count[#, x] == 1, x, Nothing] &;

f@l

({1, 3, 4})

Also, using the third argument of GroupBy and Lookup:

Lookup[GroupBy[Normal@Counts[l], #[[2]] == 1 &, #[[All, 1]] &], True]

({1, 3, 4})

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44