質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
多次元配列

1次元配列内にさらに配列を格納している配列を、多次元配列と呼びます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

1回答

1150閲覧

辞書の入った配列から一つ辞書を取り出し、変更して戻したい。

data7600

総合スコア18

多次元配列

1次元配列内にさらに配列を格納している配列を、多次元配列と呼びます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2020/04/24 11:20

前回の質問の続きのような感じです。

Swift

1var dicArray:[[String:Any]] = [["name":"田中","age":45,"height":167], 2 ["name":"佐藤","age":44,"height":180], 3 ["name":"鈴木","age":44,"height":177], 4 ["name":"高橋","age":45,"height":169], 5 ["name":"黒田","age":44,"height":172], 6 ["name":"山本","age":44,"height":179]]

前回、上記の配列からageが44の辞書を取り出してheightの順で並べ替えて下記の様にする所まで出来ました。

Swift

1var dicArray2:[[String:Any]] = [["name":"黒田","age":44,"height":172], 2 ["name":"鈴木","age":44,"height":177], 3 ["name":"山本","age":44,"height":179], 4 ["name":"佐藤","age":44,"height":180]]

ここから辞書を一つ取り出して内容を変更して、dicArrayに戻したいのですがどうすればよいかわかりません。
例えば下記のように変更したものを戻したいのです。

Swift

1var dic = dicArray2[0] 2dic.updateValue(45, forKey: "age") 3dic.updateValue(171, forKey: "height")

もとのdicArrayから変更する前のdicを探して削除して値の変更後のdicを追加すれば良いと思うのですが、もとのdicArrayから変更する前のdicを探す方法がわかりません。

let index = dicArray.index(of: dic)
では
Protocol type 'Any' cannot conform to 'Equatable' because only concrete types can conform to protocols
とエラーが表示されます。

なにか方法があれば宜しくおねがいします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TsukubaDepot

2020/04/24 11:47

想定されている仕様がよく分からないのですが、dicArray のうち、44歳の人の年齢を45歳にした上、身長を一律に171センチにしたい、ということでしょうか。
data7600

2020/04/24 12:04

そうですね。44歳のリストを作った上でその中から一人を取り出して内容を変更して元のリストに戻したいのです。 tableviewで表示予定です。
TsukubaDepot

2020/04/24 12:07

ということは、最終的にはdicArrayでage==44歳の人のうち、「いずれか任意の」一人を抜き出し、その人だけ年齢や身長だけ更新したい、ということでしょうか。
TsukubaDepot

2020/04/24 12:22

ちなみに、氏名に重複はない、という仮定でしょうか。 仮に指名に重複があるとすると、オリジナルを特定するのは難しくなるので(前回の質問だと、44歳で抜き出したあと身長順でソートしているので、尚更オリジナルの順番がわからなくなるため)、idを振るなりした方が処理は簡単になりそうに思います。
data7600

2020/04/24 12:40

これはサンプルで考えたので、もともとのリストはすべてのキーで重複があります。 元のリストから特定の要素で取り出してソートしたリストをtableviewに表示して、それを元にリストに戻したいのです。要素の増減はないですが、リストの増減はあります。 idも手ですね。
guest

回答1

0

ベストアンサー

index(of:)(このメソッドは非推奨となり、firstIndex(of)を推奨)に与えることができる引数は、Equatableプロトコルに準拠している必要があります(内部で要素の比較を行うため)。

しかし、辞書に含まれているAny型は具体的な型(concrete type)がないため比較することができないためエラーとなります。

辞書(Dictionary)をEquatableにできるか否かは私の知識ではわかりませんでした。

一番簡単なのは、前回のご質問で ch3cooh さんがご指摘のように、データを構造体として扱い、その構造体をEquatableに準拠させることです。

準拠させることで、以下のような処理が可能となります。

swift

1struct User: Equatable { 2 let name: String 3 let age: Int 4 let height: Int 5} 6 7let dicArray: [User] = [ 8 User(name: "田中", age: 45, height: 167), 9 User(name: "佐藤", age: 44, height: 180), 10 User(name: "鈴木", age: 44, height: 177), 11 User(name: "高橋", age: 45, height: 169), 12 User(name: "黒田", age: 44, height: 172), 13 User(name: "山本", age: 44, height: 179) 14] 15 16let dic = dicArray[3] 17 18if let firstIndex = dicArray.firstIndex(of: dic) { 19 // インデックス番号が表示される 20 print(firstIndex) 21}

辞書のvalueAny型になってしまうとその後の扱いがやや厄介(キャストが増える)なので、やはり構造体を使って処理されるのが見通しが良くなるのではないかと思います。


辞書を使ったままインデックスを求める方法と、抽出したデータから元のデータを書き換える方法も考えていたので、そちらも載せておきます。
もちろん、辞書を構造体に書き換えれば同じようなロジックでいけるはずです。

もっといい方法があるかもしれませんが、参考になれば幸いです。

Playgound で実行できるようになっています。

swift

1import UIKit 2 3var dicArray:[[String:Any]] = [["name":"田中","age":45,"height":167], 4 ["name":"佐藤","age":44,"height":180], 5 ["name":"鈴木","age":44,"height":177], 6 ["name":"高橋","age":45,"height":169], 7 ["name":"黒田","age":44,"height":172], 8 ["name":"山本","age":44,"height":179]] 9 10func pickup(array: [[String: Any]]) -> [[String: Any]] { 11 return array.filter { (e) -> Bool in 12 return (e["age"] as! Int) == 44 13 }.sorted { (x, y) -> Bool in 14 return (x["height"] as! Int) < (y["height"] as! Int) 15 } 16} 17 18// tableView を表示したつもり 19func printTable(array: [[String: Any]], title: String = "") { 20 print(title) 21 for col in array { 22 print(col) 23 } 24 print("") 25} 26 27// 指定秒数待つ 28func waitSec(sec: Double) { 29 Thread.sleep(forTimeInterval: sec) 30} 31 32// ここから本題 33// オリジナルを表示 34printTable(array: dicArray, title: "** original **") 35waitSec(sec: 2.0) 36 37// 原本から44歳のみをコピー 38var dicArray2 = pickup(array: dicArray) 39 40// tableView に表示したつもり 41printTable(array: dicArray2, title: "** tableView **") 42waitSec(sec: 2.0) 43 44// 「山本」さんのみ変更する -> オリジナルのデータを変更する 45// オリジナルから、山本さんが含まれる最初のインデックスを得る 46let target = "山本" 47guard let index = dicArray.firstIndex(where: { ($0["name"] as! String).contains(target) }) else { 48 // 目的の名前が見つからなかった場合。 49 // ここでは処理の都合fatalError で強制的に落としているが、実際は適切に処理する 50 fatalError("(target) さんは見つかりませんでした") 51} 52print("(target)さんのデータを書き換えます。") 53print("index: (index)") 54 55// オリジナルから更新したいデータを更新 56dicArray[index].updateValue(45, forKey: "age") 57dicArray[index].updateValue(171, forKey: "height") 58waitSec(sec: 2.0) 59 60// 書き換えたあとのオリジナルを表示 61printTable(array: dicArray, title: "** original **") 62waitSec(sec: 2.0) 63 64// オリジナルから44歳のみをコピー 65dicArray2 = pickup(array: dicArray) 66 67// tableView に表示したつもり 68printTable(array: dicArray2, title: "** tableView **")

投稿2020/04/24 14:10

TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

data7600

2020/04/24 15:41

なるほど。構造体を使ったほうがスッキリしそうですね。 まだ構造体を使ったことがないのでもう少し調べてみます。
data7600

2020/04/27 10:06

構造体を使ってやりたいことが出来ました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問