455,539 Members | 1,289 Online
Need help? Post your question and get tips & solutions from a community of 455,539 IT Pros & Developers. It's quick & easy.

# how can I avoid abusing lists?

 P: n/a 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 Jul 7 '06 #1
16 Replies

 P: n/a 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 Jul 7 '06 #2

 P: n/a Rob Cowie wrote: Just forget the lists... counters = {0:0, 1:0, 2:0, 3:0, 4:0} Or perhaps just use a list: >>counters = [0,0,0,0]def inc(v): .... counters[v] += 1 .... >>inc(1)inc(1)inc(3)counters [0, 2, 0, 1] The increment function should probably include a try:...except: Likewise for IndexErrors Jul 7 '06 #3

 P: n/a Thomas Nelson wrote: 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 Jul 7 '06 #4

 P: n/a Thomas Nelson wrote: 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 Jul 7 '06 #5

 P: n/a 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 '' % ( 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 Jul 7 '06 #6

 P: n/a Thomas Nelson wrote: 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 -- bruno desthuilliers python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for p in 'o****@xiludom.gro'.split('@')])" Jul 7 '06 #7

 P: n/a Thomas Nelson wrote: 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: >>histogram = {}for i in range(1,7): for j in range(1,7): s = i + j if histogram.has_key(s): histogram[s] += 1 else: histogram[s] = 1 >>for i in histogram: print i,histogram[i] 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. > Thanks, THN Jul 7 '06 #8

 P: n/a me********@aol.com wrote: if histogram.has_key(s): histogram[s] += 1 else: histogram[s] = 1 I wonder if histogram[s] = histogram.get(s, 0) + 1 would be more efficient... Cheers, -- Klaus Alexander Seistrup Copenhagen, Denmark http://ipsum.dk/ Jul 7 '06 #9

 P: n/a Thomas Nelson wrote: 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] * 3dispatch = [0, 0, 2, 0, 1]for value in [1, 1, 0, 4]: .... types[dispatch[value]] += 1 .... >>types [3, 1, 0] >>inflated = [0] * 5groups = [[0, 1, 3], [4], [2]]for value in [1, 1, 0, 4]: .... inflated[value] += 1 .... >>[sum(inflated[i] for i in group) for group in groups] [3, 1, 0] Peter Jul 7 '06 #10

 P: n/a In article <11*********************@s13g2000cwa.googlegroups. com>, Thomas Nelson wrote: 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 ? Jul 7 '06 #11

 P: n/a Tim Chase wrote: > 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 Jul 7 '06 #12

 P: n/a Thanks to everyone who posted. First, I don't think my question was clear enough: Rob Cowie, Ant, Simon Forman, me********@aol.com, 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[i] 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 Jul 7 '06 #13

 P: n/a No, your question was clear. With hindsght and a more thorough read of your post I see my error ;^) Jul 7 '06 #14

 P: n/a Thomas Nelson wrote: Thanks to everyone who posted. First, I don't think my question was clear enough: Rob Cowie, Ant, Simon Forman, me********@aol.com, 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[i] 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. Jul 7 '06 #15

 P: n/a Peter Otten a écrit : (snip) I don't think your code is ugly. Anyway, here are two more alternatives: >>>>types = [0] * 3dispatch = [0, 0, 2, 0, 1]for value in [1, 1, 0, 4]: ... types[dispatch[value]] += 1 ... >>>>types [3, 1, 0] I wonder why I'm still pretending to be a programmer > >>>>inflated = [0] * 5groups = [[0, 1, 3], [4], [2]]for value in [1, 1, 0, 4]: ... inflated[value] += 1 ... >>>>[sum(inflated[i] for i in group) for group in groups] [3, 1, 0] me-change-job-me-go-selling-pizzas :( Jul 7 '06 #16

 P: n/a In article <11**********************@s16g2000cws.googlegroups .com>, Rob Cowie Just forget the lists...counters = {0:0, 1:0, 2:0, 3:0, 4:0}def increment(value): counters[value] += 1increment(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 valuethat 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] -- Jim Segrave (je*@jes-2.demon.nl) Jul 8 '06 #17

### This discussion thread is closed

Replies have been disabled for this discussion.