6

Given: a list, such as l=[4,4,4,4,5,5,5,6,7,7,7] Todo: get the count of an element and keep their occurrence order, e.g.: [(4,4),(5,3),(6,1),(7,3)]

I could do it with:

tmpL    = [(i,l.count(i)) for i in l]
tmpS    = set()
cntList = [x for x in tmpL if x not in tmpS and not tmpS.add(x)]

But is there a better way? I have seen the link here, but it sorts the counts and hence breaks the order.

Edit: performance is not an issue for the solution, preferable something built-in.

Community
  • 1
  • 1
lukmac
  • 4,617
  • 8
  • 33
  • 34

3 Answers3

11

Use groupby:

>>> l = [4,4,4,4,5,5,5,6,7,7,7,2,2]
>>> from itertools import groupby
>>> [(i, l.count(i)) for i,_ in groupby(l)]
[(4, 4), (5, 3), (6, 1), (7, 3), (2, 2)]
jro
  • 9,300
  • 2
  • 32
  • 37
  • but casting to set will change the order: >>> l=[7,7,7,7,7,4,4,4,5,5,6,6,6,6,6] >>> set(l) set([4, 5, 6, 7]) – lukmac Oct 26 '11 at 13:07
  • 3
    @lukmac Don't do it this way. `count` depends on the size of the whole list, not just the size of the group. Use Tim's method or the same general idea with `k, sum(1 for _ in g)` instead of `k, len(list(g))`. – agf Oct 26 '11 at 13:17
  • @agf: I updated the answer as it was already accepted, but I'm now wondering what the normal procedure is. When a question is accepted, is it generally agreed upon to include (minor) fixes to improved upon it? Or should the asking person change the acceptance? Did I do the right thing? – jro Oct 26 '11 at 13:21
  • It's fine to add fixes, but _not_ to change your answer to be exactly equivalent to an existing answer just because it's better. It doesn't matter whether the answer is accepted or not. The OP can change that if he wants. – agf Oct 26 '11 at 13:26
  • @agf: it became indeed identical, shame on me for updating in a rush. Rolled back to the initial answer. To lukmac: see the discussion above; please reaccept the answer. – jro Oct 26 '11 at 13:31
9
>>> import itertools
>>> [(k, len(list(g))) for k, g in itertools.groupby(l)]
[(4, 4), (5, 3), (6, 1), (7, 3)]

This keeps the order of the items and also allows repeated items:

>>> l=[4,4,4,4,5,5,5,6,7,7,7,4,4,4,4,4]
>>> [(k, len(list(g))) for k, g in itertools.groupby(l)]
[(4, 4), (5, 3), (6, 1), (7, 3), (4, 5)]
Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
2

If you are using Python 2.7, you can use the Counter from collections, which does exactly what you are looking to do:

http://docs.python.org/library/collections.html#collections.Counter

Krystian Cybulski
  • 10,789
  • 12
  • 67
  • 98
  • However, it doesn't keep the items in order (since it's implemented as a dictionary), which doesn't fulfill the OP's requirements. – Tim Pietzcker Oct 26 '11 at 13:18