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

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

新規登録して質問してみよう
ただいま回答率
85.35%
for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

Q&A

解決済

5回答

1798閲覧

Swift for文の中で条件を満たしたらfor文の手前にもう一度戻りたい

kizahashi

総合スコア17

for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

0グッド

1クリップ

投稿2020/05/30 03:29

お世話になっております。
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ページで確認できます。

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

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

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

guest

回答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
MasakiHori

総合スコア3391

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

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

kizahashi

2020/05/30 20:55

できました! いやー、全てを一つの配列にするという発想はありませんでした... .flatMap { $0 }色々なところに活用できそうですね。 ご回答ありがとうございました!
退会済みユーザー

退会済みユーザー

2020/05/30 22:40

(1...).lazy.map { } 初めて知りました。 ありがとうございます。
guest

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

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

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

kizahashi

2020/05/30 10:53

playground調子悪くなる事多いですよね... 考えていただきありがとうございます!
guest

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:) という、与えられたファイル名からファイル番号を取得する関数を作っておいて、それを imageStringArraycompactMap メソッドで実行してあげれば、ファイル番号の配列を作ることができます。

ここから先は、好きなように、たとえば欠番を作っても良いなら、ループを使わずに 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
TomohiroKumagai

総合スコア441

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

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

kizahashi

2020/05/30 10:43

まだまだ勉強不足でプロトコル?ジェネリクス?などわからないので使えるように勉強してみたいと思います。 ファイル名なのでよく考えたら欠番ありでも大丈夫ですね! 考えていただきありがとうございました。
guest

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

hoshi-takanori

総合スコア7901

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

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

kizahashi

2020/05/30 10:29

できました。すごいですね! 回答ありがとうございました〜!
guest

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

TsukubaDepot

総合スコア5086

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

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

kizahashi

2020/05/30 07:54

試してみましたが難しそうです。空の配列も含まれている時もありimageStringArrayに入っている順番がバラバラなので。 ご回答ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問