使用indexOfObject通过数组进行二进制搜索:inSortedRange:options:usingComparator:

时间:2013-09-24 14:18:10

标签: iphone ios objective-c ipad nsarray

我不理解数组方法indexOfObject:inSortedRange:options:usingComparator:

更具体地说,indexOfObject属性。

根据文档传入的值应为An object for which to search in the array.但这没有意义..如果我已经有了对象的引用,为什么我会在数组中搜索它?这是否意味着对象类型?

我有一个对象数组,而我拥有的只是这些对象的属性。即。我有一系列汽车,当我的汽车ID为12345时,我需要找到汽车对象。

我将传递给indexOfObject属性的方法?这是我正在尝试的

   MyCarObject *searchObject;
   NSUInteger findIndex = [sortedArray indexOfObject:searchObject
                                       inSortedRange:searchRange
                                             options:NSBinarySearchingFirstEqual
                                     usingComparator:^(id obj1, id obj2)
                              {
                                return [obj1 compare:obj2];
                              }];

但是这显然不会得到一个基于ID的对象..看起来它会给我一个我已经引用的属性的索引,这似乎毫无意义....

如果这不是正确的方法,那么我应该使用什么?我需要对对象数组使用二进制搜索并拉出对该对象的引用。我所拥有的只是一个可以比较的属性。

4 个答案:

答案 0 :(得分:2)

此方法返回给定数组中对象的索引,这有时非常有用。使用isEqual:方法(默认情况下比较指针)比较数组中的对象。这就是该方法无法知道您希望使用某些自定义属性进行比较的原因。

要使用您自己的属性在数组中查找特定对象,请使用

  • NSArray方法- (NSArray *)filteredArrayUsingPredicate:和相应的NSPredicate(关于SO的问题和教程有很多问题)
  • 自己的循环将您想要的任何属性与任何值进行比较(对象的车牌ID与您正在搜索的ID一样)

答案 1 :(得分:0)

- (BWCProductCategory *)categoryForID:(NSNumber *)ID categories:(NSArray *)categories {
    NSRange searchRange = NSMakeRange(0, categories.count);
    NSUInteger index = [categories indexOfObject:ID
                                   inSortedRange:searchRange
                                         options:NSBinarySearchingFirstEqual
                                 usingComparator:^NSComparisonResult(id obj1, id obj2) {
                                     if ([obj1 isKindOfClass:[BWCProductCategory class]]) {
                                         return [[(BWCProductCategory *)obj1 categoryID] compare:obj2];
                                     } else {
                                         return [obj1 compare:[(BWCProductCategory *)obj2 categoryID]];
                                     }
                                 }];

    return (index == NSNotFound) ? nil : categories[index];
}

这确实有用,但感觉很尴尬,而且我对执行大型搜索时的性能影响没有信心(尽管它肯定还不及O(n)搜索)。也许你可以在这些方法之上构建一个更好的方法来隐藏kludge因素。

答案 2 :(得分:0)

我为Swift的Array创建了一个扩展,以便以这种方式使Swift非常干净。

import Foundation

extension Array where Element: AnyObject {

    public func indexOfObject<T: AnyObject>(obj: T, options opts: NSBinarySearchingOptions, usingComparator cmp: (T, Element) -> NSComparisonResult) -> Int {
        return (self as NSArray).indexOfObject(obj, inSortedRange: NSRange(0..<count), options: opts, usingComparator: { (a: AnyObject, b: AnyObject) -> NSComparisonResult in
            if a === obj {
                return cmp(a as! T, b as! Element)
            } else {
                var result = cmp(b as! T, a as! Element)

                if result == .OrderedDescending {
                    result = .OrderedAscending
                } else if result == .OrderedAscending {
                    result = .OrderedDescending
                }

                return result
            }
        })
    }
}

以下是一个示例用法:

class ItemWithProperty {
    var property: Int

    init(property: Int) {
        self.property = property
    }
}

let listOfItems = [ItemWithProperty(property: 1),
    ItemWithProperty(property: 20),
    ItemWithProperty(property: 30),
    ItemWithProperty(property: 45),
    ItemWithProperty(property: 45),
    ItemWithProperty(property: 45),
    ItemWithProperty(property: 60),
    ItemWithProperty(property: 77),
]

let indexOf20 = listOfItems.indexOfObject(20, options: .FirstEqual) { number, item in
    number.compare(item.property)
}
// returns 1

let indexOf25 = listOfItems.indexOfObject(25, options: .FirstEqual) { number, item in
    number.compare(item.property)
}
indexOf25 == NSNotFound
// comparison is true, number not found

let indexOfFirst45 = listOfItems.indexOfObject(45, options: .FirstEqual) { number, item in
    number.compare(item.property)
}
// returns 3

let indexOfLast45 = listOfItems.indexOfObject(45, options: .LastEqual) { number, item in
    number.compare(item.property)
}
// returns 5

let indexOf77 = listOfItems.indexOfObject(77, options: .FirstEqual) { number, item in
    number.compare(item.property)
}
// returns 7

答案 3 :(得分:0)

compare:方法(您用于二进制搜索比较器)根据对象的不同而不同。例如,NSString将比较实现为词法比较。所以,如果你有一个NSArray的{​​{1}},那么你将得到的是与输入字符串匹配的字符串列表中的索引(如果不在数组中,则为NSString )。

对于您的对象类型(NSNotFound),您将实现MyCarObject的定义以确定compare:的相对顺序。然后,您可以构造MyCarObject的新实例,并使用此方法确定等效对象(由MyCarObject确定)是否已在列表中。

请注意,此方法执行二进制搜索,因此必须使用与搜索它相同的比较器对数组进行排序。您可以使用compare:来查找插入新元素的索引,以便对列表进行排序。