NSArray enumerating by group - ios

Using Objective-C, is it possible to go through an array by groups :
exemple :
NSArray *arr = 1, 2, 3, ....100;
Every 10 objects, do something and go on
so :
object 0 to 9 : you do something with each object and after the 10° object you do a last action
then object 10 to 19 : you do something with each object and after the 19° object you do a last action
and so on until the last object
thank you for your help

something like this:
for (int i = 0; i < arr.count; i++)
{
[self doSomethingWithArray];
if (i % 10 == 0)
[self doSomethingElse];
}

No it is not possible in Objective-C with in-built functions which matches your exact description. There are crude ways to do it by loops which matches your exact description.
But if you are aware before hand that you are going to make such type of operations, define your own data-structure. Create an NSObject sub-class, define your items (10 items which you were talking about) in it. Then in array, you can directly take out each instance of it comprising of your defined NSObject.

"enumerating by group"; If you want exactly as stated, you can subclass NSEnumerator.
For example:
In your Application code:
#import "NSArray+SubarrayEnumerator.h"
NSArray *arr = ...;
for(NSArray *grp in [arr subarrayEnumeratorEach:10]) {
// do what you want.
}
NSArray+SubarrayEnumerator.h
#import <Foundation/Foundation.h>
#interface NSArray (SubarrayEnumerator)
- (NSEnumerator *)subarrayEnumeratorEach:(NSUInteger)perPage;
#end
NSArray+SubarrayEnumerator.m
#import "NSArray+SubarrayEnumerator.h"
#interface _NSArraySubarrayEnumeratorEach : NSEnumerator
#property (assign, nonatomic) NSUInteger cursor;
#property (assign, nonatomic) NSUInteger perPage;
#property (strong, nonatomic) NSArray *src;
#end
#implementation NSArray (SubarrayEnumerator)
- (NSEnumerator *)subarrayEnumeratorEach:(NSUInteger)perPage {
_NSArraySubarrayEnumeratorEach *enumerator = [[_NSArraySubarrayEnumeratorEach alloc] init];
enumerator.perPage = perPage;
enumerator.src = self;
return enumerator;
}
#end
#implementation _NSArraySubarrayEnumeratorEach
- (id)nextObject {
NSUInteger start = _cursor;
if(start >= _src.count) {
return nil;
}
NSUInteger count = MIN(_perPage, _src.count - start);
_cursor += _perPage;
return [_src subarrayWithRange:NSMakeRange(start, count)];
}
#end

Related

How can I ensure value is in the custom class property at the NSMutableArray?

I create the custom class name with FileModel.
FileModel.h
#import <Foundation/Foundation.h>
#interface FileModel : NSObject
#property (nonatomic, copy) NSString *fileName;
#property (nonatomic, copy) NSString *fileType;
#property (nonatomic, strong) NSDate *editDate;
#property (nonatomic, assign) NSInteger fileSize;
#end
I want to compare the particular string with the fileName.
I create the sample like below .m
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *fileSampleName = [[NSArray alloc] initWithObjects:#"apple.png",#"banana.png",#"cherry.png",#"durian.png",#"grape.png",#"avocado.png", nil];
NSMutableArray *fileData = [NSMutableArray new];
FileModel *fileModel = nil;
for( NSInteger i = 0 ; i < fileSampleName.count ; i++){
fileModel = [FileModel new];
fileModel.fileName = [fileSampleName objectAtIndex:i];
fileModel.fileType = #"photo";
fileModel.fileSize = 0;
fileModel.editDate = [NSDate new];
[fileData addObject:fileModel];
}
// fileData's fileName containsObject #"grape" or not?
}
NSArray has containsObject method.
But How can I check the #"grape" is containsObject using fileData at the custom class property filename?
I known using for loop compare one by one.
Did they have other method to check like containsObject?
--- edit---
I try to using indexOfObjectPassingTest method , But the result always is 1.
BOOL result = [fileData indexOfObjectPassingTest:^ BOOL (id tr,NSUInteger index, BOOL *te){
FileModel *fileModel = (FileModel*)tr;
if([#"orange" isEqualToString: fileModel.fileName]){
*te = YES;
return YES;
}else{
return NO;
}}];
NSLog(#"result:%#",#(result)); // it always return 1
Why? thank you very much.
Take a look that NSArray class reference in Xcode. One method you could use is indexOfObjectPassingTest. There are number of related methods depending on your needs. All take a block that's used to test objects to see if they meet whatever criteria you want. In your case you'd test the fileName string.
So you'd pass in a closure that compared the fileName property of each object to your desired filename.

iOS - JSONModel with dynamic key

I am calling a web service which returns dictionary to render the graph. Dictionary structure is
{"1":0,"2":0,"8":0,"9":2,"10":3,"11":0,"12":0}
The problem is keys are dynamic values like 1,2,3 etc which indicates month. Is it possible to represent this in JsonModel?
See you cant create properties at run time as per response structure. But we can smartly use pre-defined things and achieve this. Do following steps:
Create one model class. So your MyCustomModel.h file will look like this
#import <Foundation/Foundation.h>
#interface MyCustomModel : NSObject
#property (nonatomic, retain) NSString * myCustomKey;
#property (nonatomic, retain) NSString * myCustomValue;
#end
This will be your MyCustomModel.m file
#import "MyCustomModel.h"
#implementation MyCustomModel
#synthesize myCustomKey, myCustomValue;
-(id)init {
self = [super init];
myCustomKey = #"";
myCustomValue = #"";
return self;
}
#end
Now lets suppose {"1":0,"2":0,"8":0,"9":2,"10":3,"11":0,"12":0} is NSDictionary and lets say its name is dictionaryResponse
Now do this stuffs:
NSArray *responseKeys = [[NSArray alloc]init];
responseKeys = [dictionaryResponse allKeys];
So your responseKeys will have all keys of response like ["1","2","8","9","10","11","12",]
Now you can iterate loop and create NSMutableArray of model objects as
NSMutableArray *arrayMonthList = [[NSMutableArray alloc]init];
for (int i = 0; i < responseKeys.count; i++) {
MyCustomModel *myModelObject = [[MyCustomModel alloc]init];
myModelObject.myCustomKey = [NSString stringWithFormat:#"%#",[responseKeys objectAtIndex:i]];
myModelObject.myCustomValue = [dictionaryResponse valueForKey:[NSString stringWithFormat:#"%#",[responseKeys objectAtIndex:i]]];
[arrayMonthList addObject:myModelObject];
}
Now arrayMonthList will consist of objects of type MyCustomModel
So you can use it and parse it. Even you can use it to show on UITableView. Following code is written to print values of model properties, you can customise at your expected level.
for (int i = 0; i < arrayMonthList.count; i++) {
MyCustomModel *myModelObject = [arrayMonthList objectAtIndex:i];
NSLog(#"Month is %# and its value is %#",myModelObject.myCustomKey,myModelObject.myCustomValue);
}

How to print (using NSLog) all properties of a class automatically?

I think it's very difficult to print out the value of all properties of any class in objective-c, in the case the type of the property is complex.
But if the class that contains properties with simple types (like, NSString, int, double, boolean), is there any way to NSLog automatically instead of NSLog manually the value of each property?
Updated:
All the solutions you gave me are still manually. Is there any way like iterate through all properties of a class, and NSLog the variable_name and the variable_value. That's what I expected.
You can do this by overriding -(void)description method.
Example:
Let's say we have simple Car class.
#interface Car : NSObject
#property (copy, nonatomic) NSString *model;
#property (copy, nonatomic) NSString *make;
#property (strong, nonatomic) NSDate *registrationDate;
#property (assign, nonatomic) NSInteger mileage;
#property (assign, nonatomic) double fuelConsumption;
#end
#implementation
- (NSString*)description {
return [NSString stringWithFormat:#"<%#:%p %#>",
[self className],
self,
#{ #"model" : self.model,
#"make" : self.make,
#"registrationDate": self.registrationDate,
#"mileage" : #(self.mileage),
#"fuelConsumption" : #(self.fuelConsumption)
}];
}
#end
Putting this in NSDictionary will create very nice output in console.
On the other hand, you can create category on NSObject class and do something like this:
- (NSString*)myDescriptionMethod {
NSMutableDictionary *dict = [NSMutableDictionary new];
unsigned int count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
const char *property = property_getName(properties[i]);
NSString *propertyString = [NSString stringWithCString:property encoding:[NSString defaultCStringEncoding]];
id obj = [self valueForKey:propertyString];
[dict setValue:obj forKey:propertyString];
}
free(properties);
return [NSString stringWithFormat:#"<%# %p %#>",
[self class],
self,
dict];
}
Then you will avoid overriding -(void)description method in your classes.
Get it from here
The most elegant way to achieve what you're looking for in Objective-C with NSObject subclasses, it to override the NSObject method description.
For example (assuming your Class has a property called propertyX):
-(NSString *)description
{
return [NSString stringWithFormat:#"<myCustomObject: %#, propertyX: %f, %f>",
[self objectID], [self propertyX].x, [self propertyX].y];
}
The default description implementation of NSObject will simply return the memory address pointed to for the object, like so:
NSLog(#"%#", self);
2015-06-15 14:20:30.123 AppName[...] myCustomObject: 0x000000>
However, by overriding this base Class method as shown above, you will be able to customise this behavior, and the log will look like this:
2015-06-15 14:20:30.123 AppName[...] myCustomObject: 0x000000 someProperty, Property: blah, blah>
There is a nice tutorial, which discusses this further here.
Example :-
+ (NSString *)description;
[NSString description];
Gives you information about the class NSString.

Sorting NSMutableArray contains NSMutableString in Objective C

Newbie Question,
I am playing with NSMutableArray *myBook that contains card elements (name and email address).
I want myBook displayed with descending list.
AddressBook.h
#import <Foundation/Foundation.h>
#import "AddressCard.h"
#interface AddressBook : NSObject
#property (nonatomic, copy) NSMutableString *bookName;
#property (nonatomic , strong) NSMutableArray *book;
AddressCard.h
#import <Foundation/Foundation.h>
#interface AddressCard : NSObject
#property (nonatomic, copy) NSMutableString *name, *email;
AddressBook.m
- (void) sort{
[book sortUsingSelector:#selector(compareNames:)];
}
- (void) showList{
NSLog(#"====== Contents of: %# =======",bookName);
for (AddressCard *theCard in book) {
NSLog(#"%-20s %-32s", [theCard.name UTF8String], [theCard.email UTF8String] );
}
NSLog(#"===============================");
}
AddressCard.m
- (NSComparisonResult) compareNames: (id) element{
return [name compare:[element name]];
}
my CompareNames method returns Ascending result. How do I make it to return Descending?
Thanks !
while - (void)sortUsingSelector:(SEL)comparator method suppose to sorts the array's elements in ascending order, the comparator method should return NSOrderedAscending if the array is smaller than the argument, NSOrderedDescending if the array is larger than the argument, and NSOrderedSame if they are equal.
Since the NSComparisonResult define as:
enum {
NSOrderedAscending = -1,
NSOrderedSame,
NSOrderedDescending
};
typedef NSInteger NSComparisonResult;
All you need is a descendingComparator, something like:
- (NSComparisonResult)compareNameInDesendingOrder:(AddressCard *)aCard {
return -[self.name compare:[aCard name]];
}
- (void)sortInDesendingOrder {
[book sortUsingSelector:#selector(compareNameInDesendingOrder:)];
}
hope it helps.

Program crush on [order setPrice:price] break point at #synthesize name

please help me to solve a simple problem.
I am a beginner in objective-c, and I am just switched to objective-c from java. I know java fair well, but not quite super deep into it.
I am building a iphone app. My app is quite simple.
The purpose of my iphone app is to take order with my iphone app in a restaurant.
Progress of My App:
My app only has couple viewPanels and buttons now :)
Here is my app sourcecode, firstview screenshot & secondview screenshot
Problem:
When i click on the Coffee button, my textField wont show up the coffee name & coffee price, which suppose to show up " coffee 1" .
and xcode will take me to the debugger from the iphone similator.(i think its crush at a line so the dubugger took me to the IBaction method and break at the line #synthesize name; It compiles with no error. please help trouble shoot why xcode take me to debugger when i press the coffee button.
SCREEN SHOWS UP RIGHT AFTER PRESS THE COFFEE BUTTON
here is the action code of the coffee button
- (IBAction)Coffee:(id)sender {
int price = 1;
NSString *name = #"coffee";
Storage *order = [[Storage alloc] init];
[order setName:name]; // i assume the program crush at here when it look into setName method.
[order setPrice:price];
[orders addOrders:order];
// Sanity check: // the program not even hit this line yet before crush;
NSLog(#"There are now %d orders in the array.", [orders.getOrders count]);
for (Storage *obj in orders.getOrders){
[check setText:[NSString stringWithFormat:#"%#",[obj description]]]; // check is the TextField instant varible. and the description method is from storage.m to print out the name and price.
}
}
The codes below are my storage classes that store all items that my customer orders.
it is a 2 dimensional array, and My Storages class is a wrapper class of Storage class.
the array format looks like this:
arrayindex1-> name, price
arrayindex2-> name, price
arrayindex3-> name, price
arrayindex4-> name, price
Storage.h
#import <Foundation/Foundation.h>
#interface Storage : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, assign) NSInteger price;
#end
Storage.m
#import "Storage.h"
#implementation Storage
#synthesize name; // program crush and goes to here.
#synthesize price;
- (NSString *)description {
// example: "coffee 1"
return [NSString stringWithFormat:#"%# %d", self.name, self.price];
}
#end
Storages.h
#import <Foundation/Foundation.h>
#import "Storage.h"
#interface Storages : NSObject
#property (nonatomic,strong, readwrite) NSMutableArray *orders;
-(void) addOrders:(Storage *)anOrder;
-(NSMutableArray *) getOrders;
#end
Storages.m
#import "Storages.h"
#implementation Storages
#synthesize orders;
- (id)init {
self = [super init];
if (self) {
orders = [[NSMutableArray alloc] init];
}
return self;
}
-(void) addOrders:(Storage *)anOrder{
[orders addObject: anOrder];
}
-(NSMutableArray *) getOrders{
return orders;
}
#end
There are a couple of problems here.
1) Don't use a pointer for the price property. Generally, unless you're doing something unusual, your properties that are objects will be pointers and your properties that are primitives (NSInteger, BOOL, float, etc) will not be pointers.
2) You will want to make sure that the orders NSMutableArray is initialized with the Storages object, otherwise orders will remain nil and whenever you try to add objects to it, nothing will happen. To initialize the NSMutableArray, do this in your init method as shown below. You can also check that the object is actually getting into a valid mutable array this by putting a simple NSLog statement in the for (Storage *obj in orders.getOrders) { ... } loop and making sure you get at least one iteration through the loop. If orders.getOrders is nil, the work block of the for loop will never get run.
3) It sounds like you need to override (and may have already overridden) the -[NSObject description]method for your Storage object. My guess is you have a mismatch in this method with the -[NSString stringWithFormat:...] format string. For example, you might be using %d or %# in the format string for the NSInteger *. Something like that could definitely cause a crash (which is what I think you mean by "Xcode taking you to the debugger"). For NSIntegers you need to use %d or %i. And as myself and others have mentioned, you want NSInteger here and not NSInteger * and you should change your property declaration.
4) Based on what you have here, I don't think you need the order property in the Storages class at all.
5) Make sure you haven't overlooked the possibility of forgetting to hook up the IBOutlet in Interface Builder to the check textField. A good test for this, besides just confirming it's connected in Interface Builder, would be a reality check test like [check setText:#"This is a test."];
6) Keep in mind that once this works, your for loop is going to execute very quickly, and you'll immediately see only the description for the last object in the orders array. But that doesn't seem to be what your question is about.
I'd suggest you make the following changes:
Storage.h
#import <Foundation/Foundation.h>
#interface Storage : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, assign) NSInteger price;
#end
Storage.m
#import "Storage.h"
#implementation Storage
#synthesize name;
#synthesize price;
- (NSString *)description {
// example: "coffee 1"
return [NSString stringWithFormat:#"%# %d", self.name, self.price];
}
#end
Your IBAction method
- (IBAction)Coffee:(id)sender {
int price = 1;
NSString *name = #"coffee";
Storage *order = [[Storage alloc] init];
[order setName:name];
[order setPrice:price];
[orders addOrders:order];
// Sanity check:
NSLog(#"There are now %d orders in the array.", [orders.getOrders count]);
for (Storage *obj in orders.getOrders){
[check setText:[NSString stringWithFormat:#"%#",[obj description]]]; // check is the TextField instant varible. and the description method is from storage.m to print out the name and price.
}
}
Storages.h
#import <Foundation/Foundation.h>
#import "Storage.h"
#interface Storages : NSObject
#property (nonatomic,strong, readwrite) NSMutableArray *orders;
-(void) addOrders:(Storage *)anOrder;
-(NSMutableArray *) getOrders;
#end
Storages.m
#import "Storages.h"
#implementation Storages
#synthesize orders;
- (id)init {
self = [super init];
if (self) {
orders = [[NSMutableArray alloc] init];
}
return self;
}
-(void) addOrders:(Storage *)anOrder{
[orders addObject: anOrder];
}
-(NSMutableArray *) getOrders{
return orders;
}
#end
What does description do in the following? (I cannot see any description object in Storage class):
[check setText:[NSString stringWithFormat:#"%#",[obj description]]];
I think if you want to print the name the do like:
[check setText:[NSString stringWithFormat:#"%#: %#",obj.name, obj.price]];
You have taken NSInteger pointer in Storages class which is not correct. NSInteger is basic data type and not a pointer. Remove that pointer and use NSInteger variable.
I hope this would resolve your problem.
You could use below code:
#interface Storage : NSObject
#property (nonatomic, retain)NSString *name;
#property (nonatomic, assign)NSInteger price;
You have two mistakes:
1- You declared price as an NSInteger and passed it as a reference. The correct is to pass it as an integer as it is and deal with it as an integer through the whole application.
2- You didn't initialize orders array in Storages class so it will be always nil and will not hold any added object.
You code may looks like:
In the button's IBAction pass the price directly
[order setPrice:price];
In the Storage class
- (NSString *)description {
return [NSString stringWithFormat: #"%# %d", name, price];
}
Add the following to the Storages class
-(id)init
{
if (self = [super init])
{
orders = [[NSMutableArray alloc] init];
}
return self;
}

Resources