python-3.xduplicatespython-object

How to ignore duplicate object values from display


I have a test class with 3 attributes, a,b,and c. 3 instances of the class has been created with values set for the attributes appropriately, Its given that 'a' attribute is always unique and when two objects with same 'b' value will always have same 'c' value. if such objects are seen, display of attribute 'c' to be omitted for that object.

class test():
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    def __repr__(self):
        return 'test(%s, %s, %s)' % (self.a, self.b, self.c)


test1 = test("1", "abc", "def")
test2 = test("2", "yyy", "xxy")
test3 = test("3", "yyy", "xxy")

objList = [test1, test2, test3]

#takes out duplicate objects
new_list = [next(obj) for i, obj in
            groupby(sorted(objList, key=lambda test: test.a), lambda test: test.c)]
print(new_list)

The above code gets me output as below.

[test(1, abc, def), test(2, yyy, xxy)]

Expected output is to omit only attribute c from the object.

[test(1, abc, def), test(2, yyy, xxy),test(3, yyy)]

Pls Help!


Solution

  • Updated REPR

    To be able to change what the class is printing, you have to be able to change the repr method. Mainly because what is presented is a bit too constrictive. If instead the class was as follows, then this is possible:

    class test():
        def __init__(self, a, b, c):
            self.a = a
            self.b = b
            self.c = c
    
        def __repr__(self):
          expr = []
          if self.a:
            expr.append(self.a)
          if self.b:
            expr.append(self.b)
          if self.c:
            expr.append(self.c)
    
          return 'test(%s)' % (', '.join(expr))
    
    

    The biggest hurdle is the fact that we need some control over what is displayed, therefore we need to change the repr method. Alternatively, you might be able to generate the same results by leverage the str method and converting to a string before printing.

    Type independent remove duplicates function

    Since I didn't know you're exact use case, and wanted to make something relevant to all types of problems, I made the following function. It will allows you to specify an equality condition, what to do when the condition is met, and how to sort the list.

    (The default is to check if subsequent values objects are equal, if the are then do not include the second one in the resulting list.)

    def remove_dups(old_list, 
                    condition=lambda a, b: a == b, 
                    remove_func=lambda x, y: None, 
                    sort_key=None):
      """
        Returns a new list that has duplicates removed from `old_list`
    
        #Arguments
        old_list: list to have duplicates discarded
        condition: test condition between previous and next obj to test equality.
        remove_func: what to add to the list if the condition is met. If `None` is returned
          then no value is added to the `new_list`
        sort_key: how to sort the `old_list`.
      """
    
    
      old_list = sorted(old_list, key=sort_key)
      comparitor = old_list[0] #used to compare previous and current obj
      new_list = [comparitor]
      for i, obj in enumerate(old_list[1:]):
        #if previous object and current obj are the same
        if condition(comparitor, obj):
    
          #run removal function
          if remove_func:
            new_value = remove_func(comparitor, obj)
          else:
            new_value = None
    
        else: #then objects are different, add the new one
          new_value = obj
    
        if new_value is not None:
          new_list.append(new_value)
    
        comparitor = obj
        new_value = None
    
      return new_list
    

    Your case (example)

    We can use this in your case like follows

    test1 = test("1", "abc", "def")
    test2 = test("2", "yyy", "xxy")
    test3 = test("3", "yyy", "xxy")
    
    objList = [test1, test2, test3]
    
    #in lambda functions `a` corresponds to previous object in list,  
    #`b` refers to currently observed object
    new_list = remove_dups(objList,
                condition=lambda a, b: a.b == b.b, #if a.b and b.b the same
                remove_func=(lambda a, b: test(b.a, b.b, None)), #then set b.c = None
                sort_key=lambda obj: (obj.a, obj.b, obj.c)
              )
    
    print(new_list) # [test(1, abc, def), test(2, yyy, xxy), test(3, yyy)]
    

    Edit

    If you want to do this without editing the repr method, then you could do some special print statements with the new_list instead of explicitly trying to print the test objects.

    Example:

    objList = [test1, test2, test3]
    
    #in lambda functions `a` corresponds to previous object in list,  
    #`b` refers to currently observed object
    new_list = remove_dups(objList,
                condition=lambda a, b: a.b == b.b, #if a.b and b.b the same
                remove_func=(lambda a, b: test(b.a, b.b, None)), #then set b.c = None
                sort_key=lambda obj: (obj.a, obj.b, obj.c)
              )
    
    exprs = []
    for test in new_list:
      expr = []
      if test.a:
        expr.append(test.a)
      if test.b:
        expr.append(test.b)
      if test.c:
        expr.append(test.c)
    
      exprs.append('test(%s)' % (', '.join(expr)))
    
    print(exprs) # [test(1, abc, def), test(2, yyy, xxy), test(3, yyy)]