# Accumulated instance count of each list element

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.)

• Table[Count[Take[list, j], list[[j]]], {j, 1, Length@list}] Commented Nov 30, 2023 at 2:36

## 8 Answers

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}

• 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. Commented Nov 30, 2023 at 3:53
• Of these, do you have a favorite? Commented Nov 30, 2023 at 4:01
• Depends on the time of the day or the phase of the moon.
– Syed
Commented Nov 30, 2023 at 4:02
• 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. Commented Dec 1, 2023 at 14:20
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}

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} *)


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}

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

Just more variations...

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

Values[SortBy[Flatten[MapIndexed[#1 -> #2[[1]] &] /@ Values@PositionIndex[list]], First]]


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}*)

 MapThread[Replace[#1,#2]&,{list,Counts@Take[list,#]&/@Range@Length@list}]

(* {1,1,1,2,2,3,4,3,1,2,4} *)

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}