8

I have this list:

a={1,1,1,1,2,2,2,2,2,4,4,4,4,6,6,6,6,7,7,7,7}

Now I would like to create a list with the ranges of positions of all same numbers, hence:

range = {{1,4},{5,9},{10,13},{14,17},{18,21}}

How can I do it ?

james
  • 3,043
  • 13
  • 29

6 Answers6

7
Values[PositionIndex[a]][[All, {1, -1}]]

{{1, 4}, {5, 9}, {10, 13}, {14, 17}, {18, 21}}

LCarvalho
  • 9,233
  • 4
  • 40
  • 96
Coolwater
  • 20,257
  • 3
  • 35
  • 64
4
a = {1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 7, 7, 7, 7};

SplitBy[Transpose[{#, Range@Length@#}], First][[;; , {1, -1}, 2]] &@a
{{1, 4}, {5, 9}, {10, 13}, {14, 17}, {18, 21}}
Kuba
  • 136,707
  • 13
  • 279
  • 740
3

Here's a solution based on this old gem:

# + {1, 0} & /@ Partition[FoldList[Plus, 0, Length /@ Split[a]], 2, 1]
   {{1, 4}, {5, 9}, {10, 13}, {14, 17}, {18, 21}}
J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
3
list = 
  {1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 7, 7, 7, 7};

Using SequencePosition (new in 10.1)

SequencePosition[list, {a_, a_ ..}, Overlaps -> False]

{{1, 4}, {5, 9}, {10, 13}, {14, 17}, {18, 21}}

Addendum

As lericr commented it might be better to use Repeated. To show the difference we prepend a 0 to list:

list = {0, 1, 1, 2, 2, 4, 4, 4, 6, 6, 6, 6, 7, 7};

SequencePosition[list, {a_, a_ ..}, Overlaps -> False]

{{2, 3}, {4, 5}, {6, 8}, {9, 12}, {13, 14}}

SequencePosition[list, {Repeated[a_]}, Overlaps -> False]

{{1, 1}, {2, 3}, {4, 5}, {6, 8}, {9, 12}, {13, 14}}

eldo
  • 67,911
  • 5
  • 60
  • 168
  • 1
    SequencePosition[a, {Repeated[x_]}, Overlaps -> False] might be better, as it works with "singleton" sequences, e.g. {1,1,1,2,3,4,4...}. Of course, we don't know whether that's relevant in OP's situation. – lericr Jan 14 '24 at 19:27
  • Thank you, lericr, I updated the answer with your suggestion – eldo Jan 14 '24 at 23:24
3
list = {1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 7, 7, 7, 7};

Another way is to use Position and MinMax:

Function[x, MinMax@Position[list, x], Listable]@Union@list

({{1, 4}, {5, 9}, {10, 13}, {14, 17}, {18, 21}})

Or using SequenceCases:

SequenceCases[list, {a_ ..} :> MinMax@Position[list, a]]

({{1, 4}, {5, 9}, {10, 13}, {14, 17}, {18, 21}})

The above approach with SequenceCases works very well if the list is not of type {1, 2, 1, 2, 1, 2, 1, 2, 1, 4, 4, 4, 4, 6, 6, 6, 6, 7, 7, 7, 7}, but in the latter case we must add DeleteDuplicates, as pointed out by @eldo.

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44
  • 1
    +1, but I would use DeleteDuplicates (to see what I mean, exchange 2s and 1s) – eldo Jan 14 '24 at 18:27
  • Interesting observation. By interleaving 2 and 1 up to position 9 and leaving the rest unchanged, we must apply DeleteDuplicates and one of the lists with odd positions is for 1 and the other list with even positions is for 2. This is more general, I like it. :-) – E. Chan-López Jan 14 '24 at 18:39
1
Clear["Global`*"];
a = {1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 7, 7, 7, 7};

b1 = {0}~Join~a // Differences // Unitize // Position[#, 1] & // 
  Flatten

b2 = Length /@ Split[a] // Accumulate
Transpose[{b1, b2}]

{{1, 4}, {5, 9}, {10, 13}, {14, 17}, {18, 21}}

Syed
  • 52,495
  • 4
  • 30
  • 85