views:

498

answers:

4

Hello. I have an array with subjects and every subject has connected time. I want to compare every subjects in the list. If there are two of the same subjects, I want to add the times of both subjects, and also want to delete the second subject information (subject-name and time). But If I delete the item, the list become shorter, and I get an out-of-range-error. I tried to make the list shorter with using subjectlegth-1, but this also don't work.

   ...
   subjectlegth = 8
   for x in range(subjectlength):
        for y in range(subjectlength):
            if subject[x] == subject[y]:
                if x != y:
                    #add
                    time[x] = time[x] + time[y]
                    #delete
                    del time[y]
                    del subject[y]
                    subjectlength = subjectlength - 1
+3  A: 

Iterate backwards, if you can:

for x in range(subjectlength - 1, -1, -1):

and similarly for y.

Chris Jester-Young
:) it works! thanks a lot!
kame
While this "solves" your problem of how to delete elements of a list while iterating over it, your bigger problem is that you are in fact modifying a list while iterating over it. Whenever you are considering doing this, step back and see if you can readdress the problem by constructing a new list with the desired values. In this case, you are gathering together time values for like subjects - think "dict", which is a much better structure for this. If using Python 2.5 or later, think "defaultdict", which is even easier to use than dict.get(). Ignacio Vazquez-Abrams' answer is the right one.
Paul McGuire
+2  A: 

The best practice is to make a new list of the entries to delete, and to delete them after walking the list:

to_del = []
subjectlength = 8
for x in range(subjectlength):
    for y in range(x):
        if subject[x] == subject[y]:
            #add
            time[x] = time[x] + time[y]
            to_del.append(y)

to_del.reverse()
for d in to_del:
    del subject[d]
    del time[d]
Ned Batchelder
+6  A: 

If the elements of subject are hashable:

finalinfo = {}

for s, t in zip(subject, time):
  finalinfo[s] = finalinfo.get(s, 0) + t

This will result in a dict with subject: time key-value pairs.

Ignacio Vazquez-Abrams
+1 - This is the *real* solution to the original problem.
Paul McGuire
+1  A: 

An alternate way would be to create the subject and time lists anew, using a dict to sum up the times of recurring subjects (I am assuming subjects are strings i.e. hashable).

subjects=['math','english','necromancy','philosophy','english','latin','physics','latin']
time=[1,2,3,4,5,6,7,8]
tuples=zip(subjects,time)
my_dict={}
for subject,t in tuples:
    try:
        my_dict[subject]+=t
    except KeyError:
        my_dict[subject]=t
subjects,time=my_dict.keys(), my_dict.values()
print subjects,time
MAK
Learn about dict.get() or even better, collections.defaultdict. Much cleaner than try-except KeyError. And why print out keys() and values()? What about items()?
Paul McGuire
I know about get and defaultdict, but wasn't sure if the OP was so I stuck with an approach that assumes less knowledge. As for keys() and values(), the OP wants two separate lists and items() returns a list of (key,value) tuples.
MAK
@MAK - Fair enough, insofar as you are perturbing the OP's original design as little as possible. But a program that keeps matched data values in separate lists *really* should be redone to use a dict. What if the design calls for adding an instructor, or a room number? Should these be added by tacking on more order-sensitive lists? You do the OP a disservice by assuming that dict concepts are beyond him/her.
Paul McGuire
@Paul McGuire: Those are things you should be saying to the OP, not me. I am not assuming dict concepts are beyond him, I am guarding against the possibility that he might not be familiar enough with dicts yet.
MAK