7
$\begingroup$

Given a list with repeated elements, for example:

list = {m, i, s, s, i, s, s, i, p, p, i}

I'd like to determine, for each position in the list, how many times the element at that position has appeared in the list up to and including that position.

That is, I'd like to define a function f such that, for my example list above,

f[list] = {1, 1, 1, 2, 2, 3, 4, 3, 1, 2, 4}

Notice that

  • f[list]〚1〛= 1 because position 1 is the first m in the list
  • f[list]〚5〛= 2 because position 5 is the second i in the list
  • f[list]〚7〛= 4 because position 7 is the fourth s in the list
  • f[list]〚9〛= 1 because position 9 is the first p in the list
  • etc.

This seems like the kind of thing there could well be a built-in for already, but I wouldn't have any guesses as to what it would be called or how to search for it. If not, is there a simple way to implement it? (Probably involving MapIndexed, I'd guess.)

$\endgroup$
1
  • 7
    $\begingroup$ Table[Count[Take[list, j], list[[j]]], {j, 1, Length@list}] $\endgroup$
    – cvgmt
    Commented Nov 30, 2023 at 2:36

8 Answers 8

8
$\begingroup$
Clear["Global`*"];
list = {m, i, s, s, i, s, s, i, p, p, i};

Count[list[[1 ;; #]], list[[#]]] & /@ Range@Length@list

Length /@ (DeleteCases[list[[1 ;; #]], Except[list[[#]]]] & /@ 
   Range@Length@list)

MapIndexed[Count[list[[1 ;; First@#2]], #1] &, list]

ReplaceList[list, {x__, y___} -> {x}] // Map[Count[#, Last@#] &]

Length /@ (PositionIndex[list[[1 ;; #]]][list[[#]]] & /@ 
   Range@Length@list)

TakeList[list, {#}] & /@ Range@Length@list // Map[First] // 
 Map[Count[#, Last@#] &]

Reverse@ReplaceList[
  Reverse@list, {___, x_, y___} :> Count @@ {{x, y}, x}]

Result:

{1, 1, 1, 2, 2, 3, 4, 3, 1, 2, 4}

$\endgroup$
4
  • 2
    $\begingroup$ MapIndexed[Count[list[[1 ;; First@#2]], #1] &, list] is the one I was halfway to formulating in my head. Not surprised that there are many possible approaches…but I am surprised that none of them is very legible! Assuming that nobody comes along with an even cleverer idea than any of these, that is. $\endgroup$ Commented Nov 30, 2023 at 3:53
  • $\begingroup$ Of these, do you have a favorite? $\endgroup$ Commented Nov 30, 2023 at 4:01
  • 2
    $\begingroup$ Depends on the time of the day or the phase of the moon. $\endgroup$
    – Syed
    Commented Nov 30, 2023 at 4:02
  • $\begingroup$ The Count[list〚1 ;; #〛, list〚#〛] & option is what I ultimately went with as it was, IMO, the clearest, from among all the options everyone presented, to see at a glance what it was doing. $\endgroup$ Commented Dec 1, 2023 at 14:20
12
$\begingroup$
accumulateCounts := Module[{c$}, c$[_] = 0; Map[PreIncrement@*c$]]

accumulateCounts @ {m, i, s, s, i, s, s, i, p, p, i}
{1, 1, 1, 2, 2, 3, 4, 3, 1, 2, 4}
$\endgroup$
6
$\begingroup$
list = {m, i, s, s, i, s, s, i, p, p, i};

Permute[Flatten[Ordering /@ Split@Sort@#], Ordering@#]&@list

(* {1, 1, 1, 2, 2, 3, 4, 3, 1, 2, 4} *)
$\endgroup$
6
$\begingroup$

Using an Association - counter:

list = {m, i, s, s, i, s, s, i, p, p, i};

c = <|# -> 0 & /@ Union @ list|>;

++c[#] & /@ list

{1, 1, 1, 2, 2, 3, 4, 3, 1, 2, 4}

Thank you, AsukaMinato, for proposing the following definition of c:

Module[{c = AssociationMap[0 &] @ list}, ++c[#] & /@ list]

{1, 1, 1, 2, 2, 3, 4, 3, 1, 2, 4}

$\endgroup$
3
  • 2
    $\begingroup$ c = list // AssociationMap[0&]; also works $\endgroup$ Commented Nov 30, 2023 at 22:16
  • $\begingroup$ A very nice short form, thank you $\endgroup$
    – eldo
    Commented Nov 30, 2023 at 23:13
  • 3
    $\begingroup$ slightly shorter: c = 0 Counts @ list. $\endgroup$
    – kglr
    Commented Dec 1, 2023 at 7:17
5
$\begingroup$

Just more variations...

Normal[SparseArray[Flatten[MapIndexed[#1 -> #2[[1]] &] /@ Values@PositionIndex[list]]]]

Values[SortBy[Flatten[MapIndexed[#1 -> #2[[1]] &] /@ Values@PositionIndex[list]], First]]
$\endgroup$
4
$\begingroup$

Another way using PositionIndex and ReplacePart:

pos = Values[PositionIndex[list]];
rep = Catenate[Thread /@ Thread[# -> Range[Length@#] & /@ #] &@pos];
ReplacePart[list, rep]

(*{1, 1, 1, 2, 2, 3, 4, 3, 1, 2, 4}*)
$\endgroup$
3
$\begingroup$
 MapThread[Replace[#1,#2]&,{list,Counts@Take[list,#]&/@Range@Length@list}]


(* {1,1,1,2,2,3,4,3,1,2,4} *)
$\endgroup$
3
$\begingroup$
list = {m, i, s, s, i, s, s, i, p, p, i};

A hash-free variant of lericr's 2nd answer

Flatten @ Values @ Sort @ Flatten @
    Map[
     MapIndexed[Rule],
     Values @ PositionIndex @ list]

{1, 1, 1, 2, 2, 3, 4, 3, 1, 2, 4}

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.