How to add all decimal numbers in an NSMutableArray

霸气de小男生 提交于 2019-12-19 11:34:51

问题


I have a NSMutableArray which have some NSDecimalNumber in it, like (500,50.80,70,8000)

Now I want to add all those decimal numbers together.

I've tried to use

for (NSDecimalNumber *number in self.numbersArray)
{
    NSDecimal *sum += [number decimalValue]
}

But failed.


回答1:


Use - (NSDecimalNumber *)decimalNumberByAdding:(NSDecimalNumber *)decimalNumber

Take a look at NSDecimalNumber Class Reference

NSDecimalNumber *lNumber = [NSDecimalNumber zero];
for (NSDecimalNumber *number in self.numbersArray)
{
    lNumber = [lNumber decimalNumberByAdding:number];
}



回答2:


A simple way to add all NSNumbers in an array is (similar to what @Mahonor said in a comment):

NSArray *myArray = ... // array of NSNumber (or NSDecimalNumber) objects
NSNumber *sum = [myArray valueForKeyPath:@"@sum.self"];

Contrary to what the Collection Operators: sum states, the numbers in the array are not converted to double, but to NSDecimal. Therefore, no precision is lost when adding decimal numbers. Even NSNumber objects which are not decimal numbers are converted to NSDecimal for the addition. The result of the summation is an instance of NSDecimalValue.

I verified (or tried to) that in two different ways. First, I ran this code

NSNumber *a = [NSNumber numberWithDouble:1.2];
NSNumber *b = [NSDecimalNumber decimalNumberWithString:@"-5.7"];
NSArray *myArray = @[a, b];
id sum = [myArray valueForKeyPath:@"@sum.self"];

and activated Objective-C message logging by setting the environment variable "NSObjCMessageLoggingEnabled=YES". As can be seen in the created "/tmp/msgSends-NNNN" file, decimalNumber (and not doubleValue) is sent to both number objects.

Second, I created a custom class implementing both decimalValue and doubleValue, and applied @sum.self to an array of objects of the custom class:

@interface MyClass : NSObject
@property (nonatomic, assign) double value;
@end

@implementation MyClass

- (NSDecimal)decimalValue
{
    return [[NSNumber numberWithDouble:self.value] decimalValue];
}

- (double)doubleValue
{
    return self.value;
}

@end

MyClass *a = [MyClass new]; a.value = 1.2;
MyClass *b = [MyClass new]; b.value = -5.7;
NSArray *myArray = @[a, b];
id sum = [myArray valueForKeyPath:@"@sum.self"];

By setting breakpoints in both methods, it is seen that only decimalValue is used for the summation (and valueForKeyPath:@"@sum.self" throws an exception if the class does not implement decimalValue).

One can also see that decimalValue is called from

-[NSArray(NSKeyValueCoding) _sumForKeyPath:]

and the assembler code for this method shows that NSDecimalAdd is uses to add the numbers.




回答3:


Manohar's suggestion in the comments is not bad. You can indeed use KVC collection operators to make a one-liner out of this: [myArray valueForKeyPath:@"@sum.doubleValue"];, but you potentially lose precision (depending on the numbers you have stored).

You're basically looking for "reduce" functionality; you need to chain calls to decimalNumberByAdding: so that each call has the succeeding element of the array as its argument. Doing this on an NSArray is easy enough, using performSelector:withObject:

@implementation NSArray (Reduce)
- (id)reduceUsingSelector: (SEL)sel
{
    id res = [self objectAtIndex:0];
    for( id obj in [self subarrayWithRange:(NSRange){1, [self count]-1}] ){
        res = [res performSelector:sel withObject:obj];
    }

    return res;
}
@end

Use this like so: NSDecimalNumber * sum = [myArray reduceUsingSelector:@selector(decimalNumberByAdding:)];

The code you have isn't successful because NSDecimal is a struct, not an object; it shouldn't be declared as a pointer, and if it wasn't, you wouldn't be able to add it. That's not the right route to a solution.



来源:https://stackoverflow.com/questions/15378633/how-to-add-all-decimal-numbers-in-an-nsmutablearray

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!