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] 表示时间戳 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 秒。

决定很明确。我将使用第三个版本。

更新 2

经过更多研究,我现在很清楚 KVC 不适用于内部数组中的单个元素。请参阅以下链接:KVC with NSArrays of NSArrays and Collection Accessor Patterns for To-Many Properties

无论如何,因为我想计算元素的最大值,所以迭代数组比使用一些技巧来使 KVC 工作更好。

4

3 回答 3

4

您可以使用键值编码和集合运算符来解决这个问题。

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

我还没有测试过代码,但我认为这正是你要找的:

[samples valueForKeyPath:@"@max.(@unionOfArrays.analogChannelData)"];

我猜你也可以@distinctUnionOfArray用来删除重复值。

这是涵盖收集运营商的Apple 文档的链接。

希望这有帮助!干杯!

于 2013-08-23T19:09:04.947 回答
2

仅供进一步探索的建议

顺便说一下,您是否可以使用单个 KVC 运算符按原样执行此操作尚不清楚。您可能会考虑向您的类添加两个属性:currentChannel,它设置/获取当前频道;和analogChannelDataForCurrentChannel, 等价于analogChannelData[currentChannel]. 那么你也能:

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

如果需要线程安全,则在两个调用之间进行任何适当的锁定(因此一个线程未设置currentChannel,然后是第二个,然后第一个使用第二个通道执行 KVC 运算符...)。

高温高压

于 2013-08-23T19:55:16.397 回答