Ios 过滤包含大量对象的数组的唯一名称

Ios 过滤包含大量对象的数组的唯一名称,ios,swift,performance,Ios,Swift,Performance,我目前正在获取大量包含街道名称和坐标的对象。返回的数组大约有22000个对象,我们想要的结果数组大约有4000个,其余的都是重复的。这类数据的问题是,获取的对象可能具有相同的名称,但坐标不同,我只对基于唯一名称获取对象感兴趣。如果有多个具有相同名称的对象,我只希望保留第一个对象 到目前为止,我一直试图通过比较名字在街上兜圈子。我宁愿使用过滤器或其他更高效的解决方案 我的结构 到目前为止我的代码 DataManager.shared.getStreetNames{(街道)位于 变量名称数组:[St

我目前正在获取大量包含街道名称和坐标的对象。返回的数组大约有22000个对象,我们想要的结果数组大约有4000个,其余的都是重复的。这类数据的问题是,获取的对象可能具有相同的名称,但坐标不同,我只对基于唯一名称获取对象感兴趣。如果有多个具有相同名称的对象,我只希望保留第一个对象

到目前为止,我一直试图通过比较名字在街上兜圈子。我宁愿使用
过滤器
或其他更高效的解决方案

我的结构 到目前为止我的代码
DataManager.shared.getStreetNames{(街道)位于
变量名称数组:[StreetName]=[]
街道名称{
let name=streetName.name
如果namesArray.count==0{
namesArray.append(streetName)
}如果namesArray.contains(其中:{$0.name==name}){
/*不要添加*/
}否则{
namesArray.append(streetName)
}
}
self.streetNames=namesArray.sorted(按:{$0.name<$1.name})
self.filteredStreetNames=self.streetNames
OperationQueue.main.addOperation{
self.streetTableView.reloadData()
}
}

这个代码块可以工作,但在iPhoneX上运行大约30秒。这太慢了。有什么想法吗?

我想如果你分析一下,你会发现
排序花费的时间最多。我找不到正式的注释,但底层实现很可能是快速排序,当数组已经排序(或数组按相反顺序排序)时,它的复杂性最差

快速排序的平均案例复杂度是O(n logn),但在最坏的情况下是O(n2)

我认为应该实现插入排序,或者更准确地说,总是将新元素插入到已排序的位置。这将使整个函数的复杂度降低到O(n)

伪代码:

  • 取街名
  • 每个街道的名称
    • 在现有数组中查找街道名称的位置(我建议进行二进制搜索,因为数组已经排序)
    • 如果街道名称已存在,请跳过
    • 如果名称不存在,请插入它
结果应该是唯一街道名称的排序数组,要求每个名称只读取和插入一次。

我的看法是:

// Given an array of elements (here just Ints):
let array = (0..<1000).map { _ in Int(arc4random_uniform(100)) }

// Sort it:
let sorted = array.sorted()

// Define an empty result (array of elements) which is a variable 
// and which gets modified in the subsequent reduce function:
var unique: [Int] = []

// A tailored reduce which depends on a sorted array and appends 
// to the result IFF that element is not the last in result:
let result = sorted.reduce(into: unique) { (result, element) in
    if let last = result.last, last == element {
    } else {
        result.append(element)
    }
}
控制台上的输出示例:
控制台
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

@MartinR通过使用
集合解决了这个问题

我的新更新结构 我的新更新代码
DataManager.shared.getStreetNames{(returnedNameset)位于
变量名称数组:[StreetName]=数组(返回的名称集)
self.streetNames=namesArray.sorted(按:{$0.name<$1.name})
self.filteredStreetNames=self.streetNames
OperationQueue.main.addOperation{
self.streetTableView.reloadData()
}
}

结果:
使用
Set

将处理时间从30秒增加到了0.4秒。我建议重新打开此问题,建议的重复地址将删除重复地址,但没有解决效率问题。为什么删除代码的
排序部分?还原了编辑器,使用将直接从数据库返回街道排序数组的查询。使用前向迭代器或使用定制的
reduce
函数很容易删除重复项。这就是我的意思:如果数组已经“某种程度上”排序,这不是快速排序的最佳选择,那么
shuffle
然后
quick sort
然后
reduce
怎么样?我也喜欢这个想法,但是要想弄清楚如何将这个伪代码转换成代码,我会很困难。任何指针?在建议的中,提供了删除重复项的最有效方法。相比之下,对数组进行排序的时间可以忽略不计。Swift使用introsort(快速排序的一种变体),compare。已排序的数组是否为最坏情况取决于枢轴元素的选择。Swift使用第一、中间、最后一个元素的中间值。@vadian-这不是比较相等的对象吗?本例中的对象是唯一的,但某些值可能相同…您对
==
的实现是错误的:不同的字符串可以具有相同的哈希值。您有什么建议来解决此问题吗?到目前为止,我对此没有任何问题,但您是正确的@MartinRIf等式仅基于街道名称,然后返回lhs.name==rhs.name
顺便说一句,使类型本身可散列并不完全是我在中建议的,但要保持迄今为止在
集中看到的所有名称,它工作得非常好,我将编辑答案!真的,谢谢@MartinR
DataManager.shared.getStreetNames { (streets) in  
    var namesArray: [StreetName] = []
    for streetName in streets {
        let name = streetName.name
        if namesArray.count == 0 {
            namesArray.append(streetName)
        } else if namesArray.contains(where: {$0.name == name }) { 
             /* Dont add */ 
        } else {
             namesArray.append(streetName)
        }
    }

    self.streetNames = namesArray.sorted(by: {$0.name < $1.name})
    self.filteredStreetNames = self.streetNames
    OperationQueue.main.addOperation {
        self.streetTableView.reloadData()
    }
}
// Given an array of elements (here just Ints):
let array = (0..<1000).map { _ in Int(arc4random_uniform(100)) }

// Sort it:
let sorted = array.sorted()

// Define an empty result (array of elements) which is a variable 
// and which gets modified in the subsequent reduce function:
var unique: [Int] = []

// A tailored reduce which depends on a sorted array and appends 
// to the result IFF that element is not the last in result:
let result = sorted.reduce(into: unique) { (result, element) in
    if let last = result.last, last == element {
    } else {
        result.append(element)
    }
}
print(array)
struct StreetName: Hashable {
    static func == (lhs: StreetName, rhs: StreetName) -> Bool {
        return lhs.name == rhs.name
    }

    var hashValue: Int {
        return name.hashValue
    }

    var name: String
    var polyLine: CLLocationCoordinate2D
}
DataManager.shared.getStreetNames { (returnedNamesSet) in
    var namesArray: [StreetName] = Array(returnedNamesSet)

    self.streetNames = namesArray.sorted(by: {$0.name < $1.name})
    self.filteredStreetNames = self.streetNames
    OperationQueue.main.addOperation {
        self.streetTableView.reloadData()
    }
}