I'd write it like this:
bin = {}
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
counters = bin.setdefault(week, [0, 0])
if full:
counters[0] += 1
else:
counters[1] += 1
for week, (times_full, times_not_full) in bin.iteritems():
print ...
Seriously, use setdefault() as often as you can. It may be a little
awkward at first, but after a little bit, you instantly know what it
means. It takes out a whole if/else statement which will make your code
smaller and more readable at the same time.
The list is actually representing a struct. Regardless of how it
smells, it's tight, small, and elegant. (This is the sort of problem
where Python shines.) I do stuff like this all the time. Lists are so
lightweight that there really isn't a better way to solve this problem.
Anything else either involves more code or is less readable, in my
opinion.
Using an idea you used earlier, you could get smaller code by saying:
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
counters = bin.setdefault(week, [0, 0])
counters[not full] += 1
Or
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
bin.setdefault(week, [0, 0])[not full] += 1
Or even
for start, end, AS, full in heard:
bin.setdefault(int((start-startDate)/aWeek), [0, 0])[not full] += 1
While smaller, those are all too tricky for my taste. I think they're
harder to read. I'd personally use the if/else. YMMV.
Using lists to represent structs is perfectly fine if the list doesn't
live longer than about one screen of code. If the list-struct gets
returned from a top-level function and gets used elsewhere, then you
should start to worry. It's too easy to forget what each element is
supposed to represent if you have to scroll your editor to see the place
where the list was created. That, in my opinion, would smell fishy. In
that case, you should probably represent your struct with a class or a
dict so that each element has a name. On the flip side, using such a
heavy solution for such a simple problem also smells bad. The solution
should should be as complicated as the problem -- no more, no less.
Adam
Randy said:
bin = {}
for whatever:
for [a, b] in foo:
x = 42 - a
if bin.has_key(x):
bin[x.b] += 1
else:
bin[x.b] = 1
bin[x.not b] = 0
for x, y, z in bin.iteritems():
print x, y, z
should the dict value become a two element list, or is there a
cleaner way to do this?
It would probably help if you explained what the real problem is
you're trying to solve.
actually, that code fragment was meant to do that. it's pretty much
what i needed to do at that point, just the variable names made
simple.
Using a two element list to store a pair of counts has a bad code
smell to me.
exactly. which is why i was asking.
That said, you could write your code something like:
bin = {}
for whatever:
# NOTE: brackets are unnecessary
for a, b in foo:
x = 42 - a
# NOTE: 'in' is generally faster than has_key()
if x in bin
bin[x][0] += 1
else:
bin[x] = [1, 0]
# NOTE: extra parens necessary to unpack count list
for x, (y, z) in bin.iteritems():
print x, y, z
so, to do this using the real names, it looks like
for [start, end, AS, full] in heard:
week = int((start-startDate)/aWeek)
if week in bin:
if full:
bin[week][0] += 1
else:
bin[week][1] += 1
else:
if full:
bin[week] = [1, 0]
else:
bin[week] = [0, 1]
...
for i, (j, k) in bin.iteritems():
if j == 0:
print str(i) + ",," + str(k)
elif k == 0:
print str(i) + "," + str(j)
else:
print str(i) + "," + str(j) + "," + str(k)
which is still pretty darned grotty and unexpressive. of course,
i could be a bit more obscure and do
if week in bin:
bin[week][not full] += 1
else:
bin[week] = [ full, not full ]
except i probably have to coerce the types or something. less
code but less obvious.
randy