objective-c. How to work with immutable Dictionary and return a copy? - objective-c

I'm new in objective-c
The idea is that I'm passing a Dictionary to a method that return an altered Dictionary back.
I have a human "breath" method that expects as argument "air" Dictionary and returns another "air" dictionary.
test
-(void)testBreathIsConsumingTheRightAmountOfOxygen {
NSDictionary *oldAir = [[Environment alloc] init].air;
NSDictionary *newAir = [self.hummy breath:oldAir];
XCTAssertEqual([[newAir valueForKey:#"O2"] intValue], 995);
}
-(void)testBreathIsProducingTheRightAmountOfDioxide {
NSDictionary *oldAir = [[Environment alloc] init].air;
NSDictionary *newAir = [self.hummy breath:oldAir];
XCTAssertEqual([[newAir valueForKey:#"CO2"] intValue], 5);
}
code (my pour solution, I don't like it)
- (NSDictionary *) breath: (NSDictionary *) air {
int breathingStep = 5;
int oxygenOut = [[air valueForKey:#"O2"] intValue] - breathingStep;
int dyoxideOut = [[air valueForKey:#"CO2"] intValue] + breathingStep;
NSMutableDictionary *newAir = [NSMutableDictionary dictionaryWithDictionary:air];
[newAir setValue:[NSNumber numberWithInt:oxygenOut] forKey:#"O2"];
[newAir setValue:[NSNumber numberWithInt:dyoxideOut] forKey:#"CO2"];
return [NSDictionary dictionaryWithDictionary: newAir];
}
The *air Dictionary looks like this
[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:1000], #"O2", [NSNumber numberWithInt:0], #"CO2", nil ]

As a side note you should really be using the modern Objective-C syntax when dealing with NSDictionary and NSNumber.
I think you're looking for the mutableCopy method which returns a mutable version of the current dictionary.
- (NSDictionary *)breath:(NSDictionary *)air {
int breathingStep = 5;
NSMutableDictionary *newAir = [air mutableCopy];
newAir[#"O2"] = #([air[#"O2"] intValue] - breathingStep);
newAir[#"CO2"] = #([air[#"CO2"] intValue] + breathingStep);
return [NSDictionary dictionaryWithDictionary:newAir];
}
To make this better the keys (O2 and CO2) should be constant, for example:
NSString *const kBreathO2Key = #"O2";
NSString *const kBreathCO2Key = #"CO2";
Usage would be:
newAir[kBreathO2Key] = #([air[kBreathO2Key] intValue] - breathingStep);
To make this even better again it may be more appropriate to create a class to hold these values rather than a dictionary. Then you wouldn't need to convert between NSNumber and int.
#interface ASAir : NSObject
#property (nonatomic, assign) NSInteger O2;
#property (nonatomic, assign) NSInteger CO2;
#end
#implementation
#end
Then the breath: method could be improved as follows:
- (ASAir *)breath:(ASAir *)air {
int breathingStep = 5;
ASAir *newAir = [[ASAir alloc] init];
newAir.O2 = air.O2 - breathingStep;
newAir.CO2 = air.CO2 + breathingStep;
return newAir;
}

Related

Why does NSMutablearray keep returning null?

I am generating a random equation say like 2*3+4..... and using DDMathparser to evaluate it. Here I have a class method which is supposed to return a random equation(stored inside a mutable array) only if it evaluates to a integer.
however it keeps returning Null and i can't figure out why. Please help me out.!
#import "Equation.h"
#import "DDMathParser.h"
#implementation Equation
-(NSMutableArray*)randEquation{
NSMutableArray* usableEquation=[[NSMutableArray alloc]init];
while(1){
NSArray *nums = #[#"1", #"2", #"3", #"4", #"5",#"6",#"7",#"8",#"9"];
unsigned index1=arc4random()%9;
NSString* num = [NSString stringWithFormat:#"%#", [nums objectAtIndex:index1]];
NSArray *symbols = #[#"+", #"-", #"*", #"/"];
unsigned index=arc4random()%4;
NSString* symb = [NSString stringWithFormat:#"%#", [symbols objectAtIndex:index]];
NSMutableArray *arrayOfSymbolsAndNumbers = [[NSMutableArray alloc] init];
for( int i=0;i<=10;i++){
if (i%2==0) {
[arrayOfSymbolsAndNumbers addObject:num];
}
else{
[arrayOfSymbolsAndNumbers addObject:symb];
}
}
NSMutableString *stringOfSymbolsAndNumbers=[[NSMutableString alloc]init];
for (NSObject * obj in arrayOfSymbolsAndNumbers)
{
[stringOfSymbolsAndNumbers appendString:[obj description]];
}
usableEquation=arrayOfSymbolsAndNumbers;
NSNumber *result=[stringOfSymbolsAndNumbers numberByEvaluatingString];
float resultFloat = [result floatValue];
float checker=resultFloat;
if (floor(checker)==checker) {
break;
}
else{
continue;
}
}
return usableEquation;
}
#end
NSLog(#"The content of array is%#",[equation randEquation]);
Based on your code, for this log to output The content of array is(null) means that equation is nil. Your randEquation (while not efficient) looks ok, the problem is that you haven't created the equation instance when you run the log statement.

Build an array of pointers from existing pointers

I have a class which must initialize a group of similar objects the same way.
NSNumber *a = nil;
NSNumber *b = nil;
NSNumber *c = nil;
a, b and c are member variables of an existing object. In my code I use a more complex NSObject subclass, but this example is easier to understand using NSNumbers.
All 3 objects must be initialized in a similar way. Therefore I want to build an array of pointers which I can use within a for-loop like this:
NSPointerArray *objs = [NSPointerArray weakObjectsPointerArray];
[objs addPointer:&a];
[objs addPointer:&b];
[objs addPointer:&c];
Running the above code I get the following error:
error: address doesn't contain a section that points to a section in a object file
How can I build an array which I can explore with a loop like this?
for (id *o in objs) {
*o = #2;
}
just wrap the pointers in 'NSValue' objects and you're good
id v1 = [NSValue valueWithPointer:p1];
id v2 = [NSValue valueWithPointer:p2];
id v3 = [NSValue valueWithPointer:p3];
id array = #[v1,v2,v3];
for(NSValue *value in array) {
void *pointer = value.pointerValue;
NSNumber *n = (__bridge NSNumber*)pointer;
}
demo for what YOU want to do it appears: (NSNumbers are immutable)
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
#autoreleasepool {
NSNumber *a;
NSNumber *b;
NSArray *os = #[ [NSValue valueWithPointer:&a],
[NSValue valueWithPointer:&b] ];
for (NSValue *v in os) {
void *pointer = [v pointerValue];
NSNumber **n_ptr = (__bridge NSNumber**)pointer;
*n_ptr = #2.0;
}
NSLog(#"%# %#", a,b);
}
}

Array of floating point values in Objective-C

How can I create array of floating point numbers in Objective-C?
Is it possible?
You can create a dynamic array (size decided at runtime, not compile time) in different ways, depending on the language you wish to use:
Objective-C
NSArray *array = [[NSArray alloc] initWithObjects:
[NSNumber numberWithFloat:1.0f],
[NSNumber numberWithFloat:2.0f],
[NSNumber numberWithFloat:3.0f],
nil];
...
[array release]; // If you aren't using ARC
or, if you want to change it after creating it, use an NSMutableArray:
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0];
[array addObject:[NSNumber numberWithFloat:1.0f]];
[array addObject:[NSNumber numberWithFloat:2.0f]];
[array addObject:[NSNumber numberWithFloat:3.0f]];
...
[array replaceObjectAtIndex:1 withObject:[NSNumber numberWithFloat:99.9f]];
...
[array release]; // If you aren't using ARC
Or using the new-ish Objective-C literals syntax:
NSArray *array = #[ #1.0f, #2.0f, #3.0f ];
...
[array release]; // If you aren't using ARC
C
float *array = (float *)malloc(sizeof(float) * 3);
array[0] = 1.0f;
array[1] = 2.0f;
array[2] = 3.0f;
...
free(array);
C++ / Objective-C++
std::vector<float> array;
array[0] = 1.0f;
array[1] = 2.0f;
array[2] = 3.0f;
For an dynamic approach you can use NSNumber object and add it to NSMutableArray, or if you need only static array then use suggestions from comments, or use standard C.
like:
NSMutableArray *yourArray = [NSMutableArray array];
float yourFloat = 5.55;
NSNumber *yourFloatNumber = [NSNumer numberWithFloat:yourFloat];
[yourArray addObject:yourFloatNumber];
and then to retrive:
NSNumber *yourFloatNumber = [yourArray objectAtIndex:0]
float yourFloat = [yourFloatNumber floatValue];
If you are using Xcode 4.4+, you can try this:
NSArray *a = #[ #1.1f, #2.2f, #3.3f];
Here is all new literals of LLVM Compiler 4.0.
How about something like this?
#interface DoubleArray : NSObject
#property(readonly, nonatomic) NSUInteger count;
#property(readonly, nonatomic) double *buffer;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithCount:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
- (double)valueAtIndex:(NSUInteger)idx;
- (void)setValue:(double)value atIndex:(NSUInteger)idx;
#end
#implementation DoubleArray
- (void)dealloc
{
if (_buffer != 0) {
free(_buffer);
}
}
- (instancetype)initWithCount:(NSUInteger)count
{
self = [super init];
if (self) {
_count = count;
_buffer = calloc(rows * columns, sizeof(double));
}
return self;
}
- (double)valueAtIndex:(NSUInteger)idx
{
return *(_buffer + idx);
}
- (void)setValue:(double)value atIndex:(NSUInteger)idx
{
*(_buffer + idx) = value;
}
#end
It's a basic array. You can extend this with more complex features like appending, indexed removal etc.

convert NSDictionary's key into an NSNumber

How can i convert the key to an NSNumber? this code gives me the error:
no matter how i try it, i always end up with the error: Incompatible integer to pointer conversion assigning to 'NSNumber' from 'NSIntegar' (aka 'int').
for (id key in consultants)
{
consultantData = [[ConsultantData alloc] init];
consultantData.name = [consultants objectForKey:key];
consultantData.conID = [[NSNumber numberWithInteger:key] integerValue];
NSLog(#"name: %# ID: %#", consultantData.name, consultantData.conID);
[consultantList addObject:consultantData];
[consultantData release];
}
here is my object ConsultantData:
#import <Foundation/Foundation.h>
#interface ConsultantData : NSObject
{
NSString *name;
NSNumber *conID;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSNumber *conID;
#end
#import "ConsultantData.h"
#implementation ConsultantData
#synthesize name;
#synthesize conID;
-(void) dealloc
{
[name release];
[conID release];
[super dealloc];
}
#end
if i try isMemberOfClass, it never returns true. if i try isKindOfClass, it tells me its a NSString.
consultants is a dictionary i receive from my sever that passes thru an XMLRPC function (server is PHP). from what i've seen, everything is a string in the server dictionary returns.
could be that you have the order wrong.
consultantData.conID = [[NSNumber numberWithInteger:key] integerValue];
means
NSNumber *foo = [NSNumber numberWithInteger:key];
NSInteger bar = [foo integerValue];
consultantData.conID = bar;
you probably want
consultantData.conID = [NSNumber numberWithInteger:[key integerValue]];
which is in verbose form:
NSInteger foo = [key integerValue];
NSNumber *bar = [NSNumber numberWithInteger:foo];
consultantData.conID = bar;
consultantData.conID = [[NSNumber numberWithInteger:key] integerValue];
The above line should be:
consultantData.conID = [NSNumber numberWithInteger:[key integerValue]];
Assuming your key is an NSString, you can't pass the key to [NSNumber numberWithInteger:key] because it is not an integer! Use the intValue method on the key to get an integer, then convert the integer to a NSNumber like so:
consultantData.conID = [NSNumber numberWithInteger:[key intValue]];
integerValue returns an NSInteger, which is not an NSNumber. You should pass the NSNumber directly:
consultantData.conID = [NSNumber numberWithInteger:[key integerValue]];

How to convert nsstring to nsdictionary?

I have gone through following question.
Convert NSString to NSDictionary
It is something different then my question.
My question is as follows.
NSString *x=#"<Category_Id>5</Category_Id><Category_Name>Motos</Category_Name><Category_Picture>http://192.168.32.20/idealer/admin/Picture/icon_bike2009819541578.png</Category_Picture>";
Now I want to convert this into a dictionary, something like this,
dictionary key = Category_Id | value = 5
dictionary key = Category_Name | value = ???
dictionary key = Category_Picture | value = ???
I don't know how to perform this.
Not the fastest implementation, but this would do the trick (and doesn’t require any third party libraries):
#interface NSDictionary (DictionaryFromXML)
+ (NSDictionary *)dictionaryFromXML:(NSString *)xml;
#end
#implementation NSDictionary (DictionaryFromXML)
+ (NSDictionary *)dictionaryFromXML:(NSString *)xml
{
// We need to wrap the input in a root element
NSString *x = [NSString stringWithFormat:#"<x>%#</x>", xml];
NSXMLDocument *doc = [[[NSXMLDocument alloc] initWithXMLString:x
options:0
error:NULL]
autorelease];
if (!doc)
return nil;
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (NSXMLElement *el in [[doc rootElement] children])
[dict setObject:[el stringValue] forKey:[el name]];
return dict;
}
#end
If it's XML then you can use an NSXMLParser. If it's not then you can write your own parser.
You could do it with a regular expression... Something like <([^>]+)>([^<]+)</\1> would grab the key into capture 1 and the value into capture 2. Iterate over the matches and build the dictionary.
This uses RegexKitLite:
NSString * x = #"<Category_Id>5</Category_Id><Category_Name>Motos</Category_Name><Category_Picture>http://192.168.32.20/idealer/admin/Picture/icon_bike2009819541578.png</Category_Picture>";
NSString * regex = #"<([^>]+)>([^<]+)</\\1>";
NSArray * cap = [x arrayOfCaptureComponentsMatchedByRegex:regex];
NSMutableDictionary * d = [NSMutableDictionary dictionary];
for (NSArray * captures in cap) {
if ([captures count] < 3) { continue; }
NSString * key = [captures objectAtIndex:1];
NSString * value = [captures objectAtIndex:2];
[d setObject:value forKey:key];
}
NSLog(#"%#", d);