Remove duplicates from array comparing the properties of its objects

前端 未结 6 1290
隐瞒了意图╮
隐瞒了意图╮ 2021-02-04 03:50

Suppose I have a class Event, and it has 2 properties: action (NSString) and date (NSDate).

And suppose I have an array of Event objects. The problem is that \"date\" pr

相关标签:
6条回答
  • 2021-02-04 04:01

    Here is working Swift code snipped which does remove duplicates while keeping the order of elements.

    // Custom Struct. Can be also class. 
    // Need to be `equitable` in order to use `contains` method below
    struct CustomStruct : Equatable {
          let name: String
          let lastName : String
        }
    
    // conform to Equatable protocol. feel free to change the logic of "equality"
    func ==(lhs: CustomStruct, rhs: CustomStruct) -> Bool {
      return (lhs.name == rhs.name && lhs.lastName == rhs.lastName)
    }
    
    let categories = [CustomStruct(name: "name1", lastName: "lastName1"),
                      CustomStruct(name: "name2", lastName: "lastName1"),
                      CustomStruct(name: "name1", lastName: "lastName1")]
    print(categories.count) // prints 3
    
    // remove duplicates (and keep initial order of elements)
    let uniq1 : [CustomStruct] = categories.reduce([]) { $0.contains($1) ? $0 : $0 + [$1] }
    print(uniq1.count) // prints 2 - third element has removed
    

    And just if you are wondering how this reduce magic works - here is exactly the same, but using more expanded reduce syntax

    let uniq2 : [CustomStruct] = categories.reduce([]) { (result, category) in
      var newResult = result
      if (newResult.contains(category)) {}
      else {
        newResult.append(category)
      }
      return newResult
    }
    uniq2.count // prints 2 - third element has removed
    

    You can simply copy-paste this code into a Swift Playground and play around.

    0 讨论(0)
  • 2021-02-04 04:03
    NSMutableArray *leftObjects = [duplicateArray mutableCopy];
    NSMutableArray *nonDuplicates = [NSMutableArray new];
    while (leftObjects.count > 0)
    {
        YourClass *object = [leftObjects objectAtIndex:0];
    
        // find all objects matching your comaprison equality definition for YourClass
        NSArray *matches = [leftObjects filteredArrayUsingPredicate:
                            [NSPredicate predicateWithBlock:^BOOL(YourClass *evaluatedObject, NSDictionary *bindings)
                             {
                                 return (evaluatedObject.name == object.name);
                             }] ];
        [leftObjects removeObjectsInArray:matches];
    
        // add first object (arbitrary, may decide which duplicate to pick)
        [nonDuplicates addObject:matches.firstObject];
    }
    
    0 讨论(0)
  • 2021-02-04 04:17

    I think the most effective way is to use NSDictionary to store the object as value and the property value as key, and before adding any object to the dictionary you check if it exist or not which is O(1) operation, i.e. the whole process will take O(n)

    Here is the code

    - (NSArray *)removeDuplicatesFromArray:(NSArray *)array onProperty:(NSString *)propertyName {
        NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
    
        for (int i=0; i<array.count; i++) {
    
            NSManagedObject *currentItem = array[i];
            NSString *propertyValue = [currentItem valueForKey:propertyName];
    
            if ([dictionary valueForKey:propertyValue] == nil) {
                [dictionary setValue:currentItem forKey:propertyValue];
            }
        }
    
        NSArray *uniqueItems = [dictionary allValues];
    
        return uniqueItems;
    }
    

    you can use it as the following

    self.arrayWithObjects = [self removeDuplicatesFromArray:self.arrayWithObjects onProperty:@"when"]; 
    
    0 讨论(0)
  • 2021-02-04 04:18

    You can create an NSMutableSet of dates, iterate your event list, and add only events the date for which you have not encountered before.

    NSMutableSet *seenDates = [NSMutableSet set];
    NSPredicate *dupDatesPred = [NSPredicate predicateWithBlock: ^BOOL(id obj, NSDictionary *bind) {
        Event *e = (Event*)obj;
        BOOL seen = [seenDates containsObject:e.date];
        if (!seen) {
            [seenDates addObject:e.date];
        }
        return !seen;
    }];
    NSArray *events = ... // This is your array which needs to be filtered
    NSArray *filtered = [events filteredArrayUsingPredicate:dupDatesPred];
    
    0 讨论(0)
  • 2021-02-04 04:21

    Wouldn't this work with kvc. I suppose the following solution could work in your case;

    Event *event1 = [[Event alloc] init];
    event1.name = @"Event1";
    event1.date = [NSDate distantFuture];
    Event *event2 = [[Event alloc] init];
    event2.name = @"Event2";
    event2.date = [NSDate distantPast];
    Event *event3 = [[Event alloc] init];
    event3.name = @"Event1";
    event3.date = [NSDate distantPast];
    NSArray *array = @[event1, event2, event3];
    
    NSArray *filteredEvents =  [array valueForKeyPath:@"@distinctUnionOfObjects.name"];
    
    0 讨论(0)
  • 2021-02-04 04:22

    Here is a Swift extension on the NSArray class that removes duplicates for the specified property:

    extension NSArray {
    /**
     - parameter property: the name of the property to check for duplicates
    
     - returns: an array of objects without objects that share an identical value of the specified property
    */
      func arrayWithoutObjectsOfDuplicateProperty(property : String) -> [AnyObject] {
        var seenInstances = NSMutableSet()
    
        let predicate = NSPredicate { (obj, bind) -> Bool in
          let seen = seenInstances.containsObject(obj.valueForKey(property)!)
    
          if !seen {
            seenInstances.addObject(obj.valueForKey(property)!)
          }
            return !seen
          }      
          return self.filteredArrayUsingPredicate(predicate)
      }
    }
    
    0 讨论(0)
提交回复
热议问题