Assuming the following list {1,2,3,3,5,6,3} I would like to get the position of values that are next to each other and are duplicates. That is, the output should be {3,4}.
Thank you for any help.
Cheers, Vaclav
The following should be a fast and robust solution:
ClearAll@neighbouringDuplicates
neighbouringDuplicates[list_] :=
Select[SplitBy[Range@Length[list], list[[#]] &], Length@# > 1 &]
neighbouringDuplicates@{1, 2, 3, 3, 5, 6, 3}
(* {{3, 4}} *)
neighbouringDuplicates@{1, 2, 3, 3, 2, 5, 4, 6, 5, 5}
(* {{3, 4}, {9, 10}} *)
This is an example of the "transform by" pattern that Szabolcs also used in a related question to great effect.
Another straightforward solution:
DeleteDuplicates@Flatten[{#, # + 1} & /@ Position[Differences[{1, 2, 3, 3, 5, 6, 3}], 0]]
(* Out: {3,4} *)
Plenty fast (as in orders of magnitude faster than using split). Since question is a bit ambiguous, I treat anything beyond a pair as multiple pairs, e.g., in {1,2,2,2,3,3,4,5,6}, the 2,2,2 is treated as a pair of duplicates. Output is strictly pair positions.
test = {1, 1, 5, 6, 9, 5, 5, 5, 3, 4, 5, 5}
With[{pl = PadLeft[#, Length[#] + 1, Min[#] - 1]},
Replace[Pick[Range[Length[#]], Differences[pl], 0],
a_ :> {a - 1, a}, 1]] &[test]
(* {{1, 2}, {6, 7}, {7, 8}, {11, 12}} *)
Some timings (caveat - on netbook, cigar lounging):
test = RandomInteger[1000000, 5000000];
r1 = neighbouringDuplicates@test; // Timing // First
r4 = With[{pl = PadLeft[#, Length[#] + 1, Min[#] - 1]},
Replace[Pick[Range[Length[#]], Differences[pl], 0],
a_ :> {a - 1, a}, 1]] &[test]; // Timing // First
r1 == r4
(*
116.641948
0.358802
True
*)
So about 300X slower using split here.
Union and Transpose? (+1 by the way)
$\endgroup$
{} in results. Nice though!
$\endgroup$
Pick is now performing as it should. (You already have my +1.)
$\endgroup$
Here's a pretty straightforward way:
list = {1, 2, 3, 3, 2, 5, 4, 6, 5, 5};
tf = Position[Table[list[[i]] == list[[i - 1]], {i, 2, Length[list]}], True]
Or one can avoid the Table using the equivalent
Position[Equal[#[[1]], #[[2]]] & /@ Partition[list, 2, 1], True]
Both of these give a list of the positions where all the pairs begin. You can change this to the exact form you are looking for by
Riffle[Flatten[tf], Flatten[tf] + 1]
{3, 4, 9, 10}
If there are triples, then it is somewhat unclear what the desired output is. As Nasser points out, this can be fixed by applying DeleteDuplicates.
list = {1, 2, 3, 3, 3, 2}; it gives {3, 4, 4, 5} !Mathematica graphics
$\endgroup$
{3,4,5} and not {3,4,4,5} I would think. I think all what you need to do is just remove duplicate positions from last list. Good answer any way :)
$\endgroup$
Position[Partition[list, 2, 1], {x_, x_}] :)
$\endgroup$
Here is Pickett's method but using SparseArray Properties as a faster alternative to Position.
fn = Union[#, # + 1] &@
SparseArray[Unitize @ Differences @ #, Automatic, 1]["AdjacencyLists"] &;
fn @ {1, 1, 5, 6, 9, 5, 5, 5, 3, 4, 5, 5}
{1, 2, 6, 7, 8, 11, 12}
If position pairs with duplicates are acceptable (rasher's format), we can save a bit more time by using Transpose[{#, # + 1}] in place of Union[#, # + 1].
This (with Transpose) tests about an order of magnitude faster than rasher's method in version 7, but Pick was optimized in v8. It (with Union) tests two orders of magnitude faster than neighbouringDuplicates.
In versions 10.1+, you can use SequencePosition
neighborPositions = SequencePosition[#, {a_, a_}]&;
Examples:
l1 = {1, 2, 3, 3, 5, 6, 3} ;
l2 = {1, 2, 3, 3, 3, 2, 5, 4, 6, 5, 5};
{#, posF @ #} & /@ {l1, l2} // Grid[Prepend[#, {"list", "positions"}], Dividers -> All] &