how to operate on NSNumber in NSNArray created by NSString - objective-c

i've got some method.
....{
NSString *mean = #"1:1:1:1:1:1:1:1:1";
tab = [self moveSourceArrayToDestinationArray:mean];
....}
-(NSArray*)moveSourceArrayToDestinationArray:(NSString*)sourceArray{
NSArray *destinationArray = [sourceArray componentsSeparatedByString:#":"];
for (NSNumber *number in destinationArray) {
sum += [number doubleValue];
}
NSLog(#"%d", [destinationArray objectAtIndex:1] * 5); // invalid operands to binary expression ('id' and 'int')
return destinationArray;
}
how can i do something mathematic operation on numbers in NSArray?

Your reference to NSNumber in your code is a mistake, and you got lucky that your code didn't throw an unrecognized selector exception. Your destinationArray is an array of NSStrings, not NSNumbers. It just so happens that both NSString and NSNumber have doubleValue and intValue methods. So when you say [number doubleValue] in your loop, you actually end up calling [NSString doubleValue] which of course still returns the number you want. However, if you were to try to call [number shortValue], where shortValue is a selector that only exists in NSNumber and not NSString, your code would throw an exception and not work.
The moral of this answer is that you should remove any reference to NSNumber in your code, or actually convert the objects in destinationArray to NSNumbers. Otherwise, you risk running into more trouble.

The easiest thing to do is to retrieve a numeric value from the NSNumber object, something you already did in your code snippet earlier. For example, try:
NSLog(#"%d", [[destinationArray objectAtIndex:1] intValue] * 5);
See the full list of numeric access functions in the NSNumber documentation under the section titled "Accessing Numeric Values."

Your object at [destionationArray objectAtIndex:index] is likely to be a NSNumber as well. Therefore, you have to to [[destinationArray objectAtIndex:index] doubleValue]at this point, too.

Related

How to divide NSObject by a number in Objective-C?

I have this code:
NSObject *distanceInMeters;
distanceInMeters = [[[[[[googleMapsApiResult objectForKey:#"routes"] objectAtIndex:0] objectForKey:#"legs"] objectAtIndex:0] objectForKey:#"distance"] valueForKey:#"value"];
NSLog(#"%#", distanceInMeters); // this outputs 7578
NSDecimalNumber *roundTripInKilometers;
roundTripInKilometers = distanceInMeters / 500; // It says "Invalid operands to binary expression ('NSObject *' and 'int')
I expect to get 15.156 but it cannot divide object value with an integer. What should I do to get 15.156?
You can't divide number from NSObject, To do the same, You have to fetch the number from it and use it anywhere
NSNumber *distanceInMeters;
distanceInMeters = [[[[[[googleMapsApiResult objectForKey:#"routes"] objectAtIndex:0] objectForKey:#"legs"] objectAtIndex:0] objectForKey:#"distance"] valueForKey:#"value"];
NSLog(#"%#", distanceInMeters); // this outputs 7578
NSDecimalNumber *roundTripInKilometers = (NSDecimalNumber *)[NSDecimalNumber numberWithDouble:[distanceInMeters doubleValue] / 500.0];
There are many problems in your code. First, you need to convert the value you get from the API into an double. An easy way to do that would be to first convert it to an instance of NSString. Then get an double from the string with the method doubleValue.
Next divide that value by 500:
double roundTripInKilometersDouble = distanceInMeters / 500.0;
Then put that result into an NSDecimalNumber:
NSDecimalNumber *roundTripInKilometers = [NSDecimalNumber numberWithDouble: roundTripInKilometersDouble];
(Haven't tested this code.)

NSNumber returning different value than the original int

I am quite new to objective-c and I am trying to convert an int into a NSNumber so that I can save it into Core-Data.
I've the following piece of code (index is an NSInteger)
- (void) associateOrNotToARoutine:(NSString*)exerciseName associate:(BOOL)associate index:(NSInteger)index
NSLog(#"number w index %d, %d",[NSNumber numberWithInteger:index],index);
and it returns
number w index 170413600, 2
I need an int of 2 to be translated into a number 2 along with all other numbers to be translated into the correct number... Could anyone tell me why i am getting this convertion? I tried reading on NSNumber manual but i found nothing
Try:
NSLog(#"number w index %#, %d",[NSNumber numberWithInteger:index],index);
^^
The %# format specifier will call the [NSNumber description] method, which should return the value you are after. Your original code will return the address of the NSNumber object, not its content.
Even though this question has already been answered, I thought I'd flesh out a longer answer for future readers in general:
What's happening?
%d is a C format string used to indicate one of the passed parameters is an integer (int) ivar value. Much like %f is used for float values.
[NSNumber numberWithInteger:index] returns a pointer to an NSNumber instance. If you use %d, NSLog thinks you're passing it an integer when, in fact, you're passing a pointer. So the pointer value (a memory address) is printed.
What's %#?
As mentioned by trojanfoe: %# tells NSLog() that you are passing an object. In that case, NSLog asks the object to describe itself using a string… it calls the description method.
Specific answer
For this specific question, there are multiple ways. The two main one being:
NSLog(#"number w index %#, %d", [NSNumber numberWithInteger:index], index);
NSLog(#"number w index %d, %d", [[NSNumber numberWithInteger:index] intValue], index);
Extra goodness
When using %#, the passed object can be anything that responds to description, essentially any descendant of NSObject. Also, if you're creating your own classes, it's a good idea to overload description to return a more meaningful string than the default NSObject implementation.
// Try using it with NSArray or NSDictionary and see how each describe themselves.
NSLog(#"the array description: %#", myArray);
NSLog(#"the dictionary description: %#", myDictionary);
You should use,
[[NSNumber numberWithInteger:index] intValue]
to get the integer value, the NSNumber, is holding

KVC - about data type

this is the code:
NSNumber *taskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
NSInteger *intTaskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
self.taskList is an NSArray which filled with core data fetch request in ViewController's viewDidLoad method.
the taskId is: 1
the intTaskId is: 269303816
In actually, the value stored in core data is: 1
below is my questions:
1, I am confused why the NSInteger incorrect?
2, Should I have to replace NSInteger with NSNumber to avoid any other problems?
NSNumber is an object, whereas NSInteger is simply a typedef for a primitive (non-object) type (like int). NSInteger is not a subclass of NSNumber. Core Data returns numbers as instances of NSNumber. You're getting the weird NSInteger value because it's pointing to an object of type NSNumber but attempting to print it as if it were just an integer.
You'll need to replace NSInteger with NSNumber to avoid any problems. You could also use the intValue method on NSNumber to get back an NSInteger:
NSNumber *objTaskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
NSInteger *intTaskId = [objTaskId intValue];
You'll need to do this if you want to do comparisons (greater than, equal too, smaller than) or arithmetic (you can't add an NSNumber to another NSNumber or an NSNumber to a primitive type like an int or float).

How to Properly set an integer value in NSDictionary?

The following code snippet:
NSLog(#"userInfo: The timer is %d", timerCounter);
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:timerCounter] forKey:#"timerCounter"];
NSUInteger c = (NSUInteger)[dict objectForKey:#"timerCounter"];
NSLog(#"userInfo: Timer started on %d", c);
produces output along the lines of:
2009-10-22 00:36:55.927 TimerHacking[2457:20b] userInfo: The timer is 1
2009-10-22 00:36:55.928 TimerHacking[2457:20b] userInfo: Timer started on 5295968
(FWIW, timerCounter is a NSUInteger.)
I'm sure I'm missing something fairly obvious, just not sure what it is.
You should use intValue from the received object (an NSNumber), and not use a cast:
NSUInteger c = [[dict objectForKey:#"timerCounter"] intValue];
Dictionaries always store objects. NSInteger and NSUInteger are not objects. Your dictionary is storing an NSNumber (remember that [NSNumber numberWithInteger:timerCounter]?), which is an object. So as epatel said, you need to ask the NSNumber for its unsignedIntegerValue if you want an NSUInteger.
Or like this with literals:
NSUInteger c = ((NSNumber *)dict[#"timerCounter"]).unsignedIntegerValue;
You must cast as NSNumber first as object pulled from dictionary will be id_nullable and so won't respond to the value converting methods.

Difference between objectForKey and valueForKey?

What is the difference between objectForKey and valueForKey?
I looked both up in the documentation and they seemed the same to me.
objectForKey: is an NSDictionary method. An NSDictionary is a collection class similar to an NSArray, except instead of using indexes, it uses keys to differentiate between items. A key is an arbitrary string you provide. No two objects can have the same key (just as no two objects in an NSArray can have the same index).
valueForKey: is a KVC method. It works with ANY class. valueForKey: allows you to access a property using a string for its name. So for instance, if I have an Account class with a property accountNumber, I can do the following:
NSNumber *anAccountNumber = [NSNumber numberWithInt:12345];
Account *newAccount = [[Account alloc] init];
[newAccount setAccountNumber:anAccountNUmber];
NSNumber *anotherAccountNumber = [newAccount accountNumber];
Using KVC, I can access the property dynamically:
NSNumber *anAccountNumber = [NSNumber numberWithInt:12345];
Account *newAccount = [[Account alloc] init];
[newAccount setValue:anAccountNumber forKey:#"accountNumber"];
NSNumber *anotherAccountNumber = [newAccount valueForKey:#"accountNumber"];
Those are equivalent sets of statements.
I know you're thinking: wow, but sarcastically. KVC doesn't look all that useful. In fact, it looks "wordy". But when you want to change things at runtime, you can do lots of cool things that are much more difficult in other languages (but this is beyond the scope of your question).
If you want to learn more about KVC, there are many tutorials if you Google especially at Scott Stevenson's blog. You can also check out the NSKeyValueCoding Protocol Reference.
When you do valueForKey: you need to give it an NSString, whereas objectForKey: can take any NSObject subclass as a key. This is because for Key-Value Coding, the keys are always strings.
In fact, the documentation states that even when you give valueForKey: an NSString, it will invoke objectForKey: anyway unless the string starts with an #, in which case it invokes [super valueForKey:], which may call valueForUndefinedKey: which may raise an exception.
Here's a great reason to use objectForKey: wherever possible instead of valueForKey: - valueForKey: with an unknown key will throw NSUnknownKeyException saying "this class is not key value coding-compliant for the key ".
As said, the objectForKey: datatype is :(id)aKey whereas the valueForKey: datatype is :(NSString *)key.
For example:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:#"123"],[NSNumber numberWithInteger:5], nil];
NSLog(#"objectForKey : --- %#",[dict objectForKey:[NSNumber numberWithInteger:5]]);
//This will work fine and prints ( 123 )
NSLog(#"valueForKey : --- %#",[dict valueForKey:[NSNumber numberWithInteger:5]]);
//it gives warning "Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'" ---- This will crash on runtime.
So, valueForKey: will take only a string value and is a KVC method, whereas objectForKey: will take any type of object.
The value in objectForKey will be accessed by the same kind of object.
This table represents four differences between objectForKey and valueForKey.
objectForKey
valueForKey
Works on ...
NSDictionary
NSDictionary / KVC
Throws exception
No
Yes (on KVC)
Feed
NSObject's subclass
NSString
Usage on KVC
cannot
can
I'll try to provide a comprehensive answer here. Much of the points appear in other answers, but I found each answer incomplete, and some incorrect.
First and foremost, objectForKey: is an NSDictionary method, while valueForKey: is a KVC protocol method required of any KVC complaint class - including NSDictionary.
Furthermore, as #dreamlax wrote, documentation hints that NSDictionary implements its valueForKey: method USING its objectForKey: implementation. In other words - [NSDictionary valueForKey:] calls on [NSDictionary objectForKey:].
This implies, that valueForKey: can never be faster than objectForKey: (on the same input key) although thorough testing I've done imply about 5% to 15% difference, over billions of random access to a huge NSDictionary. In normal situations - the difference is negligible.
Next: KVC protocol only works with NSString * keys, hence valueForKey: will only accept an NSString * (or subclass) as key, whilst NSDictionary can work with other kinds of objects as keys - so that the "lower level" objectForKey: accepts any copy-able (NSCopying protocol compliant) object as key.
Last, NSDictionary's implementation of valueForKey: deviates from the standard behavior defined in KVC's documentation, and will NOT emit a NSUnknownKeyException for a key it can't find - unless this is a "special" key - one that begins with '#' - which usually means an "aggregation" function key (e.g. #"#sum, #"#avg"). Instead, it will simply return a nil when a key is not found in the NSDictionary - behaving the same as objectForKey:
Following is some test code to demonstrate and prove my notes.
- (void) dictionaryAccess {
NSLog(#"Value for Z:%#", [#{#"X":#(10), #"Y":#(20)} valueForKey:#"Z"]); // prints "Value for Z:(null)"
uint32_t testItemsCount = 1000000;
// create huge dictionary of numbers
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithCapacity:testItemsCount];
for (long i=0; i<testItemsCount; ++i) {
// make new random key value pair:
NSString *key = [NSString stringWithFormat:#"K_%u",arc4random_uniform(testItemsCount)];
NSNumber *value = #(arc4random_uniform(testItemsCount));
[d setObject:value forKey:key];
}
// create huge set of random keys for testing.
NSMutableArray *keys = [NSMutableArray arrayWithCapacity:testItemsCount];
for (long i=0; i<testItemsCount; ++i) {
NSString *key = [NSString stringWithFormat:#"K_%u",arc4random_uniform(testItemsCount)];
[keys addObject:key];
}
NSDictionary *dict = [d copy];
NSTimeInterval vtotal = 0.0, ototal = 0.0;
NSDate *start;
NSTimeInterval elapsed;
for (int i = 0; i<10; i++) {
start = [NSDate date];
for (NSString *key in keys) {
id value = [dict valueForKey:key];
}
elapsed = [[NSDate date] timeIntervalSinceDate:start];
vtotal+=elapsed;
NSLog (#"reading %lu values off dictionary via valueForKey took: %10.4f seconds", keys.count, elapsed);
start = [NSDate date];
for (NSString *key in keys) {
id obj = [dict objectForKey:key];
}
elapsed = [[NSDate date] timeIntervalSinceDate:start];
ototal+=elapsed;
NSLog (#"reading %lu objects off dictionary via objectForKey took: %10.4f seconds", keys.count, elapsed);
}
NSString *slower = (vtotal > ototal) ? #"valueForKey" : #"objectForKey";
NSString *faster = (vtotal > ototal) ? #"objectForKey" : #"valueForKey";
NSLog (#"%# takes %3.1f percent longer then %#", slower, 100.0 * ABS(vtotal-ototal) / MAX(ototal,vtotal), faster);
}