This is a pretty simple concept, but I'm not getting the results I'm wanting. I have an NSMutableArray that is populated with NSArrays, I want to loop through that NSMutableArray and remove certain NSArrays based on a key-value pair. My results have many of the NSArrays that I should be removing and I think it has something to do with the count of the NSMutableArray and the int I declare in the For Loop.
Here is my code: (restArray is the NSMutableArray)
for (int i=0; i<restArray.count; i++) {
NSArray *array = restArray[i];
if ([[array valueForKey:#"restaurant_status"] isEqualToString:#"0"]) {
[restArray removeObjectAtIndex:i];
}
}
Does someone know what I am doing wrong here?
It is not recommended to modify an array on what are you currently iterating.
Lets create a tmp array, and reverse your logic.
NSMutableArray * tmpArray = [NSMutableArray array];
for (int i=0; i<restArray.count; i++) {
NSArray *array = restArray[i];
if (![[array valueForKey:#"restaurant_status"] isEqualToString:#"0"] {
[tmpArray addObject:array];
}
}
So at the end of the iteration, you should end up with tmpArray having the arrays you needed.
Use NSPredicate:
NSArray *testArray = #[#{#"restaurant_status" : #"1"}, #{#"restaurant_status" : #"0"}];
NSArray *result = [testArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(restaurant_status == %#)", #"1"]];
When you remove an element all the elements past it shift down by one, e.g. If you remove the element at index 3 then the element previously at index 4 moves to index 3.
Every iteration you increase the index by one.
Combine the above two and you see that when you remove an element your code skips examining the following element.
The simple solution is to reverse the order of the iteration:
for (int i = restArray.count - 1; i >= 0; i--)
and then your algorithm will work.
Addendum
You can safely ignore this addendum if your arrays contain < 2^32 elements and you use Clang or GCC (and most other C compilers).
It has been raised in the comments that this answer has a problem if the array has 0 elements in it. Well yes & no...
First note that the code in the question is technically incorrect: count returns an NSUInteger which on a 64-bit machine is a 64-bit unsigned integer, the loop variable i is declared as an int which is 32-bit signed. If the array has more than 2^31-1 elements in it then the loop is incorrect.
Most people don't worry too much about this for some reason ;-) But let's fix it:
for (NSInteger i = restArray.count - 1; i >= 0; i--)
Back to the problem with an empty array: in this case count returns unsigned 0, C standard arithmetic conversions convert the literal 1 to unsigned, the subtraction is done using modular arithmetic, and the result is unsigned 2^64-1.
Now that unsigned value is stored into the signed i. In C converting from signed to unsigned of the same type is defined to be a simple bit-copy. However converting from unsigned to signed is only defined if the value is within range, and implementation defined otherwise.
Now 2^64-1 is greater than the maximum signed integer, 2^32-1, so the result is implementation defined. In practice most compilers, including Clang and GCC, choose to use bit-copy, and the result is signed -1. With this the above code works fine, both the NSInteger and the int (if you've less than 2^32-1 elements in your array) versions.
What the comments raise is how to avoid this implementation-defined behaviour. If this concerns you the following will handle the empty array case correctly with ease:
for (NSUInteger i = restArray.count; i > 0; )
{
i--; // decrement the index
// loop body as before
}
If the array is empty the loop test, i > 0, will fail immediately. If the array is non-empty i, being initialised to the count, will start as one greater than the maximum index and the decrement in the loop will adjust it - effectively in the loop test i contains the number of elements left to process and in the loop body after the decrement contains the index of the next element to process.
Isn't C fun (and mathematically incorrect by definition)!
Related
while adding a integer to object at index of an array, I am getting an error of "arithmetic on pointer to interface id which is not a constant size for this architecture and platform", didn't know how to resolve it.
Please help.
my code is -
if (arrayTotalAmount.count>0) {
int sum = 0;
for (int i = 0; i<=arrayTotalAmount.count; i++) {
sum = (sum+[arrayTotalAmount objectAtIndex:i]);
}
In 4th line I am getting that error.
Thanks
Objective C array only accepts NSObject type. That means it is impossible to insert a primitive value into an NSArray. You are getting an error because objectAtIndex method returns a pointer which points to that NSObject, the arithmetic operations are still valid on pointers but the thing is that the size of a pointer as integer (32bit, 64bit) may differ on device. So one of the solution is typecasting the pointer sum+(int)[arrayTotalAmount objectAtIndex:i] which makes no sense in your case.
The solution you are looking for is probably sum+[[arrayTotalAmount objectAtIndex:i] intValue] or similar. Assuming that array contains NSNumber objects. If the object inside an array is not an NSNumber then your app will fail in runtime showing an error indicating that an object X does not have a method called intValue in which case you will need to figure how to convert object X to your int.
You just need to convert your array object to integer and then add it will work for you.
if (arrayTotalAmount.count>0) {
int sum = 0;
for (int i = 0; i<=arrayTotalAmount.count; i++) {
sum = (sum+[[arrayTotalAmount objectAtIndex:i] intValue]);
}
I have an NSArray filled with only NSStrings
I understand that to iterate over a NSArray of n elements, all I have to do is use for (NSString *element in arrayOfElements). However, I was wondering if there is specific function that will perform a comparison between every string element in the array with each other. For example, if I have the array:
[#"apple", #"banana", #"peach", #"kiwi"],
how would I do the comparison so apple is compared to banana, peach and then kiwi; and then banana is against peach and wiki, and finally peach is against kiwi?
Try using nested for loops, ex:
for (int i = 0 ; i < array.count ; i ++) {
for (int j = i + 1 ; j < array.count ; j ++) {
// compare array[i] to array [j]
}
}
Edit: And although wottle's suggestion would work, I'd recommend mine in this case, since it won't waste iterations going over the same comparisons multiple times. What I've done in this algorithm by setting j = i + 1 is compare each element in the array only to the ones after it.
Given "the array will not have any duplicates, and every NSString will be unique" this sounds like a great case for using NSSet classes instead of NSArray. NSMutableSet provides:
minusSet:
Removes each object in another given set from the receiving
set, if present.
and
intersectSet:
Removes from the receiving set each object that isn’t a
member of another given set.
I'm not sure which operation you're looking for but it sounds like one of those should cover your exact use case.
What you're trying to do is a bit beyond what custom comparators were meant to do. Typically when you have a list and you want to run a custom comparator, you're doing it to sort the list. You seem to want to do some specific action when certain items in the list compare to others, and for that, I think a loop within a loop is your best bet. It won't be very good performance, so hopefully you are not expecting a large array:
-(void) compareArrayToSelf
{
NSArray *array=#[#"apple", #"bananna", #"peach", #"kiwi"];
for( NSString *value1 in array)
{
for( NSString *value2 in array)
{
if( ![value1 isEqualToString:value2] && [self compareArrayValue:value1 toOtherValue:value2])
{
//Do something with either value1 or value2
}
}
}
}
I am making a program where I need to loop through an array with a list of letters. I want the letters to be shown on their specific label. I have therefore created an outlet of each (about 38) and given them the name "alif01", "alif02", etc.
for (int i = 0; i < [arabicLetters count]; i++) {
int num = i;
NSString *letterString = [arabicLetters objectAtIndex:i];
NSLog(#"alif0%d is %#", num, letterString);
alif0**[i]**.text = arabicLetters[i];
}
is it possible to use the index [i] instead of writing it all manually?
You should not have 38 IBOutlet properties for this. You should have an array (possibly an IBOutletCollection) so that you can loop over the array / index into the array.
While technically you can create a key name and use KVC valueForKey: (appending strings / string format), the array approach is a much better solution.
Indeed, as you already have a loop, you would be better served by creating the labels in the loop directly, then you know you have the correct number. This is particularly beneficial later, when you change the contents of arabicLetters (though that sounds like it isn't a concern in this particular case).
Try with below code:
for (int i = 0; i < [arabicLetters count]; i++) {
NSString *letterString = [arabicLetters objectAtIndex:i];
NSString *propertyName = [NSString stringWithFormat:#"alif0%d.text",i];
[self setValue:letterString forKeyPath:propertyName];
}
I found this answer:
https://stackoverflow.com/a/5163334/1364174
Which presents how for in loop is implemented.
NSFastEnumerationState __enumState = {0};
id __objects[MAX_STACKBUFF_SIZE];
NSUInteger __count;
while ((__count = [myArray countByEnumeratingWithState:&__enumState objects:__objects count:MAX_STACKBUFF_SIZE]) > 0) {
for (NSUInteger i = 0; i < __count; i++) {
id obj = __objects[i];
[obj doSomething];
}
}
The problem is that, I found it wrong.
First of all, when you have Automatic Reference Counting (ARC) turned on, you got an error
Sending '__strong id *' to parameter of type '__unsafe_unretained_id*' changes retain/release properties of pointer
But even when I turn ARC off I found out that I __object array seems to behave strangely :
This is actual Code (I assumed MAX_STACKBUFF_SIZE to be 40):
#autoreleasepool {
NSArray *myArray = #[#"a", #"b", #"c", #"d", #"e", #"f", #"g"];
int MAX_STACKBUFF_SIZE = 40;
NSFastEnumerationState __enumState = {0};
id __objects[MAX_STACKBUFF_SIZE];
NSUInteger __count;
while ((__count = [myArray countByEnumeratingWithState:&__enumState objects:__objects count:MAX_STACKBUFF_SIZE]) > 0) {
for (NSUInteger i = 0; i < __count; i++) {
id obj = __objects[i];
__enumState.itemsPtr
NSLog(#" Object from __objects ! %#", obj); // on screenshot different message
}
}
}
return 0;
I got EXC_BAD_ACESS when I try to get the contents of the __object array.
I also found out that when you try to iterate through __enumState.itemsPtr it actually works.
Could you explain me what is going on here ? Why my __objects seems to be "shrunken down". And why it doesn't contains desired object? And why is that error when ARC is turned on.
Thank you very very much in advance for your time and effort! (I provided screenshot for better understanding what causes an error)
First of all, strong pointers cannot be used in C-structures, as explained in the "Transitioning to ARC Release Notes", therefore the objects array has be be declared
as
__unsafe_unretained id __objects[MAX_STACKBUFF_SIZE];
if you compile with ARC.
Now it is not obvious (to me) from the NSFastEnumeration documentation, but it is
explained in Cocoa With Love:Implementing countByEnumeratingWithState:objects:count:
that the implementation need not fill the supplied objects array, but can just set
__enumState.itemsPtr to an existing array (e.g. some internal storage). In that case, the contents of the
__objects array is undefined, which causes the crash.
Replacing
id obj = __objects[i];
by
id obj = __enumState.itemsPtr[i];
gives the expected result, which is what you observed.
Another reference can be found in the "FastEnumerationSample" sample code:
You have two choices when implementing this method:
1) Use the stack
based array provided by stackbuf. If you do this, then you must
respect the value of 'len'.
2) Return your own array of objects. If
you do this, return the full length of the array returned until you
run out of objects, then return 0. For example, a linked-array
implementation may return each array in order until you iterate
through all arrays.
In either case, state->itemsPtr MUST be a valid
array (non-nil). ...
Even though , I execute the below code again & again I get the same Output. But , I think it should not:
int ObjectCount =500;
NSMutableArray *mut_arr = [[NSMutableArray alloc]initWithCapacity:0];
for (int i = 0; i<ObjectCount ; i++)
{
[mut_arr addObject:[NSNumber numberWithInt: rand()%ObjectCount]];
}
NSSet* uniqueSet = [NSSet setWithArray:mut_arr];
NSLog(#"Array of %d objects generates %d Unique Objects",[mut_arr count],[uniqueSet count]);
The output is as follows:
Array of 500 objects generates 317 Unique Objects
Here, Since the array contains random numbers the unique set count should be same again & again for same ObjectCount.
You're not actually generating unique NSNumber objects; some of them are equal.
A NSArray can contain multiple objects that are equal. A NSSet can not. This is why the set created from your array has less objects.
The reason why you're always getting 317 objects is that you're using rand() without seeding: Why do I always get the same sequence of random numbers with rand()?
Consider using arc4random() instead, which is seeding automatically.
Use like this
[mut_arr addObject:[NSNumber numberWithInt:(arc4random() % (ObjectCount-1) + 1)]];