how can I avoid abusing lists?

T

Thomas Nelson

I have this code:
type1 = [0]
type2 = [0]
type3 = [0]
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2} # the real map is
longer than this

def increment(value):
map[value][0] += 1

increment(1)
increment(1)
increment(0)
increment(4)
#increment will actually be called many times through other functions
print type1[0], type2[0], type3[0]
#should print "3 1 0"

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?

Thanks,
THN
 
R

Rob Cowie

Just forget the lists...

counters = {0:0, 1:0, 2:0, 3:0, 4:0}

def increment(value):
counters[value] += 1

increment(1)
increment(1)
increment(3)
increment(4)

print counters[0]
0 print counters[1]
2 print coutners[2]
0 print counters[3]
1 print coutners[4]
1

The increment function should probably include a try:...except:
statement to catch KeyErrors that would arise if you passed a value
that is not a key in the counters dictionary.

Rob C
 
S

Simon Forman

Thomas said:
I have this code:
type1 = [0]
type2 = [0]
type3 = [0]
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2} # the real map is
longer than this

def increment(value):
map[value][0] += 1

increment(1)
increment(1)
increment(0)
increment(4)
#increment will actually be called many times through other functions
print type1[0], type2[0], type3[0]
#should print "3 1 0"

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?

Thanks,
THN

In this case, lists are unnecessary. Just use ints.

Since your "type codes" are ints, you can create your map like this
(assuming 10 types):

map = dict((n, 0) for n in range(10))


Then your increment function becomes:

def increment(value):
map[value] += 1


And instead of,
print type1[0], type2[0], type3[0]

say,
print map[0], map[1], map[2]


Peace,
~Simon
 
J

Justin Azoff

Thomas said:
This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?

Thanks,
THN

Just assign each type a number (type1 -> 1, type2 -> 2) and then count
the values as usual

def count(map, it):
d={}
for x in it:
x = map[x] #only difference from normal count function
#d[x]=d.get(x,0)+1
if x in d:
d[x] +=1
else:
d[x] = 1
return d
map = {0:1, 1:1, 2:3, 3:1, 4:2}
count(map, [1,1,0,4]) {1: 3, 2: 1}
for x in count(map, [1,1,0,4]).items():
.... print 'type%d: %d' %x
....
type1: 3
type2: 1
 
T

Tim Chase

Just forget the lists...
counters = {0:0, 1:0, 2:0, 3:0, 4:0}

You'll notice that the OP's code had multiple references to the
same counter (0, 1, and 3 all mapped to type1)

The OP's method was about as good as it gets. One might try to
redo it with an accumulator class of some sort:

class Accumulator(object):
def __init__(self, startvalue = 0):
self.counter = startvalue
def __iadd__(self, qty):
self.counter += qty
return self.counter
def add(self, qty = 1):
self.counter += qty
def __int__(self):
return self.counter
def __str__(self):
return str(self.counter)
def __repr__(self):
return '<Accumulator 0x%x (%s)>' % (
id(self), str(self.counter))

type1 = Accumulator()
type2 = Accumulator()
type3 = Accumulator()
d = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2}

print ','.join([str(x) for x in d.values()])
# all zeros
d[0] += 1
print ','.join([str(x) for x in d.values()])
# d[0], d[1], and d[3] have incremented
d[2].add()
d[2].add()
print ','.join([str(x) for x in d.values()])
# d[2] has now incremented twice
d[4].add(5)
print ','.join([str(x) for x in d.values()])
# d[4] has now incremented by 5

Some of the syntactic sugar of the class could likely be left out
if you just want, but it does the same thing as the OP's, with a
diff. spin on the syntax.

-tkc
 
B

Bruno Desthuilliers

Thomas said:
I have this code:
type1 = [0]
type2 = [0]
type3 = [0]
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2}

Warning : you're shadowing the builtin map() function.
# the real map is
longer than this

def increment(value):
map[value][0] += 1

increment(1)
increment(1)
increment(0)
increment(4)
#increment will actually be called many times through other functions
print type1[0], type2[0], type3[0]
#should print "3 1 0"

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists.

Not really.
Does anyone know a
cleaner way to have the same funtionality?


# first replace the list hack
class Counter(object):
def __init__(self, name, *keys):
self.name = name
self.keys = keys
self.count = 0

def inc(self):
self.count += 1

# now wrap the whole thing in a convenient way
class Counters(object):
def __init__(self, *counters):
self._counters = dict()
self._counters_map = dict()
for counter in counters:
assert counter.name not in self._counters
self._counters[counter.name] = counter
for key in counter.keys:
assert key not in self._counters_map
self._counters_map[key] = counter

def __getattr__(self, name):
return self._counters[name].count

def __getitem__(self, key):
return self._counters_map[key].count

def __call__(self, key):
self._counters_map[key].inc()

# and finally, let's use it:
increment = Counters(Counter("type1", 0, 1, 3),
Counter("type2", 4),
Counter("type3", 2)
)

increment(1)
increment(1)
increment(0)
increment(4)

print increment.type1, increment.type2, increment.type3


HTH
 
M

mensanator

Thomas said:
I have this code:
type1 = [0]
type2 = [0]
type3 = [0]
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2} # the real map is
longer than this

def increment(value):
map[value][0] += 1

increment(1)
increment(1)
increment(0)
increment(4)
#increment will actually be called many times through other functions
print type1[0], type2[0], type3[0]
#should print "3 1 0"

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?

I usually do this:
for j in range(1,7):
s = i + j
if histogram.has_key(s):
histogram += 1
else:
histogram = 1
print i,histogram

2 1
3 2
4 3
5 4
6 5
7 6
8 5
9 4
10 3
11 2
12 1

Note that only results actually encountered create dictionary
entries, so there is no result for index 0 or 1.
 
K

Klaus Alexander Seistrup

if histogram.has_key(s):
histogram += 1
else:
histogram = 1


I wonder if

histogram = histogram.get(s, 0) + 1

would be more efficient...

Cheers,
 
P

Peter Otten

Thomas said:
I have this code:
type1 = [0]
type2 = [0]
type3 = [0]
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2} # the real map is
longer than this

def increment(value):
map[value][0] += 1

increment(1)
increment(1)
increment(0)
increment(4)
#increment will actually be called many times through other functions
print type1[0], type2[0], type3[0]
#should print "3 1 0"

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?

I don't think your code is ugly. Anyway, here are two more alternatives:
types = [0] * 3
dispatch = [0, 0, 2, 0, 1]
for value in [1, 1, 0, 4]:
.... types[dispatch[value]] += 1
....[3, 1, 0]

inflated = [0] * 5
groups = [[0, 1, 3], [4], [2]]
for value in [1, 1, 0, 4]:
.... inflated[value] += 1
....
[sum(inflated for i in group) for group in groups]

[3, 1, 0]

Peter
 
J

Jon Ribbens

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?

How about this:

map = {}
def increment(value):
map[value] = map.get(value, 0) + 1

?
 
S

Simon Forman

Tim said:
You'll notice that the OP's code had multiple references to the
same counter (0, 1, and 3 all mapped to type1)

The OP's method was about as good as it gets. One might try to

D'oh! Didn't notice that.

Yeah, Thomas, if you really do want more than "type code" (i.e. key to
your map dict) to map to the same type counter then IMHO your original
method is very apt.

Peace,
~Simon
 
T

Thomas Nelson

Thanks to everyone who posted. First, I don't think my question was
clear enough: Rob Cowie, Ant, Simon Forman, (e-mail address removed), and Jon
Ribbens offered solutions that don't quite work as-is, because I need
multiple values to map to a single type. Tim Chase and Bruno
Destuilliers both offer very nice OOP solutions, and I think this is
the route I will probably go. However, for the simplest and easiest
solution, I really like this from Peter Otten:
inflated = [0]*5
groups = [[0,1,3],[4],[2]]
for value in [1,1,0,4]:
inflated[value] += 1
print [sum(inflated for i in group) for group in groups]

4 lines (one more to assign the lists to name values, but that's
minor), and intuitive. If I had just thought of this to begin with, I
wouldn't have bothered posting.

Thanks to all for the advice.

THN
 
R

Rob Cowie

No, your question was clear. With hindsght and a more thorough read of
your post I see my error ;^)
 
S

Simon Forman

Thomas said:
Thanks to everyone who posted. First, I don't think my question was
clear enough: Rob Cowie, Ant, Simon Forman, (e-mail address removed), and Jon
Ribbens offered solutions that don't quite work as-is, because I need
multiple values to map to a single type. Tim Chase and Bruno
Destuilliers both offer very nice OOP solutions, and I think this is
the route I will probably go. However, for the simplest and easiest
solution, I really like this from Peter Otten:
inflated = [0]*5
groups = [[0,1,3],[4],[2]]
for value in [1,1,0,4]:
inflated[value] += 1
print [sum(inflated for i in group) for group in groups]

4 lines (one more to assign the lists to name values, but that's
minor), and intuitive. If I had just thought of this to begin with, I
wouldn't have bothered posting.

Thanks to all for the advice.

THN


I want to be sure I understand the above.

inflated is a list of counters, one for each "type code"?

groups is a list of groups of "type codes", one for each actual "type"?

The for loop simulates four calls to an "increment()" function?

The list comprehension sums all the counters that correspond to each
actual type?

so you could write:

type1, type2, type3 = groups = [[0,1,3],[4],[2]]

Is that right?


Wow, very nice.


Peace,
~Simon


It was too easy to read
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2}
as
map = {0:type1, 1:type2, 2:type3, 3:type4, 4:type5}

sorry.
 
B

Bruno Desthuilliers

Peter Otten a écrit :
(snip)
I don't think your code is ugly. Anyway, here are two more alternatives:

types = [0] * 3
dispatch = [0, 0, 2, 0, 1]
for value in [1, 1, 0, 4]:
... types[dispatch[value]] += 1
...

[3, 1, 0]

I wonder why I'm still pretending to be a programmer
inflated = [0] * 5
groups = [[0, 1, 3], [4], [2]]
for value in [1, 1, 0, 4]:
... inflated[value] += 1
...
[sum(inflated for i in group) for group in groups]


[3, 1, 0]


me-change-job-me-go-selling-pizzas :(
 
J

Jim Segrave

Just forget the lists...

counters = {0:0, 1:0, 2:0, 3:0, 4:0}

def increment(value):
counters[value] += 1

increment(1)
increment(1)
increment(3)
increment(4)

print counters[0]
0 print counters[1]
2 print coutners[2]
0 print counters[3]
1 print coutners[4]
1

The increment function should probably include a try:...except:
statement to catch KeyErrors that would arise if you passed a value
that is not a key in the counters dictionary.

Rob C

counters = {}
def increment(value):
counters[value] = counters.get(value, 0) + 1

increment(1)
increment(1)
increment(3)
increment(4)
increment('a string key')


keyz = counters.keys()
keyz.sort()
for k in keyz:
print k, counters[k]

Takes care of IndexError and ValueError. It does not report keys that
don't get incremented. If that's important, then initalise counters as
in the quoted posting.

For Python 2.4 and later, you can replace the keyz =
counts.keys()/keyz.sourt() for k in keyz: with

for k in sorted(counters.heys()):
print k, counters[k]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top