Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get list of objects with unique attribute

Tags:

python

list

set

Background

I have a list. This list has many objects. Each object has an id. Now the objects are of different types.

objects = [Aobject, Bobject, Cobject] 

where

>>> Aobject != Bobject True >>> Aobject.id ==  Bobject.id True 

Problem

I want a list of unique objects based on the object.id.

Something like this:

set(objects, key=operator.attrgetter('id')) 

(This does not work. But I want something like this)

like image 878
Akamad007 Avatar asked Apr 05 '12 07:04

Akamad007


People also ask

How do I get unique values in a list of objects?

By using set() method Then we extract the unique values from the list by applying the set() function. Now declare a variable 'z' and use the list() function. Once you will print 'z' then the output will display the unique values.

What is unique list in Python?

Python's unique list is a list that contains unique elements irrespective of the order. There are several methods to get a unique list from the given list. Let's see the below example of what does the unique list contains.

What do you call the attributes that create unique object in Python?

An instance/object attribute is a variable that belongs to one (and only one) object. Every instance of a class points to its own attributes variables. These attributes are defined within the __init__ constructor.


2 Answers

seen = set()   # never use list as a variable name [seen.add(obj.id) or obj for obj in mylist if obj.id not in seen] 

This works because set.add returns None, so the expression in the list comprehension always yields obj, but only if obj.id has not already been added to seen.

(The expression could only evaluate to None if obj is None; in that case, obj.id would raise an exception. In case mylist contains None values, change the test to if obj and (obj.id not in seen))

Note that this will give you the first object in the list which has a given id. @Abhijit's answer will give you the last such object.

Update:

Alternatively, an ordereddict could be a good choice:

import collections seen = collections.OrderedDict()  for obj in mylist:     # eliminate this check if you want the last item     if obj.id not in seen:        seen[obj.id] = obj  list(seen.values()) 
like image 81
Marcin Avatar answered Sep 24 '22 18:09

Marcin


How about using dict (since its keys are unique)?

Assuming we have

class Object:     def __init__(self, id):         self.id = id   Aobject = Object(1) Bobject = Object(1) Cobject = Object(2) objects = [Aobject, Bobject, Cobject] 

then list with Objects unique by id field can be generated using dict comprehension in Python 3

unique_objects = list({object_.id: object_ for object_ in objects}.values()) 

in Python 2.7

unique_objects = {object_.id: object_ for object_ in objects}.values() 

and in Python <2.7

unique_objects = dict([(object_.id, object_) for object_ in objects]).values() 

Finally, we can write function (Python 3 version)

def unique(elements, key):     return list({key(element): element for element in elements}.values()) 

where elements may be any iterable and key is some callable which returns hashable objects from elements (key equals to operator.attrgetter('id') in our particular case).

Marcin's answer works fine but doesn't look Pythonic to me since list comprehension mutates seen object from outer scope, also there is some magic behind using set.add method and comparing its result (which is None) with obj.

And final but not less important part:

Benchmark

setup = ''' import random   class Object:     def __init__(self, id):         self.id = id   objects = [Object(random.randint(-100, 100))            for i in range(1000)] ''' solution = ''' seen = set() result = [seen.add(object_.id) or object_           for object_ in objects           if object_.id not in seen] ''' print('list comprehension + set: ',       min(timeit.Timer(solution, setup).repeat(7, 1000))) solution = ''' result = list({object_.id: object_                for object_ in objects}.values()) ''' print('dict comprehension: ',       min(timeit.Timer(solution, setup).repeat(7, 1000))) 

on my machine gives

list comprehension + set:  0.20700953400228173 dict comprehension:  0.1477799109998159 
like image 43
Azat Ibrakov Avatar answered Sep 24 '22 18:09

Azat Ibrakov