お世話になっております。
Swiftの基礎の質問になると思います。よろしくお願い致します。
画像を使ったアプリを作っており
FileManagerで画像を保存していて
数字を使ってファイル名を決めています。
ファイル名が重複することがないように
ファイル名の配列の中に無いファイル名を作りたいです。
//以下コード
fileNumberはファイル名に使う数字
imageStringArrayは画像ファイル名の二次元配列
Swift
1fileNumber += 1 2let imageString = "image(fileNumber)" 3for i in 0..<imageStringArray.count{ 4if imageStringArray[i].contains(imageString){ 5 fileNumber += 1 6 } 7}
上記のような感じのやり方でやろうと思っています。
for文で配列の中身を一つずつ確認し、配列の中に同じ要素がある場合はfileNumberを+=1しています。
配列に重複しているものが見つかりfileNumberを+=1したらfor文の前に戻ってまたfor文を最初からやり直したいのですがどう書けばいいのでしょうか?
またファイル名の扱い方としてあまり良くない点などがあればアドバイスもお願いしたいです。
ご回答よろしくお願い致します。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
もう解決した質問ですが、気になってしまったのですみません。
安直に「flatにしてやればいいのでは? 」と書きましたが、さすがに乱暴すぎたので少し考えなおしました。
Arrayに対して2次元配列の場合その中身に対象のインスタンスが存在するかを確認するメソッドを追加してみました。
swift
1extension Array { 2 3 func deepContains<E: Equatable>(_ element: E) -> Bool 4 where Element == [E] { 5 6 contains { $0.contains(element) } 7 } 8} 9 10let imageStringArray = [ 11 ["image1", "image5", "image3"], 12 ["image2", "image6"], 13 ["image4"], 14] 15 16let fileName = (1...) 17 .lazy 18 .map { "image($0)" } 19 .first { !imageStringArray.deepContains($0) }! // nilになることはない。 Int.maxを超えるのでクラッシュするから。
条件によりますが少しパフォーマンスが上がっているはずです。
ややこしく考えずにimageStringArrayをflatにしてやればいいのでは?
swift
1let imageNames = imageStringArray.flatMap { $0 } 2let fileName = (1...) 3 .lazy 4 .map { "image($0)" } 5 .first { !imageNames.contains($0) }
投稿2020/05/30 16:41
編集2020/06/26 09:33総合スコア3391
0
playground調子が悪かったので…
備忘目的追記
swift
1import UIKit 2 3class ViewController: UIViewController { 4 5 let fileNames = ["03", "fileName_094", "fileName_056", "BBB", "45", "fileName_001", "fileName_002", "fileName_005"] 6 7 override func viewDidLoad() { 8 super.viewDidLoad() 9 10 print(fileNames.newFileName) // => "fileName_004" 11 } 12} 13 14extension String { 15 // 同一接頭句でないと連番の意味が薄れるが念の為。 16 var numberStr: String { 17 18 guard let regex = try? NSRegularExpression(pattern: "^*[\d]+$", options: []) else { 19 return "" 20 } 21 22 let results = regex.matches(in: self, options: [], range: NSMakeRange(0, self.count)) 23 24 var str: String = "" 25 26 for i in 0 ..< results.count{ 27 for j in 0 ..< results[i].numberOfRanges{ 28 let range = results[i].range(at: j) 29 str.append((self as NSString).substring(with: range)) 30 } 31 } 32 return str 33 } 34} 35 36extension Array where Element == String { 37 38 var newFileName: String { 39 40 let prefixStr: String = "fileName_" 41 42 let maxDigit = self.map { $0.numberStr.count }.max() ?? 1 43 44 let suffixNumbers: [Int] = self.map { Int($0.numberStr) ?? 0 }.sorted(by: <) 45 46 let newSuffixNumber: String 47 = String(format: "%0(maxDigit)d", ((1...).lazy.map { $0 }.first { !suffixNumbers.contains($0) }) ?? 0 ) 48 49 return prefixStr + newSuffixNumber 50 51 } 52} 53
swift
1import UIKit 2 3class ViewController: UIViewController { 4 5 let fileNames = ["AA003y", "094", "556", "BBB", "DE45", "BB001", "CCDFR002", "005", "kaegei990785304743", "08307320683"] 6 7 8 override func viewDidLoad() { 9 super.viewDidLoad() 10 11 print("num: (fileNames.fileNumber)") => num: Optional("3") 12 13 14 } 15 16} 17 18extension String { 19 20 var number: Int? { 21 // 正規表現わかんない 22 guard let regex = try? NSRegularExpression(pattern: "^*[\d]+$", options: []) else { 23 return nil 24 } 25 26 let results = regex.matches(in: self, options: [], range: NSMakeRange(0, self.count)) 27 28 var str: String? = "" 29 30 for i in 0 ..< results.count{ 31 for j in 0 ..< results[i].numberOfRanges{ 32 let range = results[i].range(at: j) 33 str?.append((self as NSString).substring(with: range)) 34 } 35 } 36 37 if let str = str { 38 return Int(str) ?? 0 39 } else { 40 return nil 41 } 42 } 43} 44 45extension Array where Element == String { 46 47 var fileNumber: String? { 48 49 // 配列が空だったら? -> 1を返す? 50 // ファイル名に数値がなかったら -> 1を返す? 51 // 桁数調整(入ってくる連番の桁数とのかねあいも考慮) 52 // 最終的にはStringを返す? 53 54 let numArray: [Int] = self.map { ($0.number ?? 0) }.sorted(by: <) 55 56 guard let max = numArray.max() else { return nil } 57 58 for i in 0 ... max { 59 60 if !(numArray.contains(i)) { return String(i) } 61 62 } 63 64 return nil 65 } 66}
時間があったら完成させようと思う
投稿2020/05/30 10:12
編集2020/05/30 22:39退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
目的の「for文の前に戻ってまたfor文を最初からやり直したい」については hoshi-takanori さんの回答が最適に感じます。
こちらは補足としての回答なのですけれど、文字列を操作して番号を取得する操作は複雑になりがちなので、もしかすると先にファイル名からファイル番号の一覧を作ってしまって、そこから次の番号を得る方法を考えると見通しが利きやすくなるかもしれないです。
swift
1/// ファイル名からファイル番号を取得します。 2/// - Parameter filename: ファイル名です。 3/// - Returns: 該当するファイル番号です。接頭辞が一致しない場合は `nil` を、一致する場合で番号がない場合は `0` になります。 4func fileNumber<T:StringProtocol>(of filename: T) -> Int? { 5 6 let prefix = "iamge" 7 8 guard filename.hasPrefix(filename) else { 9 10 return nil 11 } 12 13 // 補足:番号なしのファイル名を許容しない場合は `?? 0` を削除して差し支えないはずです。 14 return Int(filename.suffix(filename.count - prefix.count), radix: 10) ?? 0 15} 16 17// ファイル番号のリストを生成します。 18let fileNumbers = imageStringArray.flatMap {$0}.compactMap(fileNumber(of:))
たとえば fileNumber(of:)
という、与えられたファイル名からファイル番号を取得する関数を作っておいて、それを imageStringArray
に compactMap
メソッドで実行してあげれば、ファイル番号の配列を作ることができます。
ここから先は、好きなように、たとえば欠番を作っても良いなら、ループを使わずに max
メソッドを使う方法でシンプルに組み立てられます。欠番を埋めていく場合は関数を用意する分だけ複雑になりそうですけれど、番号の並び替えなどもしやすくなっていて、考えをまとめやすくなる、融通が効きやすくなるのがこの方法の場合のメリットのように感じます。
swift
1// ファイル番号の最大値が得られたら、その次の番号を次のファイル番号とします。 2if let maxFileNumber = fileNumbers.max() { 3 4 let nextFileNumber = maxFileNumber + 1 5 6 7}
投稿2020/05/30 09:49
編集2020/05/30 16:56総合スコア441
0
ベストアンサー
こんな感じでしょうか。「元に戻ってやり直す」というのは基本的にループになります。そして、外側のループを break や continue するにはラベルを使います。
参考: Swiftは多重ループなどを一気にbreakできる - Qiita
swift
1func nextFilename(_ imageStringArray: [[String]]) -> String { 2 var fileNumber = 1 3 outerLoop: while true { 4 let imageString = "image(fileNumber)" 5 for subArray in imageStringArray { 6 if subArray.contains(imageString) { 7 fileNumber += 1 8 continue outerLoop 9 } 10 } 11 return imageString 12 } 13} 14 15let imageStringArray = [ 16 ["image1", "image5", "image3"], 17 ["image2", "image6"], 18 ["image4"], 19] 20 21print(nextFilename(imageStringArray))
投稿2020/05/30 09:11
総合スコア7901
0
contains
はコレクションに適用できますので、必ず1番から始まっているという保証があれば、こういう風に書き換えることができると思います。
Swift
1var fileNumber = 0 2 3repeat { 4 fileNumber += 1 5} while imageStringArray.contains("image(fileNumber)") 6 7// つづきの番号 8print(fileNumber)
ただし、1番から始まらない、途中に空き要素があるなどの要因が考えられるのであれば別の方法を検討する必要がありそうです。
投稿2020/05/30 06:41
総合スコア5086
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/05/30 20:55
退会済みユーザー
2020/05/30 22:40