比较对象属性,从数组中删除重复项

19
假设我有一个名为Event的类,它有两个属性:action(NSString)和date(NSDate)。
假设我有一个Event对象的数组。问题在于“date”属性可能相同。
我需要删除重复项,也就是说,具有相同日期的两个不同对象是重复的。
我可以从任何字符串或NSDate数组中删除重复项,它们很容易比较。但如何处理复杂对象,其中它们的属性要进行比较?
不要问我到目前为止做了什么,因为我想到的唯一一件事是冒泡排序,但这是一个新手解决方案,并且很慢。
非常感谢任何帮助(链接、教程、代码)。
提前致谢。
编辑
感谢dasblinkenlight的帮助,我已经创建了一个自定义方法:
- (NSArray *)removeDuplicatesInArray:(NSArray*)arrayToFilter{

    NSMutableSet *seenDates = [NSMutableSet set];
    NSPredicate *dupDatesPred = [NSPredicate predicateWithBlock: ^BOOL(id obj, NSDictionary *bind) {
        YourClass *e = (YourClass*)obj;
        BOOL seen = [seenDates containsObject:e.propertyName];
        if (!seen) {
            [seenDates addObject:e.when];
        }
        return !seen;
    }];
    return [arrayToFilter filteredArrayUsingPredicate:dupDatesPred];
} 

YourClass 是你的类对象所属的名称,propertyName 是你要比较的该对象的属性。

假设 self.arrayWithObjects 包含 YourClass 的对象。

填充数组后,使用

self.arrayWithObjects = [self removeDuplicatesInArray:self.arrayWithObjects];

完成操作。

所有权归 dasblinkenlight 所有。谢谢!


如果您采取的方法是防止将日期与数组中已有的事件相等的事件对象添加到事件中,则可以正常工作。 - LuisEspinoza
不用了,这个数组已经从一个巨大的JSON中填充了。这个过程太复杂了,我想在操作后删除重复项更容易些。 - John Smith
通过日期对数组进行排序(使用NSArray的几个sortedArray...函数非常容易),然后遍历排序后的数组,将元素复制到一个新的NSMutableArray中,跳过任何与上一个元素具有相同日期的元素。 - Hot Licks
6个回答

26
你可以创建一个日期的 NSMutableSet,对事件列表进行迭代,并仅添加那些你以前没有遇到过的日期的事件。
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];

3
这个问题可以用KVC解决。我想以下解决方案适用于你的情况:
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"];

1
@distinctUnionOfObjects.name 只会返回一个名为 "name" 的唯一属性数组,而不是其他对象。 - lagos

2
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
这是一个针对NSArray类的Swift扩展,用于删除指定属性中的重复项:
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
这是一段有效的 Swift 代码片段,可以在保持元素顺序的同时删除重复项。
// 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

如果你想知道这个 reduce 魔法是如何工作的 - 这里有一个使用更多扩展的 reduce 语法的例子

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

您可以将此代码简单地复制粘贴到Swift Playground中并进行操作。


0

我认为最有效的方法是使用NSDictionary将对象存储为值,属性值作为键,并在添加任何对象到字典之前检查它是否存在,这是O(1)操作,即整个过程将需要O(n)

以下是代码

- (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;
}

您可以按以下方式使用它

self.arrayWithObjects = [self removeDuplicatesFromArray:self.arrayWithObjects onProperty:@"when"]; 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接