SwiftのカスタムクラスArrayでプロパティの値を合計
SwiftのArray内に以下の様なクラスのオブジェクトが入っていたとして、任意のプロパティの合計を求める。
※末尾にNSArrayでの記法も書いておきます。(コメントありがとうございます!)
使用するカスタムクラス(!無しのプロパティに修正)
class Cat:NSObject{
var name:String
var color:String
var age:Int
var score:Int
init(data: (String,String,Int,Int)){
name = data.0
color = data.1
age = data.2
score = data.3
}
}
テストデータ
テストデータを準備しておきます。
var all_cats:[Cat] = [Cat]()
all_cats.append(Cat(data:("タマ","白",10,10)))
all_cats.append(Cat(data:("コトロ","茶トラ",2,10)))
all_cats.append(Cat(data:("ミケ","三毛",3,10)))
all_cats.append(Cat(data:("チビトラ","茶トラ",4,10)))
all_cats.append(Cat(data:("タケチヨ","キジトラ",7,10)))
all_cats.append(Cat(data:("マリアンヌ","白",7,10)))
合計の処理
正式な記法
正式な記法だと以下のとおり。
let score_sum:Int = all_cats.reduce(0,{
//sum=最終的な戻り値:return結果が次の繰り返しのsumとなる
//task=Array内のオブジェクト:繰り返しのたびに要素がセットされる
(var sum:Int, var cat:Cat) -> Int in
//毎回Taskのpropertyの値を足し込むことで、property値のsumも可能
return sum + cat.score
})
実行結果は以下のとおり
println("score sum=\(score_sum)\n")
score sum=60
※省略した記法
前回同様に、引数の記述を省略して$0,$1として値を受け取ることができます。またreturn文を省略して直接処理結果をreturnすることができます。
let score_sum2:Int = all_cats.reduce(0, {
$0 + $1.score
})
//結果確認
println("score sum=\(score_sum2)\n")
score sum=60
Objective-Cと同様にNSArrayを使う場合
NSArrayのvalueForKeyPathというメソッドは非常に高機能で、値の合計などに使用することができます。
詳しくは、Apple KVC Programming Guideを参照のこと
let score_sum3
= (all_cats as NSArray).valueForKeyPath("@sum.score") as Int
println(score_sum3)
※ただしSwift上からこのコードを利用する場合には、Array内のカスタムクラスの計算させたいプロパティには、Optional型やImplicitly Unwrapped Optional型は使えないようです。
(プロパティの型として?や!付きの宣言しているとvalueForKeyPathからは利用できない)
class Cat:NSObject{
var name:String!
var color:String!
var age:Int!
var score:Int!
//以下略
}
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MyProject.Cat 0x999999999999> valueForUndefinedKey:]: this class is not key value coding-compliant for the key score.'
※以下は未解決時の記載
※うーん、このコードはうまく動かないです。エラーメッセージ的には配列内の要素がNSObjectとみなされていないようです。原因がわかる方いらっしゃいましたらコメントいただけると有難いです。
(NSKeyValueCodingプロトコルと関係がありそう)
(追記)
こっちは動くのはなぜだろう。@max,@min,@sum,@avgはダメ。@countとかは結局プロパティにアクセスしないからOKなのだろうか。
~~```swift:UseCat.swift
let score_count
= (all_cats as NSArray).valueForKeyPath("@count.score") as Int
println(score_count)