【问题标题】:Binary searching through an array with indexOfObject:inSortedRange:options:usingComparator:使用 indexOfObject:inSortedRange:options:usingComparator 对数组进行二进制搜索:
【发布时间】:2013-09-29 19:39:23
【问题描述】:

数组方法看不懂indexOfObject:inSortedRange:options:usingComparator:

更具体地说,indexOfObject 属性。

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

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

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

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

但这显然不会获得基于 ID 的对象.. 它似乎会给我我已经引用的属性的索引,这似乎毫无意义....

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

【问题讨论】:

    标签: iphone ios objective-c ipad nsarray


    【解决方案1】:

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

    要通过自己的属性在数组中查找特定对象,可以使用

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

    【讨论】:

    • filteredArrayUsingPredicate 导致很多减速,因为它从数组的开头搜索到结尾。我需要对非常大的数组进行二进制搜索。因此我为什么要尝试使用这种方法
    • 因此,您只需确保始终拥有按 Car ID 排序的数组,然后只需编写一个小方法即可使用二进制搜索算法找到您的对象。长话短说:如果您对NSPredicate 不满意,我认为您必须为此编写自己的实现
    【解决方案2】:

    可以用这个 API 来做你想做的事,尽管我并不真正提倡这种方法。如果您传入要搜索的键,它将与被比较的元素一起传递到比较器中。但是,传递这些参数的顺序会发生变化,因此您需要在运行时检查比较器参数的类,以便区分它们并执行比较。

    - (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 小时的调试后,我也注意到了这一点,比较器参数的顺序看似随机切换 - 为什么这是我无法想象的。这意味着如果你在一个字符串数组上使用它,你也需要一个字符串比较来确定比较器中的哪个参数是哪个!
    【解决方案3】:

    我为 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
    

    【讨论】:

      【解决方案4】:

      compare: 方法(用于二进制搜索比较器)根据对象表示不同的含义。例如,NSString 将比较实现为词法比较。因此,如果您有一个已排序的NSArrayNSString,那么您将得到与输入字符串匹配的字符串列表中的索引(如果不在数组中,则为NSNotFound)。

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

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

      【讨论】:

        猜你喜欢
        • 2020-10-19
        • 2013-05-22
        • 1970-01-01
        • 2012-10-02
        • 1970-01-01
        • 1970-01-01
        • 2023-03-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多