Swiftでタプル要素で構成される配列から特定のタプルを削除する

データの保ち方については常に頭を悩ませ、試行錯誤する処です。 今回は手元のプログラムの進捗上、異なる型を一まとめにしたデータ単位を配列にして保たせようと試みました。 異なる型を一まとめにするとなると開発環境はiPhoneアプリを拵える為のプログラム言語 Swift では Tupleタプル が適当と考え採用しました。 クラスから生成した一つ一つのインスタンスのデータを其々タプルにまとめ、 其れ等タプルをまとめて配列で扱おう、と言う目論見です。

Swiftのタプル型

あちこち食堂(仮題)開発途中画面
あちこち食堂(仮題)開発途中画面

動的なデータの保たせ方としてタプルのみで構成された配列を採用したのには、 タプルと言うデータはな不可変長ではありますが、 要素に型を混在させられるので、 型の異なる要素をひとまとめにしたい時は便利である、と言う理由です。 Swiftのタプル型については はじはじアプリ体験記 の以下のリンク先ページが役立つかも知れません。

【Swift】タプルの使い方。タプルの宣言と値の格納、取得方法

スポンサーリンク
日付:2018年6月8日
開発機:MacBook Pro (13-inch, Late 2016, Four Thunderbolt 3 Ports)
MacOSバージョン:macOS High Sierra 10.13.5
Xcodeバージョン:9.4
言語:Swift 4.1
主関連アプリ:あちこち食堂(仮題)

手元の開発ゲームアプリでは次々と生成されるインスタンスが其々ユニークなIDを保有します。 此の整数を要素に保つ配列を idAry とし、 今回、主眼となるタプルを要素に保つ配列は tupleAry として、idAryと同期しながら処理を進めます。 宣言は大凡以下の様な形式で宣言します。

var tupleAry:[(ID:Int, cX:Int, cY:Int, mFlg:Bool, type:String)] = []

此れ等、整数型やブール型、文字列型のタプルの要素は インスタンスが生成された時点でtupleAryに収めます。

tupleAry.append((ID: Int(obj.name!)!, cX:obj.cX, cY:obj.cY, mFlg:false, type:obj.uType))

従ってタプル配列に情報の収められたインスタンスがゲーム内で要らなくなった時点で、 インスタンス廃棄と共に関連する当該配列内のタプルを削除する必要があります。 処で配列に於いて特定の要素を削除する為には通常 Array.index(of: elem) で以てキー値を探り、該当キー値の要素をremoveするでしょう。 敢えて此処に於けるタプル配列に適用すれば以下の様になるかも知れません。

tupleAry.remove(at: tupleAry.index(of: elem)!)

アプリ仕様の要請

此処で elem は配列内の削除したい特定の要素を指しますので、 タプル配列に於いてはタプルになります。 タプル要素の内容については、 特別に当該タプルを何某か変数に保存して適用しても良いのですが、 他のデータとの連携にIDを媒介とする他処理も鑑みた時、 出来ればIDで特定したい要請があります。 インスタンスの管理はIDで処理しているからです。 例えばidAryでは削除したいIDをInt(id)とすると、以下の様にすれば削除は叶うでしょう。

idAry.remove(at: id.index(of: id)!)

しかし要素がタプルの場合は、elemの下のキー値[0]要素の指定はなかなか難儀です。 何とかならないものかと例によって補完機能に依れば、次の様な補完がされるのも面白くありました。

tupleAry.remove(at: tupleAry.index(where: <#T##((ID: Int, cX: Int, cY: Int, mFlg: Bool, type: String)) throws -> Bool#>))

当然の如く、remove(at:)メソッドに於いては、 elem全体が求められる訳で、 IDのみが分かっている状態で当該タプルの削除を求めているのであれば、 ID以外が分からない時点で全要素を求められても困るのでした。

此処で動的変数名が扱えるならば上手い方法も思い付くのですが 残念ながらSwiftでは動的変数名が使えません。 何とか動的変数名を使う方法はないものかと思い倦ねた末に 下手げに質問箱に投稿を投げ掛けなどすれば、 おととい来やがれ…と言われ兼ねないでしょう。 動的変数名については teratail の2016年7月20日に遣り取りの有った以下の裏技的な情報を見れば、 言わんとしているものが分かられるものと思います。

Xcode - [Swift] 変数名を変数で設定する方法(41727)

斯くの如き裏技も有る様ですが、 複雑さを増す嫌いも有り、Swiftのバージョンアップに伴うメンテナンス上の問題も有り、 副作用の考えられれば余り使いたくない処です。 また、タプルでソートする Qiita の2017年10月24日の情報なども有りますが、 些か欲っする仕様とは異なる様です。

Swift3でタプルの配列をソートする

SwiftのFor-in書式

今回の要請の様なタプル配列から特定のタプルを削除する上手い上手い方法はないかものか、と考えた際、 Swiftではバージョン3からは for 文の書式が in を用いる様に変わって、従来の変数をインクリメントさせる書式が使えなくなったのを思い出しました。 それでは、インデックス、キー値を知りたい時は如何様にすべきか、と言う問いを解決すべき問題は 即ち、今回の問題を解決する情報に直結する筈です。 さて、Swiftで配列の要素其々を処理するには mapfilterreduce、 などが有りました。 此れ等の手法を用いれば解決への糸口は掴めるかも知れません。 Qiita の2015年12月06日の情報には以下の様なページが有ります。

Swiftのmap, filter, reduce(などなど)はこんな時に使う!

上の情報を閲すれば、 forEachはfor-inをより簡潔に記述したい時に使う! なる項目が用意されています。 其の下には enumerate要素と要素のインデックスが欲しい時に使う! も用意されています。 此れは明らかに配列をイテレートして順次、キー値と要素を取り出す手法でしょう。 Qiita には2015年12月21日に以下の如き情報も共有されています。

Swift3.0では使い慣れたfor文が使えなくなります

enumerateはインデックスと要素のタプルの配列を生成してくれるSequenceTypeに定義されたメソッドです。

インデックスを利用したい場合には、 enumerateを使用してインデックスを取得する方法が用意されている訳です。 他にもfor文でループさせる代わりに配列全体に処理を施したい際の書式なども紹介されています。 此処迄の情報が得られれば、何もIDの拘らず、任意の要素の特徴でタプルを特定できるでしょう。 此れ等、情報を参考に目論見を達成したコードが以下になります。

for i in idAry {
	if (self.childNode(withName: String(i)) != nil) {
		let obj:unitClass = self.childNode(withName: String(i)) as! unitClass
		for i_tpl in tupleAry {
			if i_tpl.cX == obj.cX - 1 && i_tpl.cY == obj.cY && i_tpl.uType == "hoge" {
				…
				for (idx, j_tpl) in tupleAry.enumerated() {
					if j_tpl.ID == i_tpl.ID {
						tupleAry.remove(at: idx)
					}
				}
			}
		}
	}
}

上ではかなりネストが深くなっていますが、 削除するか如何か検討する処のインスタンスの左隣の座標位置に有って、 尚且つ其の左隣のインスタンスタイプがhogeであるインスタンスと同期するタプルを削除しています。

本記事配信事由

今回は、データの保たせ方として此のような方式を取りました。 処でタプル型がimmutableな性格を持つ為に、 配列から特定のタプルを削除するという今回の要請があったのですが、 実は加えてインスタンスが画面内の位置を移動して座標が変わった際には当該タプルを削除しつつ、 座標を新しくしたタプルを追加する作業が必要になります。 此の要請をコーディングするに当たって、 今回の方法はソフトウェア構成の中に於ける位置付けとしては、余り良い方法ではありませんでした。

未だソフトウェア構築の途上の試行錯誤中の採用でしたので、 勿論タプル型は使い出のある型にも関わらず、 使う箇所が上手くなかったようです。 上手くなかった、と言うのも今回の方法は構築中のソフトウェア内では、 他に上記してもいるIDを主にした整数型1次元配列と、他に優先順位を主にした整数型2次元配列を必要として、 今回のタプルを主にした配列を加えた3つの配列の同期を取るという処理迄もが必要とされ、 タプルならではの特性がコードを複雑にする方向に働いたからです。 其の為にコード内の見通しが悪くなり、ソフトウェアの画面上の動きとコードの結び付きが把握出来なくなってしまったのでした。 しかし、僅かながらでもタプル型と其れを含む処理の振る舞いが腑に落ちた部分もあり、身にもなり、 又、試行錯誤中のコードも今後、別の場面で利用出来る可能性があるもの、 と本記事を書き置いたものです。 今日2018年7月16日、現在は此の手法を卓袱台返して他のデータの保持法の採用を試行錯誤しています。