Reputation: 17132
I have a class such has:
class ex(object):
def __init__(self, tag, quantity):
self.tag = tag
self.quantity = quantity
I also have a list with multiple instance of this class such has:
[ex('a', 10.5), ex('b', 5.0), ex('c', 2.0), ex('a', 5.0), ex('c', -10.0)]
I am trying to figure out a nice way to add the self.quantiy attribute if the self.tag matches another instance in the list.
output would be :
[ex('a', 15.5), ex('b', 5.0), ex('c', -8.0)]
thanks
edit: final instance can be a new instance or the first of the list accumulated. the max size of the list will be around len() of 1000
Upvotes: 1
Views: 172
Reputation: 96028
Here is an O(n) solution creating new objects:
In [9]: class Ex(object):
...: def __init__(self, tag, quantity):
...: self.tag = tag
...: self.quantity = quantity
...:
...: def __repr__(self):
...: return '%s(%r, %r)' % (self.__class__.__name__, self.tag, self.quantity)
...:
In [10]: objs = [Ex('a', 10.5), Ex('b', 5.0), Ex('c', 2.0), Ex('a', 5.0), Ex('c', -10.0)]
In [11]: objs
Out[11]: [Ex('a', 10.5), Ex('b', 5.0), Ex('c', 2.0), Ex('a', 5.0), Ex('c', -10.0)]
In [12]: grouped = {}
In [13]: for obj in objs:
...: grouped[obj.tag] = grouped.get(obj.tag, 0) + obj.quantity
...:
In [14]: new_objs = [Ex(k, v) for k,v in grouped.items()]
In [15]: new_objs
Out[15]: [Ex('b', 5.0), Ex('a', 15.5), Ex('c', -8.0)]
You could get fancier using a defaultdict
or a Counter
, or an OrderedDict
if order matters.
Upvotes: 1
Reputation: 362847
I think a plain loop is actually better choice than a list comprehension here. It is possible in O(n) complexity by keeping track of tags already seen:
seen = {}
for instance in in_list:
try:
existing = seen[instance.tag]
except KeyError:
seen[instance.tag] = instance
else:
existing.quantity += instance.quantity
out_list = list(seen.values()) # can drop the "list" call on Python 2.x
The first instance from each group will be used, and updated afterward if the tag is seen again.
If you wish to preserve original ordering of the tags, then drop in a collections.OrderedDict
for the seen
variable instead of a plain dict. There will be a slight performance penalty, yet the solution remains O(n) complexity.
Upvotes: 1
Reputation: 36033
O(n^2)
, but simpler code.
class ex(object):
def __init__(self, tag, quantity):
self.tag = tag
self.quantity = quantity
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.tag, self.quantity)
exes = [ex('a', 10.5), ex('b', 5.0), ex('c', 2.0), ex('a', 5.0), ex('c', -10.0)]
summed_exes = [ex(tag, sum(e.quantity for e in exes if e.tag == tag))
for tag in sorted({e.tag for e in exes})]
print(summed_exes)
sorted
is optional.
Output:
[ex('a', 15.5), ex('b', 5.0), ex('c', -8.0)]
Upvotes: 2