2

I have a list

l = [object0, object1, object2, object3, object4....object499]

maximum length is never more than 500

All objects have attribute x, y, z, a

I have to modify object.y if duplicates in object.x as:

if object9.x == object10.x:
  object9.y = object9.z * object9.a/1000
  object10.y = object10.y - object9.z * object9.a/1000

It is guaranteed that duplicates will be in consecutive objects.

There may be more than 2 duplicates ie for example

object12.x == object13.x == object14.x

so modification will proceed in the same manner of the third duplicate based on the value of modified 2nd duplicate.

I have written a loop to do it, but was thinking if there is any pythonic/faster way of doing it. I am using python3.7

EDIT:

tag = None
for i, o in enumerate(l):
  if tag is None:
     x_a = o.x
  elif x_a == o.x #duplicate found
    temp = o.y
    c_over = 0
    c_value = o[i-1].z * o[i-1].a/1000
    if c_value < o[i-1].y:
       o[i-1].y = c_value
       c_over = temp - c_value #carry over value
    o[i] = c_over # either zero of carry over value
  x_a = o.x 
4
  • 3
    Could you show your existing loop so others may be able to suggest how to improve it, or otherwise suggest alternative methods? Commented Mar 13, 2020 at 10:17
  • 3
    You want itertools.groupby, with operator.attrgetter('x') as the key function. Commented Mar 13, 2020 at 10:21
  • 1
    You can iterate on couples of your list with for (prev, current) in zip(l, l[1:]): Commented Mar 13, 2020 at 10:23
  • Right now enumerating on list and if duplicate found then modify the previous element. posting the code of the loop Commented Mar 13, 2020 at 10:32

2 Answers 2

3

Since you're modifying attributes of your objects you can loop over consecutive pairs via zip:

for a, b in zip(l, l[1:]):
    if a.x == b.x:
        a.y = a.z * a.a/1000
        b.y = b.y - a.y
Sign up to request clarification or add additional context in comments.

4 Comments

what makes it a bit more tricky than that is that there might be a c for which b.x == c.x but since you are modifying b before checking c you are missing it.
@Ev.Kounis b.x is never modified so nothing is missed here. x is used for the check and y gets modified.
You are right. There is a bit of ambiguity there in the question come to think of it a bit harder. It is not clear to me if the modification takes place from the beginning or the end of the group. Assuming a triplet of (a, b, c), the modification of c will take place with the already modified value of b in both your and my answer.
@Ev.Kounis That's explicitly required by the OP: "[...] so modification will proceed in the same manner of the third duplicate based on the value of modified 2nd duplicate."
3

@Shadow Ranger's comment is definitely the way to go here. I did not test this any but it should do the trick

import itertools
import operator


l = [object0, object1, object2, object3, object4]

for k, g in itertools.groupby(l, key=operator.attrgetter('x')):
    l = list(g)
    for o1, o2, in zip(l, l[1:]):  # Note 1, 2
        o1.y = o1.z * o1.a / 1000
        o2.y = o2.z - o1.y

Notes:

  1. assuming you modify the groups from beginning to the end. If you want to do it the other way round, you have to replace zip(l, l[1:]) with zip(l[len(l)-2::-1], l[::-1])
  2. kudos @ShadowRanger for pointing out that the if check is not needed (handled by the zip)

1 Comment

Note: You don't need to check the length (the zip will just terminate immediately if l[1:] is empty), and if you're running forwards, you could use itertools.tee, to avoid listifying at all (which makes it completely lazy; no realizing elements before you need them). g, g2 = itertools.tee(g), next(g2), for o1, o2 in zip(g, g2):

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.