1

I have a list of objects that are a specific class within my code like so,

[object1, object2, object3, object4, object5, object6]

Namely this class has two attributes: class.score and class.id

I might have objects with the same id. Eg.:

[object1.id, object2.id, object3.id, object4.id, object5.id, object6.id] = [1, 2, 3, 4, 2, 3]

But with different scores. Eg.:

[object1.score, object2.score, object3.score, object4.score, object5.score,
object6.score] = [0.25, 0.55, 0.6, 0.4, 0.30, .33]

What I want to do is to have list with no duplicates of this objects id-wise but adding the scores. So for the previous example the output would be:

[object1.id, object2.id, object3.id, object4.id] = [1, 2, 3, 4]
[object1.score, object2.score, object3.score, object4.score] = [.25, .85, .93, .4]

I have managed to do that with two for loops:

k = 1
    for object in list_of_objects:
        j = 1
        for object2 in list_of_objects:
            if object.id == object2.id and j > k:
                object.score = object.score + object2.score
                list_of_objects.remove(object2)
            j += 1
        k += 1

But I'm looking to do it in a more python-ish, way something along the lines of:

newlist[:] = [ x for x in list_of_objects if certain_condition(x)]

Thanks.

4
  • Do you care about the order the final list is in? Commented Aug 12, 2015 at 6:33
  • 1
    how can two same objects have different attributes? Commented Aug 12, 2015 at 6:36
  • @RenaeLider, I don't think it's there Python id(), it's just an attribute called id. I don't think the repeats in that first list are intended to mean it's the same object (though certainly it does imply that on its own). Commented Aug 12, 2015 at 6:39
  • @Cyphase not really, I'm more concerned about keeping just a unique id with the correct sum of scores Commented Aug 12, 2015 at 7:25

3 Answers 3

6

itertools.groupby was made exactly for this situation https://docs.python.org/2/library/itertools.html#itertools.groupby

from itertools import groupby
# object.id is our key:
keyfunc = lambda obj: obj.id
list_of_objects = sorted(list_of_objects, key=keyfunc)

scores = [sum(score_list) for id, score_list in groupby(list_of_objects, keyfunc)]
ids = [id for id, score_list in groupby(list_of_objects, keyfunc)]
Sign up to request clarification or add additional context in comments.

1 Comment

but I want to alter the list of objects not create a new list @frank
2

Normally you do this using a dictionary to detect already seen objects:

seen = {}
for x in my_objects:
    if x.id in seen:
        seen[x.id].score += x.score
    else:
        seen[x.id] = x
my_objects[:] = seen.values()

Using a dictionary makes the computation O(n) instead of O(n²)

3 Comments

@RenaeLider: a set of what? If you keep just the .id once you see it's an already seen object you also need to find the original to add the score to it
do I get to keep the class structure by doing my_objects[:] = list(seen.values()) ?
@DiegoAgher: yes, seen.values() are the original objects to which the score field has been incremented. The list() call is not needed if assigning back to the original list (I edited the post removing it).
1

You can go using the Python Built-in Functions, within a single line, by supplying an additional custom function:

def r(l, o):
    if len(l) > 0 and l[-1].id == o.id:
        l[-1].score += o.score
    else:
        l.append(o)
    return l

key = attrgetter('id')

And then simply use the reduce function in combination with sorted and the above custom function:

list_of_objects = reduce(r, sorted(list_of_objects, key=key), [])

Then you will have what you need:

[1: 0.25, 2: 0.85, 3: 0.93, 4: 0.4]

Comments

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.