如何获取CGPoint数组的每个值重复的次数

Posted

技术标签:

【中文标题】如何获取CGPoint数组的每个值重复的次数【英文标题】:how to get the number of times each value of a CGPoint array is repeated 【发布时间】:2017-06-11 13:56:37 【问题描述】:

我有一个CGPoints 数组,

pointArray = [(532.7, 150.0), (66.6, 150.0), (129.2, 150.0), (129.2, 150.0), (301.2, 150.0), (444.2, 150.0), (532.7, 150.0), (532.7, 150.0), (532.7, 150.0)]

如何获取每个点重复的次数?

【问题讨论】:

看看NSCountedSet @LeoDabus 所以让它成为 Hashable :) @LeoDabus 当然是。 Martin 已经为您完成了 :) @LeoDabus 看来你现在需要它了。将 CGPoints 转换为字符串以用作字典中的键实际上是一种非常糟糕的散列算法。只需将其换成设计更好的算法,就像马丁发布的那样,并使用与您目前相同的算法 @LeoDabus 在大多数情况下什么可以正常工作? 【参考方案1】:

正如@Alexander 在 cmets 中所说,您应该使用 NSCountedSet 您可以这样使用它:

let array = [
    CGPoint(x: 532.7, y: 150.0),
    CGPoint(x: 66.6, y: 150.0),
    CGPoint(x: 129.2, y: 150.0),
    CGPoint(x: 129.2, y: 150.0),
    CGPoint(x: 301.2, y: 150.0),
    CGPoint(x: 444.2, y: 150.0),
    CGPoint(x: 532.7, y: 150.0),
    CGPoint(x: 532.7, y: 150.0),
    CGPoint(x: 532.7, y: 150.0)
]

let countedSet = NSCountedSet(array: array)
countedSet.count(for: array.last!) //returns 4

如果您不想使用NSCountedSet,您可以将字典中的每个点存储为键,并将计数存储为值。棘手的是CGPoint不符合Hashable,你可以这样做:

extension CGPoint:Hashable
    public var hashValue: Int 
        let x = Double(self.x)
        let y = Double(self.y)

        return Int(((x + y)*(x + y + 1)/2) + y)
        //this hash function may not be the best for your data

    


var map = [CGPoint:Int]()
for point in array 
    if let count = map[point] 
        map[point] = count + 1
     else 
        map[point] = 1
    


map[array.last!]//returns 4

我认为最好使用NSCountedSet

【讨论】:

NSCountedSet 也需要Hashable。我相信幕后的技巧是这些值被转换为NSValue NSCountedSet 有两个初始化器:public convenience init(array: [Any])public convenience init(set: Set<AnyHashable>) 你只需要 HashableSet 传递给 init (我使用 Swift 3.0.2 和 Xcode 8.2。 1)【参考方案2】:

我认为使用 CountedSet 是多余的。我只会数他们:

let points = [CGPoint(x: 532.7, y: 150.0), CGPoint(x: 66.6, y: 150.0), CGPoint(x: 129.2, y: 150.0), CGPoint(x: 129.2, y: 150.0), CGPoint(x: 301.2, y: 150.0), CGPoint(x: 444.2, y: 150.0), CGPoint(x: 532.7, y: 150.0), CGPoint(x: 532.7, y: 150.0), CGPoint(x: 532.7, y: 150.0)]
var d : [NSValue:Int] = [:]
for p in points 
    let v = NSValue(cgPoint:p)
    if let ct = d[v] 
        d[v] = ct+1
     else 
        d[v] = 1
    

// how many times does `points[0]` appear in `points`?
d[NSValue(cgPoint:points[0])] // 4

【讨论】:

这是我认为最好的方法。您还可以使 CGPoint Hashable 返回其对应的 NSValue hashValue 请注意,您可以只从 CGPoint 转换为 NSValue 而不是创建新对象 @LeoDabus 是的,我认为这在 Swift 3.0.1 中是全新的,但我仍然不习惯。【参考方案3】:

我同意 matt 的观点,即接受的解决方案似乎有些矫枉过正。我建议采用与他采用的方法类似的方法,但使用功能样式:

let dictionary:[String:Int] = array.reduce([:])
    var dict = $0.0, key = String(describing: $0.1)
    dict[key] = (dict[key] ?? 0 ) + 1
    return dict

使用这种方法,使用您的原始数组,我们得到如下结果:

let pointArray:[(CGFloat, CGFloat)] = [(532.7, 150.0), (66.6, 150.0), (129.2, 150.0), (129.2, 150.0), (301.2, 150.0), (444.2, 150.0), (532.7, 150.0), (532.7, 150.0), (532.7, 150.0)]

let dictionary:[String:Int] = pointArray.reduce([:])
    var dict = $0.0, key = String(describing: $0.1)
    dict[key] = (dict[key] ?? 0 ) + 1
    return dict


print(dictionary)  //output is ["(129.2, 150.0)": 2, "(66.6, 150.0)": 1, "(532.7, 150.0)": 4, "(444.2, 150.0)": 1, "(301.2, 150.0)": 1]

请注意,使用 String(describing:) 作为字典键允许它与示例代码中的元组以及其他不一定是 CGPointHashable 实例的变体一起使用。这将为您提供字典的Hashable 键,而无需编写自定义代码; 然而,正如 Alexander 在下面的 cmets 中指出的那样,生成 Hashable 值并不是最高效的方式,如果您在数万个或更多点上进行操作,或者在循环,使用 String(describing:) 与自定义函数从点元组生成哈希之间的区别可能很大

【讨论】:

使用 String(describing:) 作为散列技术是不必要的昂贵且容易发生冲突 @Alexander 一般来说,是的。在这种情况下,使用他的(CGFloat, CGFloat) 值数组,它不应该发生冲突,并且您不能创建元组Hashable。在CGPoint 上使用Hashable 扩展名,或在NSValue 中包装CGPoints 是可以在其他情况下替代的实现,但此答案提供:1)算法的功能样式2)有效的解决方案使用 OP 的示例输入,以前的答案都没有涵盖这些输入 当然会造成碰撞。你用 1 个 Int hashValue 代表 CGPoint 的 2 个 Ints。不存在任何可以映射这些 1:1 的函数。您的算法很好,但是使用为CGPoint 定义的适当哈希函数而不是使用String(describing:) 会更好 @Alexander CGPoint 不包含 Ints 并且 OP 示例代码根本没有使用 CGPoint;它使用 Floats / Doubles 的元组。无法在元组上创建Hashable 扩展,String(describing:) 输出一个String,它对于元组中Floats 的每个唯一组合都是唯一的,并且不会与String 的哈希冲突其他组合的值。如果您查看我的示例代码中的打印输出,您将看到 String 键。除非String 有一个糟糕的哈希实现(它没有),否则这些都不会相互冲突 每个元组都需要在堆上动态分配以包含字符串的缓冲区。对于任何非平凡数量的元组,自己实现一个简单的散列函数都会带来令人信服的性能优势。尤其是在移动设备上,这是 Swift 的主要目标之一。

以上是关于如何获取CGPoint数组的每个值重复的次数的主要内容,如果未能解决你的问题,请参考以下文章

如何找出数组中出现次数超过长度一半的元素

SQL-如何在聚合操作期间获取每组重复次数最多的文本值?

如何复制数组中不同次数的元素?

Vuejs - 如何使用 v-for 获取数组中的所有唯一值(删除重复项)

JS判断一个数组中是不是有重复值的三种方法

如何获取GROUP CONCAT(php,mysql)的每个不同值的出现次数