配列の要素を一意にする
RubyでいうところのArray#uniq
みたいなもの
let array = [1, 1, 2, 4, 1, 3, 2] array.enumerated().compactMap({ index, item in array.index(of: item) == index ? item : nil })
それぞれの「index」と「arrayからindex(of:)
で取得したindex」が一致した場合のみ要素が残るようにしている。
つまり最初に出現した要素だけが残るようになっている。
毎回書くののも面倒なので、extensionしておくと便利
Closureの即時実行を使ったスコープの切り分け
Swiftのletはimmutableな変数宣言で、一度値を入れたらその後に変更することができない。
let hoge = "hoge" hoge = "fuga" // コンパイルエラー
これは変数宣言のときに値を入れないといけない、という制限ではなく、その変数が使われるまでに値が入っていれば問題ない。 ただし、その場合は型の宣言を省略できない。
let hoge: String hoge = "hoge" // OK
「その変数が使われるまでに値が入っていれば問題ない。」というのは例えばif文やswitch文を使っても大丈夫
let hoge: String if arc4random() % 2 == 0 { str = "even" } else { str = "odd" } debugPrint(str) // "even" もしくは "odd"が出力される
たまに処理が複雑で使い捨て変数を割り当てないといけないことがある。
let array = Array(1...10) var sum = 0 array.forEach({ sum += $0 }) let average = sum / array.count
このときsum
変数は一時的な変数で、average
を求めたら必要なくなる。このexampleではそんなことないんだけど、使い捨ての変数名を考えるが面倒で、手が止まってしまうことがしばしばあり、違うスコープを切りたい時に有効なのがクロージャだ。
let array = Array(1...10) let average: Int = { var sum = 0 array.forEach({ sum += $0 }) return sum / array.count }()
クロージャ内部は別のスコープなので、使い捨ての変数は本当に使いたい部分でだけ有効になる。 まぁそもそもこのexampleではreduceを使えばいい話なんだけど…
let average = array.reduce(0) { sum, i in sum + i } / array.count
Viewに関するコードを書いていると使い捨ての変数を定義したくなることがあるので、そういうときに使うと便利。
forEachでインデックスを使いたい
配列をすべて舐めるような処理をする時はforEachを使うけど、この時indexを使うことができない
let array = ["a", "b", "c", "d"] array.forEach { string in debugPrint(string) }
そんなときはArray#enumerated()
を使えばindexも使うことができる
- enumerated() - Array | Apple Developer Documentation
- EnumeratedSequence - Swift Standard Library | Apple Developer Documentation
let array = ["a", "b", "c", "d"] array.enumerated().forEach({ index, string in debugPrint("\(index): \(string)") }) // "0: a" // "1: b" // "2: c" // "3: d"
UITextFieldで入力文字を制限する
UITextFieldDelegate
を使う
例えばInt
にパースできる値だけにしたければ、次のようなコードになる
class ViewController: UIViewController, UITextFieldDelegate { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { super.viewDidLoad() } func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { return Int(string) != nil } }
ただ、このdelegateメソッドの引数である string
は入力された値だけなので、UITextField
に入る値を数値だけに制限したい場合は、これだけでは不十分。
range
引数に、UITextField
のキャレットなどの文字選択位置が渡されるので、それで置換してあげればいい。
class ViewController: UIViewController, UITextFieldDelegate { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { super.viewDidLoad() } func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { if let currentString = textField.text, let _range = Range(range, in: currentString) { let newString = currentString.replacingCharacters(in: _range, with: string) return Int(newString) != nil } else { return false } } }
コマンドラインの実行結果を監視する
コマンドラインの実行結果を監視したいんだと
- watchだと履歴が見れない
- whileだと全部垂れ流しで全部のデータを見るのがだるい
コマンドラインの実行結果が変わったときだけに表示してほしい〜〜って思ったんだけど、watchもwhileも微妙だったのでチョコチョコかいた そういうコマンドありそうなんだけど知らないなあ
while do sleep 1 c=`date +"%Y-%m-%d %H:%M"` if [ $c != $d ]; then date echo $c echo '-------------------------------------------------------------------' fi d=$c done
こんな感じに標準出力が変わったときに表示される
2018年 3月13日 火曜日 07時40分00秒 JST 2018-03-13 07:40 ------------------------------------------------------------------- 2018年 3月13日 火曜日 07時41分00秒 JST 2018-03-13 07:41 ------------------------------------------------------------------- 2018年 3月13日 火曜日 07時42分00秒 JST 2018-03-13 07:42 ------------------------------------------------------------------- 2018年 3月13日 火曜日 07時43分00秒 JST 2018-03-13 07:43 ------------------------------------------------------------------- 2018年 3月13日 火曜日 07時44分00秒 JST 2018-03-13 07:44 -------------------------------------------------------------------
ImageMagickを使って画像サイズを取得する
$ identify -format "%w x %h" ./hogehoge.jpg 1000 x 1333
ImageMagickを使って画像の種類を判別する
ImageMagickのidentify
コマンドを使って画像の判定ができる。
拡張子はJPGと言いながら実はPNGだった例
$ identify -format %m ./path/to/image-file.jpg
PNG
特定のディレクトリ以下のすべてのファイルの種類を表示するならこんな感じ
for imagefile in $(find ./ -name *.jpg); do format=$(identify -format %m $imagefile) echo "${format} ${imagefile}" done
jpg以外のファイルを探すならこんな感じ
for imagefile in $(find ./ -name *.jpg); do format=$(identify -format %m $imagefile) if [ $format != 'JPEG' ]; then echo "${format} ${imagefile}" fi done
magic numberの確認
例えばカメラのRAWファイルの場合、ImageMagickはデフォルトで判別することはできない*1 どういう画像なのかはたいていマジクナンバーを見れば分かる
$ od -c ./path/to/raw-file.jpg | head -4 0000000 I I R O \b \0 \0 \0 027 \0 \0 001 004 \0 001 \0 0000020 \0 \0 360 017 \0 \0 001 001 004 \0 001 \0 \0 \0 340 \v 0000040 \0 \0 002 001 003 \0 001 \0 \0 \0 020 \0 \0 \0 003 001 0000060 003 \0 001 \0 \0 \0 001 \0 \0 \0 006 001 003 \0 001 \0
IIRO magic number
で検索するとOlympusのORFファイルだということが分かった。
参考
- Command-line Tools: Identify @ ImageMagick http://www.imagemagick.org/script/identify.php
- Format and Print Image Properties @ ImageMagick http://www.imagemagick.org/script/escape.php
*1:ufrawをインストールすれば扱うことができる