Get NSDictionary with variable names as keys - ios

Say I have n variables
NSNumber* A = #(1);
NSNumber* B = #(2);
NSNumber* C = #(3);
NSNumber* D = #(4);
NSNumber* E = #(5);
...
I need a dictionary like
{#"A":#(1), #"B":#(2), #"C":#(3), #"D":#(4), ... }
One can imagine a more cumbersome example that would be tedious to type out
I think saw a C style function for it but I can't remember. Something like NSDictionaryForVariables()

The C preprocessor macro (not a function) you're looking for is NSDictionaryOfVariableBindings. However, outside of Auto Layout (and, debatably, even there), it's not really a great idea to be setting up dependencies between runtime and compile-time identifiers like that.
Depending on what you're trying to actually accomplish, Key-Value Coding might be a better solution.

It's not a good approach you may find another way to solve your issue but if you want to have an idea about your requested solution here here my try
Our properties
#interface TestyViewController ()
#property (nonatomic) NSNumber* a;
#property (nonatomic) NSNumber* b;
#property (nonatomic) NSNumber* c;
#property (nonatomic) NSNumber* d;
#end
Set the values
- (void)viewDidLoad {
[super viewDidLoad];
self.a=#(1);
self.b=#(2);
self.c=#(3);
self.d=#(4);
}
Get our instance variables
-(NSArray *)propertyNames{
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);
NSMutableArray * propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
return propertyNames;
}
Create the dictionary
- (IBAction)buttonClicked:(id)sender
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (NSString* varName in [self propertyNames])
{
[dict setObject:[self valueForKey:varName] forKey:varName];
}
NSLog(#"%#",dict);
}
result
2015-07-15 20:30:56.546 TestC[879:27973] {
a = 1;
b = 2;
c = 3;
d = 4;
}

Related

Sort 2 NSArrays By Date [duplicate]

This question already has answers here:
Sorting two NSArrays together side by side
(4 answers)
Closed 6 years ago.
I have 2 NSMutableArrays declared. One is filled with names and then another one is filled with string values of NSDate.
I want to sort both of them according to the date in the second one. For example if element 3 in the date array becomes element 0 I want the same to happen for the name array.
What is the easiest way to do this? I know how to sort the date array just not the corresponding name array!
(Objective-C Please!)
Sorting 2 arrays and keeping them in sync is a pain. You basically have to sort them by hand.
Probably the easiest way to do this is to create an array of dictionaries where each dictionary contains a data and a name.
Then sort the array of dictionaries by date.
EDIT:
Here is the code for creating custom objects containing a name and a date. There is code to sort by date as well as code to sort by name:
/**
The thing class has a property date and a property name.
It is purely for creating sorted arrays of objects
*/
#interface Thing : NSObject
#property (nonatomic, strong) NSDate *date;
#property (nonatomic, strong) NSString *name;
#end
#implementation Thing
/**
This is a dummy init method that creates a Thing ojbect with a random name and date
*/
- (instancetype) init {
self = [super init];
if (!self) return nil;
NSString *letters = #"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSMutableString *temp = [NSMutableString new];
for (int x = 0; x < 10; x++) {
unichar aChar = [letters characterAtIndex:arc4random_uniform(26)];
[temp appendFormat: #"%C", aChar];
}
self.name = [temp copy];
//Create a random date
uint32 halfMax = 2000000000;
uint32 max = halfMax * 2;
int32_t value = arc4random_uniform(max) - halfMax;
NSTimeInterval now = [[NSDate date] timeIntervalSinceReferenceDate];
self.date = [NSDate dateWithTimeIntervalSinceReferenceDate: now + value];
return self;
}
- (NSString *) description {
return [NSString stringWithFormat: #"Name: %# Date: %#", self.name, self.date];
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
//Create an array of Thing objects
const int count = 50;
NSMutableArray *thingArray = [NSMutableArray arrayWithCapacity: count];
for (int x = 0; x < count; x++) {
thingArray[x] = [[Thing alloc] init];
}
#if 1
//Sort by date, ascending
[thingArray sortUsingComparator:^NSComparisonResult(Thing *obj1,
Thing *obj2) {
NSComparisonResult bigger =
[obj1.date timeIntervalSinceReferenceDate] <
[obj2.date timeIntervalSinceReferenceDate] ?
NSOrderedAscending : NSOrderedDescending;
return bigger;
}];
#else
//Sort by name
[thingArray sortUsingComparator:^NSComparisonResult(Thing *obj1,
Thing *obj2) {
return [obj1.name compare: obj2.name];
}];
#endif
NSLog(#"%#", thingArray);
}
return 0;
}

Set Default values of NSString properties automatically

I have many bean/data classes in my code which I'm using to convert to JSON for network communication purposes. The issue is, if there's a NSString property in my class I want to set its default value to empty string #"" rather than nil. One option I have is this :Setting Default Values For NSString Properties but I have to write code to set properties values, I don't want to do this.
I tried getting all the properties using Objc runtime and did something like this:
unsigned int numberOfProperties = 0;
objc_property_t *propertyArray = class_copyPropertyList([self class], &numberOfProperties);
for (NSUInteger i = 0; i < numberOfProperties; i++)
{
objc_property_t property = propertyArray[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
const char * propAttr = property_getAttributes(property);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:#"\""];
if (attrArray.count > 0) {
NSString *propType = [attrArray objectAtIndex:1];
if ([propType containsString:#"NSString"]) {
[self setValue:#"" forKey:name];
}
}
}
free(propertyArray);
This is working like a charm for me. The only issue is I have inherited classes and this code only sets the values for child class, it doesn't sets values of properties in base class. I'm using xcode 6.3.1 & iOS 8.x. Any help is much appreciated. Thanks
You may define a recursive method setDefaultPropValuesForClass: in your bean/data base class, e.g. Bean, and invoke it from base class init method. See the implementation below:
#interface Bean : NSObject
// Add your props
// ...
// .....
#end
#implementation Bean
- (instancetype)init {
self = [super init];
if (self) {
[self setDefaultPropValues];
// TODO: All other initializations
}
return self;
}
- (void)setDefaultPropValues {
[self setDefaultPropValuesForClass:self.class];
}
- (void)setDefaultPropValuesForClass:(Class)refClass {
if (![refClass isSubclassOfClass:[Bean class]]) {
return;
}
// First set default property values in super classes
Class baseClass = class_getSuperclass(refClass);
[self setDefaultPropValuesForClass:baseClass];
//
unsigned int numberOfProperties = 0;
objc_property_t *propertyArray = class_copyPropertyList(refClass, &numberOfProperties);
for (NSUInteger i = 0; i < numberOfProperties; i++)
{
objc_property_t property = propertyArray[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
const char * propAttr = property_getAttributes(property);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *allAttrs = [propString componentsSeparatedByString:#","];
// Check if property is readonly
if (NSNotFound == [allAttrs indexOfObject:#"R"]) {
// Find Property type token
NSArray * attrArray = [propString componentsSeparatedByString:#"\""];
if (attrArray.count > 1) {
Class propType = NSClassFromString([attrArray objectAtIndex:1]);
if ([propType isSubclassOfClass:[NSString class]]) {
[self setValue:#"" forKey:name];
}
}
}
}
free(propertyArray);
}
#end
Can you check if your class is a subclass by
[self class] is SubclassOfClass: then get copy of property list of base or super class.
objc_property_t *propertyArray = class_copyPropertyList([[self class]superclass], &numberOfProperties);

how can i put the integerValue for textField into the array?

#property NSMutableArray*textFieldsA;
#property NSMutableArray*textFieldsB;
#property NSMutableArray*textFieldsR;
#property NSMutableArray *operandArrayA;
#property NSMutableArray *operandArrayB;
_textFieldsA = #[self.textFieldA0, self.textFieldA1, self.textFieldA2, self.textFieldA3, self.textFieldA4, self.textFieldA5, self.textFieldA6, self.textFieldA7, self.textFieldA8];
_textFieldsB = #[self.textFieldB0, self.textFieldB1, self.textFieldB2, self.textFieldB3, self.textFieldB4, self.textFieldB5, self.textFieldB6, self.textFieldB7, self.textFieldB8];
_textFieldsR = #[self.textFieldR0, self.textFieldR1, self.textFieldR2, self.textFieldR3, self.textFieldR4, self.textFieldR5, self.textFieldR6, self.textFieldR7, self.textFieldR8];
for (int i = 0; i < 9; i++) {
_operandA = [((UITextField*)_textFieldsA[i]).text integerValue];
_operandB = [((UITextField*)_textFieldsB[i]).text integerValue];
[self.arithmetic setOperandA:_operandA operandB:_operandB operator:_operator];
_finalString = [NSString stringWithFormat:#"%d",[self.arithmetic result]];
((UITextField*)_textFieldsR[i]).text = _finalString;
}
how can i put the integerValue of
self.textFieldA0.text , self.textFieldA1.text , self.textFieldA3.text ........self.textFieldA8.text
in to the array (operandArrayA)?
i have tried
operandArrayA[i] = [((UITextField*)_textFieldsA[i]).text integerValue];
but it is not work, how should i do?
Unfortunately you can not add primitive datatypes in NSArray. You need to create object of NSNumber for integer value. Store this object to an array.
Like this:
NSNumber *number = #([((UITextField*)_textFieldsA[i]).text integerValue]);
operandArrayA[i] = number;
You need to alloc and initialize all the arrays..like below
operandArrayA=[[NSMutableArray alloc]init];
operandArrayB=[[NSMutableArray alloc]init];
//if u want to store strings
[operandArrayA addObject:((UITextField*)_textFieldsA[i]).text];
[operandArrayB addObject:((UITextField*)_textFieldsB[i]).text];
//(OR) if u want to store integer value then u need to convert to NSNumber
[operandArrayA addObject:[NSNumber numberWithInt:[((UITextField*)_textFieldsA[i]).text integerValue]]];
[operandArrayB addObject:[NSNumber numberWithInt:[((UITextField*)_textFieldsB[i]).text integerValue]]];
//(OR)
operandArrayA[i]=[NSNumber numberWithInt:[((UITextField*)_textFieldsA[i]).text integerValue]];
operandArrayB[i]=[NSNumber numberWithInt:[((UITextField*)_textFieldsB[i]).text integerValue]];
Hope it helps you..

Cannot sum values downloaded into array

I am working on a project where I download a few values (stored as float in my database) into my array and sum them. I can NSLog the numbers but Unfortunately I am having the hardest time to sum those values in my array.
Adding some code
DETAILS.H
#import <Foundation/Foundation.h>
#interface Details: NSObject
#property (nonatomic, strong)NSNumber *min;
//#property (nonatomic, strong) NSString *min;
#end
just the part of the code where I put the values in the array
for (int i = 0; i < jsonArray.count; i++)
{
NSDictionary *jsonElement = jsonArray[i];
tempDetails *downloadTemps = [[tempDetails alloc] init];
downloadTemps.min = jsonElement[#"min"];
// Add this question to the locations array
[_locations addObject:downloadTemps];
}
View controller code
for (Details *sav in _importArray )
{
NSLog(#"High :- %# ", sav.min); //THIS LIST ALL MY VALUES CORRECTLY
}
NSMutableArray *newArray = [_importArray mutableCopy];
int totalSum = 0;
for(int i=0; i<[newArray count];i++)
{
totalSum = totalSum + [[newArray objectAtIndex:i] intValue];
}
NSLog(#"Total:%d",totalSum);
In the view controller I get the error
[Details intValue]: unrecognized selector sent to instance ,
I am assuming I am getting this error because min is not declared right, But I am unsure how to do it other wise. Any help offered would be appreciated.
Thank you
You are asking for the selector intValue, but you should be asking for the selector min.
totalSum = totalSum + [[[newArray objectAtIndex:i] min] intValue];
You were pretty close in the first block you posted:
for (Details *sav in _importArray )
{
NSLog(#"High :- %# ", sav.min); //THIS LIST ALL MY VALUES CORRECTLY
}
Then:
int totalSum = 0;
for (Details *sav in _importArray )
{
totalSum += [sav.min intValue];
}
Incidentally, why are you asking for the intValue of something that you initially wrote was a float?

Using NSMutableArray to keep Objects

I think that I'm missing some fundamental knowledge on Xcode Objective C programming standards. Unfortunately I couldn't find the appropriate solution to my problem.
The problem is that when I try to keep data in an array of objects it becomes impossible to keep them separately. Adding new objects overwrites the previous objects in array. Here is some code about that:
CustomObject.m file:
#import "CustomObject.h"
NSString * title;
NSString * detail;
#implementation CustomObject
- (void) initCustomObjectWithValues : (NSString *) iTitle : (NSString *) iDetail {
title = [NSString stringWithString:iTitle];
detail = [NSString stringWithString:iDetail];
}
- (NSString *) getTitle {
return title;
}
- (NSString *) getDetail {
return detail;
}
#end
viewDidLoad function in ViewController.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
myMutableArray = [[NSMutableArray alloc] init];
for (int i=0; i<10; i++) {
NSString * tempTitle = [#"title " stringByAppendingString:[NSString stringWithFormat:#"%d",i]];
CustomObject * myCustomObject = [[CustomObject alloc] init];
[myCustomObject initCustomObjectWithValues :[NSString stringWithFormat:#"%#",tempTitle]
:[#"detail " stringByAppendingString:[NSString stringWithFormat:#"%d",i]]];
[myMutableArray addObject:myCustomObject];
}
for (int i=0; i<10; i++) {
NSLog(#"%#",[[myMutableArray objectAtIndex:i] getTitle]);
NSLog(#"%#",[[myMutableArray objectAtIndex:i] getDetail]);
NSLog(#"----------------------------");
}
}
Here, myMutableArray is defined at the top of the ViewController.m file. (To make it global and can be used in other functions in future)
Here what I've got in the logs:
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
As far as I understand each new added object overwrites the olds. First I thought that they are referring to same allocated memory but in debug tool myMutableArray seems like this:
Printing description of myMutableArray:
<__NSArrayM 0x8d8cb60>(
<CustomObject: 0x8d8e990>,
<CustomObject: 0x8d8dd40>,
<CustomObject: 0x8d8d2e0>,
<CustomObject: 0x8d8d470>,
<CustomObject: 0x8d8d350>,
<CustomObject: 0x8d8ddf0>,
<CustomObject: 0x8d8df00>,
<CustomObject: 0x8d8df40>,
<CustomObject: 0x8d8dff0>,
<CustomObject: 0x8d8e0c0>
)
Does anyone have an idea about the solution. It should be something very basic but I can't catch the problem.
Thank you all in advance
using
NSString * title;
NSString * detail;
outside of the #interface part creates global variables. When you assign a variable to title or detail you don't set an instance variable of your object, you change those global variables. And since they are global, they are the same for all objects that reference them.
Turn those global variables into instance variables, or even better use #property.
Your code is bad objective-c overall.
You should not use get in getters that return variables. You should not have methods that start with init and don't return self. You should only call init in [[Foo alloc] init...] situations. You should avoid unnamed parameters in your methods.
And there is no need to create strings from strings from strings.
Here is how I would write it:
// CustomObject.h
#interface CustomObject : NSObject
#property (copy, nonatomic) NSString * title;
#property (copy, nonatomic) NSString * detail;
- (id)initWithTitle:(NSString *)title detail:(NSString *)detail
#end
// CustomObject.m
#import "CustomObject.h"
#implementation CustomObject
- (id)initWithTitle:(NSString *)title detail:(NSString *)detail {
self = [super init];
if (self) {
// use stringWithString: to create #"" strings when title is nil
// if nil is a valid value for those variables you should use
// _title = [title copy];
_title = [NSString stringWithString:title];
_detail = [NSString stringWithString:detail];
}
return self;
}
#end
for (int i=0; i<10; i++) {
NSString *tempTitle = [NSString stringWithFormat:#"title %d",i];
NSString *tempDetail = [NSString stringWithFormat:#"detail %d",i];
CustomObject * myCustomObject = [[CustomObject alloc] initWithTitle:tempTitle detail:tempDetail];
[myMutableArray addObject:myCustomObject];
}
for (int i=0; i<10; i++) {
CustomObject *object = myMutableArray[i];
NSLog(#"%#", object.title);
// or NSLog(#"%#", [object title]); if you don't like dot-notation.
NSLog(#"%#", object.detail);
NSLog(#"----------------------------");
}

Resources