Newbie: changing an attribute of objects in a list

Alex Martelli aleax at aleax.it
Fri Jun 13 15:47:41 CEST 2003


<posted & mailed>

Ben Simuyandi wrote:

> I'm very sorry about the confusion. It comes from mistyping, and not
> thinking through my problem.
> 
> The first problem is from me not entering the number correctly. As you
> say, it should be a.order = 8 and g.order =9.
> 
> As for the second problem, this is because I had not thought about
> *exactly* what I want. What I would like is for the .age to be compared if
> any items have matching .order values, and the object with the larger .age
> value to have its .order value increased by one.
> 
> for example:
> if d.order and e.order match
>    compare d.age and e.age
>       if d.age > e.age
>         d.order = d.order + 1
>       else if d.age < e.age
>         e.order = e.order + 1
> 
> But after that, all objects with a .order value matching or greater than
> the changed .order value with have to be increased by one as well, so that
> all items still have a unique .order value. I don't actually need to
> compare .age values any more.

I suggest you think about the problem in a slightly different way, which
I believe should produce similar results but in a conceptually simpler
and speedier way:

step 1: "sort all objects by increasing order (primary) and age (secondary)"

step 2: "reassign the values of the order field to ensure uniqueness"

These two steps would leave your objects sorted by order and age rather
than in whatever sequence you initially had them in.  If that sequence
is important to you, then you need to keep track of that too, and add
a third and final step reordering everything in sequence; or else, use
the indices into the sequence rather than the items themselves, so the
order of the items never actually gets perturbed.

Each sorting step should use the DSU (Decorate-Sort-Undecorate) idiom
that is very well covered in the sorting and searching chapter of the
Python Cookbook and also exemplified in Python in a Nutshell.

Here is how I would approach a solution (warning: untested code):


Prerequisite: the argument items is a nonempty list of objects which have
  comparable fields named age (arbitrary type) and order (integer).

def updateOrder(items):
    aux = [ (items[i].order, items[i].age, i) for i in range(len(items)) ]
    aux.sort()
    curr_order = aux[0][0]
    for order, age, i in aux[1:]:
        if order <= curr_order:
            order = curr_order+1
            items[i].order = order
        curr_order = order

On exit ensures: the sequence of items is not affected.  Each item's .order
field is unique within the list.  If two items in the list x,y were such
that x.order < y.order before updateOrder was called, the same condition
still applies after updateOrder exits.  If two items in the list x, y were
such that x.order == y.order before updateOrder was called, then after
updateOrder exits x.order < y.order if and only if x.age < y.age, or if
x.age == y.age and x precedes y in the list's sequence; otherwise (x.age >
y.age, or x.age == y.age and x follows y in the list's sequence), then
x.order > y.order after updateOrder exits.  No item's .age nor any other
attribute is modified, except that each item's .order attribute _is_
modified but only where such modification is indispensable to assure
the rest of these post-conditions.


Alex





More information about the Python-list mailing list