

テーブルビューのセルに辞書型の配列を表示させるコードで、辞書のキー配列やデータを取得するところがわかりません。ご教示いただけますか。ご回答に感謝します。

エラーは、次のとおりです。  
①Ambiguous reference to member 'subscript'（109行目）  
②Value of type '[[String : Bool]]' has no member 'updateValue'（123、148行目）  
③Value of type '[[String : Bool]]' has no member 'keys'」（152行目）  
ネットで探して試しましたが、解決できるものは見つかりませんでした。

[code]

    import UIKit
    
    class ViewController : UIViewController, UITableViewDelegate, UITableViewDataSource {
        
        ////
        func numberOfSections(in tableView: UITableView) -> Int // Default is 1 if not implemented
        { return sectionTitle.count
        }
        ////
        func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? // fixed font style. use custom view (UILabel) if you want something different
        {
            return sectionTitle[section]
        }
        
        
        // ステータスバーの高さ、上にカレンダーかデイトピッカーを置く。
        let statusBarHeight = UIApplication.shared.statusBarFrame.height + 200
        
        //セクションの項目
        var sectionTitle = ["H28,01,23","H27,12,31","H28,01,12","H28,02,21","H28,11,10"]
        
        
        // チェックリストの項目とチェック状態
        var checkListItem1: [String : Bool] = [
            "アイテム1" : true,
            "アイテム2" : false,
            "アイテム3" : true,
            "アイテム4" : true,
            "アイテム5" : false
        ]
        var checkListItem2: [String : Bool] = [
            "アイテム2-1" : false,
            "アイテム2-2" : true,
            "アイテム2-3" : true,
            "アイテム2-4" : true,
            "アイテム2-5" : false
        ]
        var checkListItem3: [String : Bool] = [
            "アイテム3-1" : true,
            "アイテム3-2" : true,
            "アイテム3-3" : true,
            "アイテム3-4" : true,
            "アイテム3-5" : false
        ]
        var checkListItem4: [String : Bool] = [
            "アイテム4-1" : true,
            "アイテム4-2" : false,
            "アイテム4-3" : true,
            "アイテム4-4" : false,
            "アイテム4-5" : false
        ]
        var checkListItem5: [String : Bool] = [
            "アイテム5-1" : true,
            "アイテム5-2" : false,
            "アイテム5-3" : true,
            "アイテム5-4" : true,
            "アイテム5-5" : true
        ]
    //    var tableData = [checkListItem1, checkListItem2, checkListItem3, checkListItem4, checkListItem5]
      var tableData: [[String: Bool]] = []
        
        let tableView = UITableView()
    
    override func viewDidLoad() {
            super.viewDidLoad()
            tableData = [checkListItem1, checkListItem2, checkListItem3, checkListItem4, checkListItem5]
            
            // UITableView の作成
            tableView.frame = CGRect(
                x: 0,
                y: statusBarHeight,
                width: self.view.frame.width,
                height: self.view.frame.height - statusBarHeight
            )
            tableView.delegate = self
            tableView.dataSource = self
            self.view.addSubview(tableView)
        }
        
        // セルの作成
        //
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            
            // Dictonary のキーの配列を取得
           //        string.characters.count 
           var keys = [String](tableData.keys)  //＜ーエラー①
    
            
            // キーで並び替え
            keys.sort()
            
            // キーの文字列を取得
            let cellText = keys[indexPath.row]
            
            // セルの作成とテキストの設定
            let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
            cell.textLabel?.text = cellText
            
            ///
            let sectionData = tableData[(indexPath as NSIndexPath).section]//＜ーエラー②
            let cellData = sectionData[(indexPath as NSIndexPath).row]
            
            
            
            // チェック状態が true なら、初めからチェック状態にする
            if self.tableData[cellText]! {
                cell.imageView?.image = UIImage(named: "checked")
            } else {
                cell.imageView?.image = UIImage(named: "unchecked")
            }
            
            return cell
        }
        
        // セルがタップされた時の処理
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            
            if let cell = tableView.cellForRow(at: indexPath) {
                
                // タップしたセルのテキストを取得
                let cellText = cell.textLabel?.text
                
                // 画像を切り替えと Dictonary の値を変更
                if cell.imageView?.image == UIImage(named: "checked") {
                    
                    self.tableData.updateValue(false, forKey: cellText!)//＜ーエラー③
                    cell.imageView?.image = UIImage(named: "unchecked")
                } else {
                    
                    self.tableData.updateValue(true, forKey: cellText!)//＜ーエラー③
                    cell.imageView?.image = UIImage(named: "checked")
                }
                
                // 選択状態を解除
                cell.isSelected = false
            }
        }
        
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            let sectionData = tableData[section]
            return self.tableData.count
        }    
    }
    
[/code]

[前回の質問](https://ja.stackoverflow.com/questions/31318/%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%83%93%E3%83%A5%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E4%B8%AD%E3%81%AE-
cannot-use-instance-member-%E3%82%A8%E3%83%A9%E3%83%BC)  
テーブルビューを作成中の「Cannot use instance member…」エラー


CC BY-SA 3.0
2016-12-23T02:40:49.753
31323
2017-03-29T07:25:00.073
2020-06-17T08:14:45.997
-1
13657
question
swift swift3
テーブルビューセルに辞書型データを表示させるコードで、キー配列やデータ取得の問題が


前回の質問にあるようなSwift言語特有の事情に基づくエラーと異なり、今回のようにデータ型に関する勘違いが原因の場合には、エラーメッセージ等で検索をかけても解決策を見つけるのは難しいでしょう。

あなたのコードで`tableData`のデータ型は`[[String: Bool]]`(これは`Array<Dictionary<String,
Bool>>`と同じ)であり、『「`String`から`Bool`への辞書型」を要素とする配列型』であると言うことです。

一旦要素型のことを置いておけば、

  * **`tableData`は配列(`Array`)型である**

と言うことですね。

あなたは`tableData`に5つの辞書型データ(`checkListItem1`〜`checkListItem5`)を入れていますが、Swift言語やiOSのフレームワークがその中からうまくいいものを選んで`tableData`を辞書型としてアクセスできるようにする、なんてことは起こりません。

* * *

_**①Ambiguous reference to member 'subscript'**_

※行番号はStackOverflowのコードサンプルでは表示されない上に、数字の方も大きくずれてしまっているようですね。`//<-①`などのようにソースコード中にコメントの形でエラーが発生する行を明示してもらうと間違いもなくエラー箇所が見やすくなるかと思います。

というわけで①はこの箇所のことでしょうか?

[code]

        let sectionData = tableData[(indexPath as NSIndexPath).section]
        let cellData = sectionData[(indexPath as NSIndexPath).row] //<-①
    
[/code]

先に書いたように、`tableData`は『「`String`から`Bool`への辞書型」を要素とする配列型』ですから、それに整数値(`(indexPath
as
NSIndexPath).section`、ちなみにこれは`indexPath.section`と書けます)で添字付けして取り出した`sectionData`は「`String`から`Bool`への辞書型」と言うことになります。`[]`の中に入れて使えるのは`String`型の値(他にもあるのですが普通使わないので省略)だけで、整数型の値である`(indexPath
as NSIndexPath).row`(これも`indexPath.row`で構いません)は使用できないと言うことです。

エラーメッセージにあるように _Ambiguous_
(曖昧)と言われてしまうと、曖昧さをなくしてはっきりさせるような手段があるのかと思ってしまいますが、この辺のSwiftのエラーメッセージはまだまだ発展途上で、「'subscript'に関してデータ型に関するエラーがある」程度の意味として読んでやらないといけません。

* * *

_**②Value of type '[[String : Bool]]' has no member 'updateValue'**_

ここと:

[code]

                self.tableData.updateValue(false, forKey: cellText!) //<-②
    
[/code]

ここのことでしょうか:

[code]

                self.tableData.updateValue(true, forKey: cellText!) //<-②
    
[/code]

まさに最初に書いたことがそのまま当てはまるのですが、`updateValue(_:forKey:)`は、辞書型に対して定義されているメソッドですので、配列型に対しては使えません。配列型である`[[String
: Bool]]`には`updateValue`なんてメソッドもプロパティもない(メソッドやプロパティを総称して _member_
と言っています)ですよと言うエラーです。

※ちなみにSwiftの辞書型の値を単に更新したい場合、普通`updateValue(_:forKey:)`は使いません。

* * *

_**③Value of type '[[String : Bool]]' has no member 'keys'**_

こちらのことでしょうか。

[code]

        var keys = [String](tableData.keys) //<-③
    
[/code]

ほとんど同じことの繰り返しになりますが、`tableData`は配列型であるのに(辞書型のプロパティである)`keys`を使っていると言うことで、②と同じエラーが表示されているわけです。

* * *

## どう修正するのか

最初に書いたように`tableData`は配列で5個の要素を持っているのに、その中の1個を選ばずに(まるで1個が自動的に選ばれて)辞書型になっているかのようにアクセスしているのが問題なのですから、「自分でちゃんと`tableData`の中から1個を選んでやって、その1個を辞書型として使う」ようにすれば良いと言うことになります。

ついでに関連する部分の修正も入れてしまいましたが、具体的には以下のようになります。`viewDidLoad()`以下の3メソッドが修正対象です。

[code]

    // セルの作成
    //
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //###まず最初に`tableData`の中からどの1個を使うのか、選ばないといけない
        let sectionData = tableData[indexPath.section] //<-`sectionData`は辞書型(`[String: Bool]`)になる
        
        //###`sectionData`(辞書型)のキーの配列を取得して並び替え
        //###`sectionData.keys`はもともと`[String]`型になるので型変換は不要
        //###並べ替え前のデータは不要なので、`sorted()`を使って並べ替え後のデータだけを作成する
        let keys = sectionData.keys.sorted() //<-③
        
        // キーの文字列を取得
        let cellText = keys[indexPath.row]
        
        // セルの作成とテキストの設定
        //###ここは本来なら
        //let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
        //または
        //let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        //としないといけないのだが、質問のエラーに関係ない部分で長くなるのでとりあえずそのまま
        let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
        cell.textLabel?.text = cellText
        
        //###`sectionData`は`[String: Bool]`なので`String`型の値で添字付け
        //###`cellData`は`Bool?`(==`Optional<Bool>`)型になる
        //###本来は`cellData`のように抽象的な名前でなく`cellIsChecked`などのわかりやすい名前にした方が良い
        let cellData = sectionData[cellText] //<-①
        
        // チェック状態が true なら、初めからチェック状態にする
        //###Swiftの条件節は`Bool`だけを受け付けるので普通は`== true`とは書かないが、`cellData`は`Bool?`型なのでこの書き方が便利
        if cellData == true {
            cell.imageView?.image = UIImage(named: "checked")
        } else {
            cell.imageView?.image = UIImage(named: "unchecked")
        }
        
        return cell
    }
    
    // セルがタップされた時の処理
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        if let cell = tableView.cellForRow(at: indexPath) {
            
            // タップしたセルのテキストを取得
            //###`Optional<String>`のままでは使いづらいので`??`演算子でデフォルト値を与えておく
            let cellText = cell.textLabel?.text ?? ""
            
            // 画像を切り替えと Dictonary の値を変更
            if cell.imageView?.image == UIImage(named: "checked") {
                
                //###`tableData`の何番目のデータを更新するのか選ばないといけない
                //###辞書型の値の更新は、通常subscriptで行う
                self.tableData[indexPath.section][cellText] = false
                cell.imageView?.image = UIImage(named: "unchecked") //<-②
            } else {
                
                //###`tableData`の何番目のデータを更新するのか選ばないといけない
                //###辞書型の値の更新は、通常subscriptで行う
                self.tableData[indexPath.section][cellText] = true
                cell.imageView?.image = UIImage(named: "checked") //<-②
            }
            
            // 選択状態を解除
            cell.isSelected = false
        }
    }
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let sectionData = tableData[section]
        //###`tableData`の要素数はnumberOfRowsではなく、`numberOfSections`として使う
        //⇒`tableData`の要素数は`sectionTitle`の要素数と同じでないといけない
        return sectionData.count
    }
    
[/code]

他にもまだ細かい部分で別のエラーが出るかもしれませんが、とりあえず上記のような修正で、今回ご質問に挙げられたようなエラは出なくなると思います。お試しください。


CC BY-SA 3.0
2016-12-23T04:20:06.957
31325
2016-12-23T04:20:06.957
2020-06-17T08:14:45.997
-1
13972
31323
answer