2017-01-26 31 views
3

我有CGPoint S中的數組數,如何獲得次重複CGPoint陣列的每個值

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)] 

我怎樣才能得到重複每個點的次數?

+0

之間有一個看看'NSCountedSet' – Alexander

+0

@LeoDabus因此,使它可哈希:) – Alexander

+0

@LeoDabus當然是。它已經由馬丁爲你完成了:) – Alexander

回答

2

由於@Alexander在評論中說,你應該使用一個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

+0

鏈接到一個散列函數'NSCountedSet'需要'Hashable'爲好。我相信幕後的訣竅是這些值被轉換爲「NSValue」。 – Sulthan

+0

'NSCountedSet'有兩個初始化: '方便市民的init(數組:[任何])'和' 方便市民的init(設置:設置)' 你只需要'Hashable'傳遞'Set'爲'初始化' (我在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 
+0

這是我認爲最好的方法。你也可以讓CGPoint Hashable返回相應的NSValue hashValue –

+0

請注意,你可以從CGPoint投射到NSValue而不是創建一個新的對象 –

+1

@LeoDabus是的,我認爲這是Swift 3.0.1中的全新內容,而且我還沒有習慣了。 – matt

0

我同意馬特認爲接受的解決方案看起來像是矯枉過正。我建議類似的方法,他拿了一個,但使用功能的風格:

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字典的密鑰,而不需要編寫自定義代碼; 然而,亞歷山大在下面的評論指出,這是不產生Hashable值最高效的方式,如果您在操作上幾萬點以上,或者在一個循環中頻繁運行此操作,差別使用String(describing:)與自定義函數來從你的觀點元組的哈希可能是顯著

+0

使用'String(描述:)'作爲散列技術是不必要的昂貴且容易碰撞 – Alexander

+0

@Alexander一般來說,是的。在這種情況下,使用'(CGFloat,CGFloat)'值的數組,它不應該相互碰撞,並且不能使元組成爲'Hashable'。在CGPoint上使用'Hashable'擴展或在'NSValue'中包裝'CGPoint'是在其他情況下可以替代的實現,但是這個答案提供了:1)算法的功能樣式2)工作的解決方案與OP的示例輸入一起,前面的答案都沒有涵蓋 –

+0

當然,這會導致衝突。你用1'Int''hashValue'來代表'CGPoint'的2'Int's。沒有任何功能可以映射這些1:1。你的算法是好的,但是爲'CGPoint'定義的恰當的哈希函數會更好,而不是使用'String(描述:)' – Alexander