2

I have a list of objects with attributes qt, cons and consper and have to merge all the objects that have the same consper value. What is the best way to do that? The list is already sorted by consper.

Example: With a list of objects of the class house:

class house():
    def __init__(self, qt, cons, consper):
        self.qt = qt
        self.cons = cons
        self.consper = consper

Turn this list:

l = [
house(2, 20, 10),
house(3, 31, 10),
house(6, 70, 11),
house(2, 40, 20),
house(1, 25, 25)]

Into this list:

l_new = [
house(5, 51, 10),
house(6, 70, 11),
house(2, 40, 20),
house(1, 25, 25)]

By adding the first two objects (because their attribute consper is equivalent)

4 Answers4

2

If the items are already sorted by that attribute, you can use itertools.groupby to get the groups and sum to get the sum for the other attributes. You also have to convert the group to list first, as those are iterators.

>>> from itertools import groupby
>>> house.__repr__ = lambda h: "house(%r, %r, %r)" % (h.qt, h.cons, h.consper)
>>> [house(sum(h.qt for h in g), sum(h.cons for h in g), k) 
...  for k, g in ((k, list(g)) for k, g in groupby(l, key=lambda h: h.consper))]
[house(5, 51, 10), house(6, 70, 11), house(2, 40, 20), house(1, 25, 25)]

Or using a dictionary:

>>> d = {}
>>> for h in l:
...     qt, cons = d.get(h.consper, (0, 0))
...     d[h.consper] = qt + h.qt, cons + h.cons
...
>>> [house(a, b, c) for a, (b, c) in d.items()]
[house(25, 1, 25), house(10, 5, 51), house(11, 6, 70), house(20, 2, 40)]
tobias_k
  • 81,265
  • 12
  • 120
  • 179
1

You can use itertools.groupby:

import itertools
class house():
  def __init__(self, qt, cons, consper):
    self.qt = qt
    self.cons = cons
    self.consper = consper
  def __repr__(self):
    return self.__class__.__name__+"({qt}, {cons}, {consper})".format(**self.__dict__)

l = [house(2, 20, 10),
 house(3, 31, 10),
 house(6, 70, 11),
 house(2, 40, 20),
 house(1, 25, 25)]
new_l = [(a, [(i.qt, i.cons) for i in list(b)]) for a, b in itertools.groupby(sorted(l, key=lambda x:x.consper), key=lambda x:x.consper)]
final_data = [house(*[sum(i) for i in zip(*b)]+[a]) for a, b in new_l]

Output:

[house(5, 51, 10), house(6, 70, 11), house(2, 40, 20), house(1, 25, 25)]
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
1

Without using itertools, you could do something like this:

class House():
        def __init__(self, qt, cons, consper):
            self.qt = qt
            self.cons = cons
            self.consper = consper

        def __str__(self):
            return "House(" + str(self.qt) + "," + str(self.cons) + "," + str(self.consper) + ")"

        def __repr__(self):
            return self.__str__()

def merge_dups(house_list):
    res = []
    house_map = {}
    for h in house_list:
        if h.consper in house_map:
            other_house = house_map[h.consper]
            merged_house = House(h.qt + other_house.qt,
                                 h.cons + other_house.cons,
                                 h.consper)
            res.remove(other_house)
            res.append(merged_house)

        else:
            house_map[h.consper] = h
            res.append(h)
    return res

print(merge_dups([
House(2, 20, 10),
House(3, 31, 10),
House(6, 70, 11),
House(2, 40, 20),
House(1, 25, 25)]))

Output

[House(5,51,10), House(6,70,11), House(2,40,20), House(1,25,25)]
CrizR
  • 688
  • 1
  • 6
  • 26
0

A simple solution is using a Dictionary as follows:

l = [
house(2, 20, 10),
house(3, 31, 10),
house(6, 70, 11),
house(2, 40, 20),
house(1, 25, 25)]
dic= {}
for x in l :
    temp = dic.get(x.consper,house(0,0,0))
    x.qt += temp.qt
    x.cons += temp.cons
    dic[x.consper]=x

print('####################')
for x in dic.keys():
    print(x)
Sir1
  • 732
  • 1
  • 8
  • 18