配列の要素を一意にする

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も使うことができる

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 を使う

textField(_:shouldChangeCharactersIn:replacementString:) - UITextFieldDelegate | Apple Developer Documentation

例えば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を使って画像の種類を判別する

ImageMagickidentifyコマンドを使って画像の判定ができる。

拡張子は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ファイルだということが分かった。

参考

*1:ufrawをインストールすれば扱うことができる