

Swiftの配列から何回重複しているかを判別して重複している回数を出力することはできますか？  
例えば[0,0,0,2,2,3,4,4]という配列があったら

0:3回  
1:0回  
2:2回  
3:1回  
4:2回  
[0,2,4,3]//重複した回数が多い順

のようにprintすることはできますか？  
やり方があったら教えてください


CC BY-SA 4.0
2019-03-27T05:46:42.400
53754
2019-03-27T19:08:09.840
2019-03-27T10:59:04.520
32684
32684
question
swift xcode array
配列から重複している回数を出力


後半のみの回答になりますが、この回答の関数を複製して、途中で`Dictionary`を返す様にすれば、各要素の回数の`Dictionary`も得ることが出来ます。

もっとエレガントな方法もあると思いますが、内容を細かく追えて改造がしやすい様に冗長気味に書いていますが

[code]

        // 元データー
    let original: Array = [0,0,0,2,2,3,4,4]
    
    
        // 関数定義
    func sortByCount(ofArray array: Array<Int>) -> Array<Int> {
        var dictForCount: Dictionary<Int, Int> = Dictionary()
        for item: Int in array {
            if let count: Int = dictForCount[item] {
                dictForCount[item] = count + 1
            } else {
                dictForCount[item] = 1
            }
        }// end foreach print(dictForCount)
        let result = (dictForCount.sorted {
            if ($0.value == $1.value) {
                return $0.key < $1.key
            } else {
                return $0.value > $1.value
            }// end if value is same
        })// end sort closure
        var resultArray: Array<Int> = Array()
        for item in result {
            resultArray.append(item.key)
        }// end foreach append key of dictionary
    
        return resultArray
    }
    
        // 適用結果
    let orderOfCount: Array<Int> = sortByCount(ofArray: original)
    print(orderOfCount)     // => [0, 4, 2, 3]
    
[/code]

こんな感じで可能だと思います。


CC BY-SA 4.0
2019-03-27T12:55:24.800
53766
2019-03-27T13:10:42.710
2019-03-27T13:10:42.710
14745
14745
53754
answer


NSCountedSetを使います。

[code]

    let a = [0,0,0,2,2,3,4,4]
    
    let counter = NSCountedSet(array: a)
    
    let counts = [0, 1, 2, 3, 4].map { ($0, counter.count(for: $0)) }
    
    counts.forEach { print("\($0.0):\($0.1)回") }
    print(counts.sorted(by: { l, r in l.1 > r.1 }).map({ $0.0 }))
    
[/code]


CC BY-SA 4.0
2019-03-27T15:37:05.323
53771
2019-03-27T15:37:05.323
2741
53754
answer


Swiftをご指名で、単に一般的なアルゴリズムを聞きたいんではないと言うことのようなんで、できるだけSwiftの`Collection`型の機能を活かすように書いてみました。

### 重複要素を数える部分

結果を保持するのには`Dictionary<T, Int>`(`[T:
Int]`)を使います。`T`は元配列の要素型。今は`T=Int`ですからこんな感じになります。

昔のSwift(<4.0)だと、`Dictionary`の中にすでに存在するかどうか調べないといけなかったので、本質的じゃないところでコードが複雑になってしまっていたんですが、今は`subscript(_:default:)`があるので、すっきりかけます。

[code]

    let arr = [0,0,0,2,2,3,4,4]
    
    //配列の重複要素を数える
    var numToCount: [Int: Int] = [:]
    for elt in arr {
        numToCount[elt, default: 0] += 1
    }
    print(numToCount) //->[2: 2, 0: 3, 3: 1, 4: 2]
    
[/code]

ご覧のように、結果となる`Dictionary`は`key`がそれぞれの整数値、`value`がカウント値となっています。普通に出力すると順不同になるのは、Swiftの`Dictionary`型の特性なので、順番に出力する場合、一工夫してやらないといけません。

### 順番に出力する

一度も現れない要素も「0回」と出力したいようなので、`min()`から`max()`までの間でループします。

[code]

    //順番に出力する
    for i in arr.min()!...arr.max()! {
        print("\(i):\(numToCount[i, default: 0])回")
    }
    
[/code]

`min()!`,
`max()!`のように`!`を使っていますから、`arr`が空配列だとクラッシュします。ここでは意図的に使っていますが、実際のアプリでは「自分はこの部分100%絶対`nil`じゃない自信がある。自分が間違っていたらクラッシュして知らせてくれ。」くらいの意図がない限り`!`を使うのはやめておきましょう。

### 重複した回数が多い順

`Dictionary`にもソート系のメソッドがありますから、活用できるようにしましょう。比較クロージャに渡される引数はkey-value
pairなので、カウント値を表すvalueの降順に並べないといけません。

[code]

    //重複した回数が多い順
    let rank = numToCount.sorted { (-$0.value, $0.key) < (-$1.value, $1.key) }.map{$0.key}
    print(rank)
    
[/code]

比較クロージャに、やや変則的な書き方を使っています。普通は「valueの降順」に並べるなら`{ $0.value > $1.value
}`とか書きます。ただ、それだと同点の値が順不同で出力されてしまうので、tupleの比較を行っています。これもあまり活用されていないように思うので、無理に使ってる感はありますが。

`(A1,B1) < (A2,B2)`と言う比較では、第一要素が同点`A1 == A2`の時は`B1 < B2`と同じになります。

並べ替えの基準に使いたいのはカウント値なので、`value`が第一要素、同点の場合は元の値の昇順で並べます。カウント値については降順ですから、そちらだけ`-`を付けるなんて、なかなか姑息なことをしています。

最後の`.map{$0.key}`は、key-value pairの配列から、`key`(元の整数値)だけ取り出すものです。

### 出力例

ご質問内の例通りの`arr`を与えるとこんな出力が得られます。

[code]

    [2: 2, 0: 3, 3: 1, 4: 2]
    0:3回
    1:0回
    2:2回
    3:1回
    4:2回
    [0, 2, 4, 3]
    
[/code]

一行目はご不要だったかも知れませんね。

* * *

Swiftの`Collection`型にはあれこれ便利なメソッドが揃っています。(他言語のライブラリにはあって、Swiftにはないものも一杯ありますが。)慣れないと動作を理解するのも???なものもあるかも知れませんが、是非有効活用できるようになってください。


CC BY-SA 4.0
2019-03-27T19:08:09.840
53773
2019-03-27T19:08:09.840
13972
53754
answer