3

同じクラスのカスタム オブジェクトのコレクションのプロパティの最大値を取得する必要があります。オブジェクトは NSArray に格納され、プロパティはたまたま数値の別の NSArray になります。

詳しく説明しましょう:

NSArray *samples; // of CMData, 4000 elements

CMData は、異なる値を持つことができる一連の異なるチャネルの特定の瞬間のサンプルをモデル化するクラスです。

@interface CMData : NSObject
@property (nonatomic) NSUInteger timeStamp;
@property (nonatomic, strong) NSArray *analogChannelData; // of NSNumber, 128 elements
@end

(質問に関係のないクラスの他のプロパティを削除しました)

たとえば、sample[1970] は次のようになります。

sample.timeStamp = 970800
sample.analogChannelData = <NSArray>
    [
    [0] = @(153.27)
    [1] = @(345.35)
    [2] = @(701.02)
    ...
    [127] = @(-234.45)
    ]

ここで、analogChannelData の各要素 [i] は、timeStamp 970800 の特定のチャネル i の値を表します。

ここで、チャネル 31 の 4000 サンプルすべての最大値を取得したいと考えています。次のコードを使用します。

NSUInteger channelIndex = 31;
NSMutableArray *values = [[NSMutableArray alloc] init]; // of NSNumber
// iterate the array of samples and for each one obtain the value for a 
// specific channel and store the value in a new array
for (CMData *sample in samples) {
    [values addObject:sample.analogChannelData[channelIndex]];
}
// the maximum 
NSNumber *maxValue = [values valueForKeyPath:@"@max.self"];

このプログラミング構造を NSPredcicate を介したフィルターで置き換えるか、valueForKeyPath: を使用して、必要なデータを最大限に取得したいと考えています。

forループなしでこれを行う方法を知っている人はいますか? NSPredicates や valueForKeyPath を使用しているだけですか?

よろしくお願いいたします。

更新 1

最後に、for ループ バージョンを keyPath バージョンに対してベンチマークしました (受け入れられた回答を参照)。はるかに高速に実行されるため、for ループを使用することをお勧めします。アルゴリズムのクラスで学んだいくつかの教訓を思い出して、値を格納するための配列を必要としないさらに高速なバージョンを実装しました。選択したチャネルを反復処理し、各反復で最大値を選択するだけです。これは断然最速のバージョンです。

そう:

  • バージョン 1: for ループ (上記のコードを参照)
  • バージョン 2: カスタム プロパティを含むバージョン (Marcus からの選択された回答、更新 2 を参照)
  • バージョン 3: 新しいコード

バージョン 3 のコード:

NSUInteger channelIndex = 31;
NSNumber *maxValue = @(-INFINITY);
for (CMTData *sample in samples) {
    NSNumber *value = sample.analogChannelData[channelIndex];
    if (value) { // I allow the possibility of NSNull values in the NSArray
        if ([value compare:maxValue] == NSOrderedDescending)
            maxValue = value;
    }
}
// the maximum is in maxValue at the end of the loop

パフォーマンス:

iOS シミュレーターでの 20.000 回の反復後:

  • バージョン 1: 12.2722 秒。
  • バージョン 2: 21.0149 秒。
  • バージョン 3: 5.6501 秒。

決定は明らかです。3 番目のバージョンを使用します。

更新 2

さらに調査した結果、KVC が内部配列の個々の要素に対して機能しないことが明らかになりました。次のリンクを参照してください: NSArrays の NSArrays を使用した KVCおよびTo-Many プロパティのコレクション アクセサー パターン

とにかく、要素の最大値を計算したかったので、KVC を機能させるためにいくつかのトリックを使用するよりも、配列を反復処理する方が適切です。

4

3 に答える 3

4

これは、Key Value Coding とコレクション演算子を使用して解決できます。

NSNumber *result = [sample valueForKeyPath:@"@max.analogDataChannel"];

更新 1

Arcanfel が述べたように、配列を結合できます。

NSNumber *result = [samples valueForKeyPath:@"@max.@unionOfArrays.@analogChannelData"];

両方がリンクしているドキュメントを読むことをお勧めします。そこには非常に強力な機能がいくつかあります。

更新 2

HRDの答えに加えて、彼はあなたの解決策を持っています。彼の変更をKVCと組み合わせる必要があります。

currentChannel の CMData オブジェクトにプロパティを追加します。その後、あなたは呼び出すことができます

[samples setValue:@(channelIndex) forKey:@"currentChannel"];

配列内のすべてのインスタンスに設定されます。次に呼び出します。

[samples valueForKeyPath:@"@max.analogDataForCurrentChannel"];

その後、完了です。

于 2013-08-23T19:08:00.407 に答える
2

さらなる探索のみの提案

これを 1 つの KVC オペレーターでそのまま実行できるかどうかは明らかではありません。考えられるのは、クラスに 2 つのプロパティを追加することcurrentChannelです。現在のチャネルを設定/取得します。とanalogChannelDataForCurrentChannelに相当しanalogChannelData[currentChannel]ます。次に、次のことができます。

samples.currentChannel = channelIndex;
... [samples valueForKeyPath:"@max.analogChannelDataForCurrentChannel"];

スレッド セーフが必要な場合は、2 つの呼び出しの間で適切なロックを使用します (つまり、1 つのスレッドが を設定せずcurrentChannel、次に 2 番目のスレッドが設定され、次に最初のスレッドが 2 番目のチャネルで KVC オペレーターを実行します...)。

HTH

于 2013-08-23T19:55:16.397 に答える