Slice NSArray from end of array - objective-c

What is the best way to "slice" an NSArray from the end, rather than the beginning, of the array (for example, finding the subarray containing the last few elements of a NSArray of unknown length)? In Python, you can use negative indices to accomplish this, e.g.:
new_list = old_list[-5:-3]
What's the most natural way to do this in Objective-C?

There's nothing to match Python's nice syntax for this, but you could do:
NSUInteger count = [myArray count];
NSArray * slice = [myArray subarrayWithRange:(NSRange){count-n, n}];
You could also write up a category for NSArray, something like:
#interface NSArray (jrdioko_slice)
- (NSArray *) jrdioko_sliceFrom:(NSInteger)start to:(NSInteger)stop;
#end
If you want to go this route, the Python source will certainly repay study. A list object creates a slice object when a slice operation is performed. The relevant method on a slice object is PySlice_GetIndicesEx. You'll just have to be careful turning those indexes into an NSRange. As the comment in that function warns "this is harder to get right than you might think". (I'll try to take a crack at this later.)
UPDATE: Here we have a slice category on NSArray. The index calculation logic is pretty much straight out of the Python code that I linked to above.* It's actually a lot easier than I thought at first if you don't have to worry about the stride part of a Python slice. I've run this through a few tests and it seems to work the same as the Python version.
#interface NSArray (WSS_Slice)
- (NSArray *)WSS_arrayBySlicingFrom:(NSInteger)start to:(NSInteger)stop;
#end
// Python allows skipping any of the indexes of a slice and supplies default
// values. Skipping an argument to a method is not possible, so (ab)use
// NSNotFound as "not specified" index value. The other way to do this would
// be with varargs, which might be even handier if one decided to implement
// the stride functionality.
enum {
WSS_SliceNoIndex = NSNotFound
};
#implementation NSArray (WSS_Slice)
- (NSArray *)WSS_arrayBySlicingFrom:(NSInteger)start to:(NSInteger)stop {
// There's an important caveat here: specifying the parameters as
// NSInteger allows negative indexes, but limits the method's
// (theoretical) use: the maximum size of an NSArray is NSUIntegerMax,
// which is quite a bit larger than NSIntegerMax.
NSUInteger count = [self count];
// Due to this caveat, bail if the array is too big.
if( count >= NSIntegerMax ) return nil;
// Define default start and stop
NSInteger defaultStart = 0;
NSInteger defaultStop = count;
// Set start to default if not specified
if( start == WSS_SliceNoIndex ){
start = defaultStart;
}
else {
// If start is negative, change it to the correct positive index.
if( start < 0 ) start += count;
// Correct for out-of-bounds index:
// If it's _still_ negative, set it to 0
if( start < 0 ) start = 0;
// If it's past the end, set it to just include the last item
if( start > count ) start = count;
}
// Perform all the same calculations on stop
if( stop == WSS_SliceNoIndex ){
stop = defaultStop;
}
else {
if( stop < 0 ) stop += count;
if( stop < 0 ) stop = 0;
if( stop > count ) stop = count;
}
// Calculate slice length with corrected indexes
NSInteger sliceLength = stop - start;
// If no slice, return a new empty array
if( sliceLength <= 0 ){
return [NSArray array];
}
else {
return [self subarrayWithRange:(NSRange){start, sliceLength}];
}
}
#end
*Therefore I think I need to include a link to the Python License and also note that this may still be “Copyright © 2001-2010 Python Software Foundation; All Rights Reserved”, because although this looks to me like a separately-copyrightable derivative work, I ain't a lawyer.

Related

NSMutableArray was mutated while being enumerated

I have an array in an old objective-C app that I am using to learn more "complicated" coding. It is back from the old days of OS X and was very much broken. I have gotten it to work (mostly)! However, the app has an NSMutableArray of images, 7 in total. I use a random number generator to insert the images on the screen, some code to allow them to fall, and then, using screen bounds, when they reach "0" on the Y axis they are removed from the array.
I initially just had:
if( currentFrame.origin.y+currentFrame.size.height <= 0 )
{
[flakesArray removeObject:myItem];
I have read when removing objects from an array it is best practice to iterate in reverse...so I have this bit of code:
for (NSInteger i = myArray.count - 1; i >= 0; i--)
{ //added for for statement
if( currentFrame.origin.y+currentFrame.size.height <= 0 )
{
[myArray removeObjectAtIndex:i];
}
Sadly both methods result in the same mutated while enumerated error. Am I missing something obvious?
If I add an NSLog statement I can get, I think, the index of the item that needs to be removed:
NSLog (#"Shazam! %ld", (long)i);
2017-01-07 14:39:42.086667 MyApp[45995:7500033] Shazam! 2
I have looked through a lot and tried several different methods including this one, which looks to be the most popular with the same error.
Thank you in advance! I will happily provide any additional information!
Adding more:
Sorry guys I am not explicitly calling NSFastEnumeration but I have this:
- (void) drawRectCocoa:(NSRect)rect
{
NSEnumerator* flakesEnum = [flakesArray objectEnumerator];
then
for( i = 0; i < numberToCreate; i++ )
{
[self newObject:self];
}
while( oneFlake = [flakesEnum nextObject] )
It is here where:
if( currentFrame.origin.y+currentFrame.size.height <= 0 )
{
NSLog (#"Shazam! %i", oneFlake);
[flakesArray removeObject:oneFlake];
}
Thank you all. I am learning a lot from this discussion!
There are two ways to go: (1) collect the objects to remove then remove them with removeObjectsInArray:.
NSMutableArray *removeThese = [NSMutableArray array];
for (id item in myArray) {
if (/* item satisfies some condition for removal */) {
[removeThese addObject:item];
}
}
// the following (and any other method that mutates the array) must be done
// *outside of* the loop that enumerates the array
[myArray removeObjectsInArray:removeThese];
Alternatively, reverseObjectEnumeration is tolerant of removes during iteration...
for (id item in [myArray reverseObjectEnumerator]) {
if (/* item satisfies some condition for removal */) {
[myArray removeObject: item];
}
}
As per the error, you may not mutate any NSMutableArray (or any NSMutable... collection) while it is being enumerated as part of any fast enumeration loop (for (... in ...) { ... }).
#danh's answer works as well, but involves allocating a new array of elements. There are two simpler and more efficient ways to filter an array:
[array filterUsingPredicate:[NSPredicate predicateWithBlock:^(id element, NSDictionary<NSString *,id> *bindings) {
// if element should stay, return YES; if it should be removed, return NO
}];
or
NSMutableIndexSet *indicesToRemove = [NSMutableIndexSet new];
for (NSUInteger i = 0; i < array.count; i += 1) {
if (/* array[i] should be removed */) {
[indicesToRemove addIndex:i];
}
}
[array removeObjectsAtIndexes:indicesToRemove];
filterUsingPredicate: will likely be slightly faster (since it uses fast enumeration itself), but depending on the specific application, removeObjectsAtIndexes: may be more flexible.
No matter what, if you're using your array inside a fast enumeration loop, you will have to perform the modification outside of the loop. You can use filterUsingPredicate: to replace the loop altogether, or you can keep the loop and keep track of the indices of the elements you want to remove for later.

What's the fastest way to remove duplicates from an array in Objective-C

Prepping for an interview. I am trying to practice by solving the following problem: Given an input array of NSNumbers where some of the numbers are duplicated, how can you create another array that only has the unique values in the original array.
I see 2 approaches:
Brute-force: Loop through each element in the array, while at a element compare it against the set of numbers in the unique list, if there is a match, don't store it, else add it to the unique list. O(n^2) worst case time?
Hash-table based approach: Have a hash-table of length N. Each element of the has-table is NSSet. Every number is mapped to 0,...N-1 using a hashing function. If it is exists in the NSSet corresponding to the "mapped-index", it is not added to "unique array". if not, it is added to set and unique array.
Is this O(N) complexity?
I looked two ways to implement approach 2
A. NSMutableArray with size of N all initialized to [NSNull null] objects at start.
B. NSMutableDictionary where key = hashed mapping integer
Code for each approach is below.
I am noticing that
i. Running time of 2A (array approach) is half of that of 2B (Mutabledictionary approach) for the input array of length 403 shown below(0.055ms vs .12ms).
ii. Running time of 1 is ~ 5 times worse 0.25ms. If there are not any duplicates, this discrepancy is even worse.
My Qs are:
Is there a better algorithm than 2?
Is there a better implementation of algorithm 2?
Why is dictionary approach slower? How can I answer this for myself using Instruments profiling. I.e how can I know exact time taken by each step using Instruments?
Code
Hashcode function
#define NUM_BUCKETS 127
#define RANDOMIZER 11
#define NUM_ITER 40000
int hashcode(int value)
{
int retVal = (value*RANDOMIZER)%NUM_BUCKETS ;
if(retVal<0)
{
retVal+=NUM_BUCKETS ;
}
return retVal ;
}
1. Brute-Force Approach
NSMutableArray *smooshedArr=[[NSMutableArray alloc] init] ;
double startTime ;
startTime=CFAbsoluteTimeGetCurrent() ;
for(int iter=0;iter<=NUM_ITER;iter++)
{
[smooshedArr removeAllObjects] ;
[smooshedArr addObject:ints[0]] ;
int i,j ;
for(i=1;i<[ints count];i++)
{
for(j=0;j<[smooshedArr count];j++)
{
if([ints[i] intValue] == [smooshedArr[j] intValue])
{
break ;
}
}
if(j==[smooshedArr count])
{
[smooshedArr addObject:ints[i]] ;
}
}
}
NSLog(#"Bruteforce took %.3fms to remove duplicates from array of length %lu",(CFAbsoluteTimeGetCurrent()-startTime)*1000/NUM_ITER,(unsigned long)[ints count]) ;
NSLog(#"Smooshed arary is %#",smooshedArr) ;
2A. Array based hash table
NSMutableArray *hashTable = [[NSMutableArray alloc] init] ;
startTime=CFAbsoluteTimeGetCurrent() ;
for(int iter=0;iter<=NUM_ITER;iter++)
{
[smooshedArr removeAllObjects];
for (NSInteger i = 0; i < NUM_BUCKETS; ++i)
{
[hashTable addObject:[NSNull null]];
}
[smooshedArr addObject:ints[0]] ;
int indexToInsert = hashcode([ints[0] intValue]) ;
hashTable[indexToInsert]=[[NSMutableSet alloc] init] ;
[hashTable[indexToInsert] addObject:ints[0]] ;
int i ;
for(i=1;i<[ints count];i++)
{
//Find hascode of element i
//If the list at index = hashcode in hashCodeArary is empty, then create a NSMutableSet, set toInsert = True
//If not empty, check if the element exists in the set. If yes, setToInsert=False. If no, setToInsert=True
int indexToInsert = hashcode([ints[i] intValue]) ;
BOOL toInsert=false ;
if(hashTable[indexToInsert] == [NSNull null])
{
hashTable[indexToInsert]=[[NSMutableSet alloc] init] ;
toInsert=true ;
}
else
{
if(![hashTable[indexToInsert] containsObject:ints[i]])
toInsert=true ;
}
if(toInsert)
{
[hashTable[indexToInsert] addObject:ints[i]] ;
[smooshedArr addObject:ints[i]] ;
}
}
}
NSLog(#"MutableArray (no cheat) took %.3fms to remove duplicates from array of length %lu",(CFAbsoluteTimeGetCurrent()-startTime)*1000/NUM_ITER,(unsigned long)[ints count]) ;
2B. Dictionary based hash table
NSMutableDictionary *hashDict = [[NSMutableDictionary alloc] init] ;
//NSLog(#"Start of hashcode approach %.6f", CFAbsoluteTimeGetCurrent()) ;
startTime=CFAbsoluteTimeGetCurrent() ;
for(int iter=0;iter<=NUM_ITER;iter++)
{
//if(iter <4) NSLog(#"iter start: %.6f", CFAbsoluteTimeGetCurrent()) ;
//if(iter <4) NSLog(#"init start: %.6f", CFAbsoluteTimeGetCurrent()) ;
[smooshedArr removeAllObjects];
[hashDict removeAllObjects] ;
//if (iter<4) NSLog(#"init end: %.6f", CFAbsoluteTimeGetCurrent()) ;
[smooshedArr addObject:ints[0]] ;
int indexToInsert = hashcode([ints[0] intValue]) ;
hashDict[#(indexToInsert)]=[[NSMutableSet alloc] init] ;
[hashDict[#(indexToInsert)] addObject:ints[0]] ;
int i ;
for(i=1;i<[ints count];i++)
{
//Find hascode of element i
//If the list at index = hashcode in hashCodeArary is empty, then create a NSMutableSet, set toInsert = True
//If not empty, check if the element exists in the set. If yes, setToInsert=False. If no, setToInsert=True
int indexToInsert = hashcode([ints[i] intValue]) ;
BOOL toInsert=false ;
if(hashDict[#(indexToInsert)] == nil)
{
hashDict[#(indexToInsert)]=[[NSMutableSet alloc] init] ;
toInsert=true ;
}
else
{
if(![hashDict[#(indexToInsert)] containsObject:ints[i]])
toInsert=true ;
}
if(toInsert)
{
[hashDict[#(indexToInsert)] addObject:ints[i]] ;
[smooshedArr addObject:ints[i]] ;
}
}
}
NSLog(#"Dictionary approach: %.3fms to remove duplicates from array of length %lu",(CFAbsoluteTimeGetCurrent()-startTime)*1000/NUM_ITER,(unsigned long)[ints count]) ;
Input tested ON, 430 elements with some dups and averaged over 40000 iterations
NSArray *ints = #[#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(2727272),#(112),#(3),#(4),#(1),#(612211),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(7272),#(1232),#(3),#(4),#(1),#(60),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(2727272),#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(2727272),#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(72),#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(13272),#(2),#(3),#(4),#(18),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(972),#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(3272),#(2),#(3),#(4),#(1),#(69),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(1272),#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(2272),#(2),#(3),#(4),#(1),#(6),#(91),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(7272),#(2),#(3),#(4),#(12),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(111),#(27272),#(2),#(321),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(4545411),#(12341),#(34210),#(123),#(1234),#(1111),#(727272),#(11187),#(9086),#(876543),#(74532),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(13272),#(2),#(3),#(4),#(18),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(658),#(45454),#(12934),#(38421),#(1243),#(12345),#(1112),#(72),#(52),#(3),#(498),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(650),#(45454),#(1234),#(3421),#(123),#(1234),#(111),#(27272),#(2),#(321),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(4545411),#(12341),#(34210),#(123),#(1234),#(1111),#(727272),#(11187),#(9086),#(876543),#(74532),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(13272),#(2),#(3),#(4),#(18),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(658),#(45454),#(12934),#(38421),#(1243),#(19992345),#(119875412),#(72),#(52),#(3),#(498),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(450454),#(46908764642),#(6753435),#(45498754),#(100234),#(65)] ;
If you are preparing for an interview, I would advise you to use the framework classes that are already implemented. Don't reimplement the wheel. Try to solve the problem from top to bottom. Don't think about details (hash functions), think about the algorithm structure:
In pseudocode:
for number in input {
if number appears for the first time {
add number to output
}
}
The only problem we have is how to implement the number appears for the first time. That's the only point that has some performance implications here.
In Objective-C we can use NSSet which is a class created exactly for this problem.
NSArray *input = #[... array of numbers];
NSMutableSet *foundNumbers = [NSMutableSet set];
NSMutableArray *output = [NSMutableArray array];
for (NSNumber *number in input) {
if (![foundNumbers containsObject:number])) {
[foundNumbers addObject:number];
[output addObject:number];
}
}
NSLog(#"Output: %#", output);
You need only one pass of the input array. The only way you could improve performance is using a different structure than NSSet, however NSSet is already highly optimized and it's unlikely you will find a better option.
If you want to think out of the box and the numbers in your input are limited to a small enough range (e.g. 0...65000), you can create a BOOL array with 65000 items, all initialized to NO and use that as a fast set implementation.
However, that will take a lot of memory and it won't pay off unless the input array is very long.
Definitely don't implement your own hash tables, NSDictionary is already a hash table. What you are doing in your second implementation is just a very obfuscated reimplementation of NSDictionary. Buckets work only when you can keep them as a simple array. Once you add hash function to it, you are losing the performance gain.
Also note that the overall quality of code is very important for interviews. Don't use #define to declare a constant. Keep a good coding style (I would strongly advice to use spaces around operators). Use iterators instead of for(;;) Try to name your variables better than hashDict (name your variables for the data they contain).
Now a little secret, there is also a class NSOrderedSet which combines NSArray and NSSet into one object and can solve your problem even easier:
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:ints];
NSLog(#"Output: %#", orderedSet);
Actually using NSOrderedSet is not even necessary – one can get away with just NSSet:
NSSet *set = [NSSet setWithArray:ints];
If you need an array as output, Key-Value Coding is here to help:
NSArray *array = [ints valueForKeyPath:#"#distinctUnionOfObjects.self"];
If you don't want to use extra space(hash), if sequence of numbers in array does not matter but still you don't want to be as slow as brute force then you can sort the array and after that remove duplicates in one pass. Time complexity nlog(n) + n

Sudoku Backtracking Algorithm Failure

I'm trying to generate a sudoku board, and although I can generate a solution, I now need to remove squares that a user can then fill in. In order to do this, I'd like to use backtracking to check each time that I remove a square, the board is
1. still solvable and 2. has only one solution.
The Problem
When I test my backtracking algorithm on this board (where the zeroes are empty squares), it returns this solution. Obviously I would prefer not to end up with several 9s in the first row, for example.
My Code
- (BOOL) solveArray: (NSArray*) numArray {
NSMutableArray* board = [numArray mutableCopy];
for (int i=0; i<9; i++) { //make all of the arrays mutable as well (since it's 2D)
[board replaceObjectAtIndex:i withObject:[board[i] mutableCopy]];
}
//if everything is filled in, it's done
if (![self findUnassignedLocation:board]) {
NSLog(#"\n%#", [SudokuBoard sudokuBoardWithArray:board]);
return TRUE;
}
NSArray* poss = [[SudokuBoard sudokuBoardWithArray:board] possibleNumbersForRow:self.arow Col:self.acol];
//if there are no options for a location, this didn't work.
if ([poss count] == 0) {
return FALSE;
}
//otherwise, continue recursively until we find a solution
else {
for (int i=0; i<[poss count]; i++) {
//make a tentative assignment
board[self.arow][self.acol] = poss[i];
//return, if successful, done
if ([self solveArray:board]) {
return TRUE;
}
//if that didn't work, unmake it and retry with other options
board[self.arow][self.acol] = [NSNumber numberWithInt:0];
}
}
return FALSE;
}
Any thoughts on where I might be going wrong?
Each level of recursion needs its own row and column variables. That is, row and column should be inputs to solveArray and outputs of findUnassignedLocation instead of being member variables. As it is, when there is backtracking the row and column of the failed level get reused by the caller.
Given that some assigned locations are being overwritten, maybe findUnassignedLocation also contains an error.
Given that the result is invalid, maybe possibleNumbersForRow also contains an error.

How to perform binary search on NSArray?

What is the simplest way to do a binary search on an (already) sorted NSArray?
Some potential ways I have spotted so far include:
The use of CFArrayBSearchValues (mentioned here) - would this work on an NSArray?
The method indexOfObject:inSortedRange:options:usingComparator: of NSArray assumes the array is sorted and takes an opts param of type NSBinarySearchingOptions - does this mean it performs a binary search? The docs just say:
Returns the index, within a specified range, of an object compared with elements in the array using a given NSComparator block.
Write my own binary search method (something along the lines of this).
I should add that I am programming for iOS 4.3+
Thanks in advance.
The second option is definitely the simplest. Ole Begemann has a blog entry on how to use the NSArray's indexOfObject:inSortedRange:options:usingComparator: method:
NSArray *sortedArray = ... // must be sorted
id searchObject = ...
NSRange searchRange = NSMakeRange(0, [sortedArray count]);
NSUInteger findIndex = [sortedArray indexOfObject:searchObject
inSortedRange:searchRange
options:NSBinarySearchingFirstEqual
usingComparator:^(id obj1, id obj2)
{
return [obj1 compare:obj2];
}];
See NSArray Binary Search
1 and 2 will both work. #2 is probably easier; it certainly doesn't make sense for that method to do anything other than a binary search (if the range is above a certain size, say). You could verify on a large array that it only does a small number of comparisons.
I'm surprised that nobody mentioned the use of NSSet, which [when it contains objects with a decent hash, such as most Foundation data types] performs constant time lookups. Instead of adding your objects to an array, add then to a set instead (or add them to both if you need to retain a sorted order for other purposes [or alternatively on iOS 5.0 or Mac OS X 10.7 there is NSOrderedSet]).
To determine whether an object exists in a set:
NSSet *mySet = [NSSet setWithArray:myArray]; // try to do this step only once
if ([mySet containsObject:someObject])
{
// do something
}
Alternatively:
NSSet *mySet = [NSSet setWithArray:myArray]; // try and do this step only once
id obj = [mySet member:someObject];
// obj is now set to nil if the object doesn't exist or it is
// set to an object that "isEqual:" to someObject (which could be
// someObject itself).
It is important to know that you will lose any performance benefit if you convert the array to a set each time you do a lookup, ideally you will be using a preconstructed set containing the objects you want to test.
//Method to pass array and number we are searching for.
- (void)binarySearch:(NSArray *)array numberToEnter:(NSNumber *)key{
NSUInteger minIndex = 0;
NSUInteger maxIndex = array.count-1;
NSUInteger midIndex = array.count/2;
NSNumber *minIndexValue = array[minIndex];
NSNumber *midIndexValue = array[midIndex];
NSNumber *maxIndexValue = array[maxIndex];
//Check to make sure array is within bounds
if (key > maxIndexValue || key < minIndexValue) {
NSLog(#"Key is not within Range");
return;
}
NSLog(#"Mid indexValue is %#", midIndexValue);
//If key is less than the middleIndexValue then sliceUpArray and recursively call method again
if (key < midIndexValue){
NSArray *slicedArray = [array subarrayWithRange:NSMakeRange(minIndex, array.count/2)];
NSLog(#"Sliced array is %#", slicedArray);
[self binarySearch:slicedArray numberToEnter:key];
//If key is greater than the middleIndexValue then sliceUpArray and recursively call method again
} else if (key > midIndexValue) {
NSArray *slicedArray = [array subarrayWithRange:NSMakeRange(midIndex+1, array.count/2)];
NSLog(#"Sliced array is %#", slicedArray);
[self binarySearch:slicedArray numberToEnter:key];
} else {
//Else number was found
NSLog(#"Number found");
}
}
//Call Method
#interface ViewController ()
#property(nonatomic)NSArray *searchArray;
#end
- (void)viewDidLoad {
[super viewDidLoad];
//Initialize the array with 10 values
self.searchArray = #[#1,#2,#3,#4,#5,#6,#7,#8,#9,#10];
//Call Method and search for any number
[self binarySearch:self.searchArray numberToEnter:#5];
// Do any additional setup after loading the view, typically from a nib.
}
CFArrayBSearchValues should work—NSArray * is toll-free bridged with CFArrayRef.

Fast Enumeration Vs NSEnumerator in Objective-C

I have seen this over and over, why exactly is it faster to use fast enumeration in loops rather than an NSEnumerator using nextObject:.
NSEnumerator is the old way to enumerate over collections. It involves creating an object to represent the enumeration, then calling a method on it for every single iteration. While this was perfectly serviceable for many years, it's not terribly efficient, as it involves at least one message send for every iteration of the loop. NSFastEnumeration is the more modern approach, which leverages native language support to provide a much more efficient enumeration. The way it works under the hood is it creates a struct that represents the current enumeration state and repeatedly calls -countByEnumeratingWithState:objects:count: on the collection. This method returns a C array of objects in the objects out-param as well as a counter in the count out-param. This allows the caller to then iterate over the C array. In essence, this means one message call per chunk of objects, which, depending on the collection, could be as efficient as a single message call to get all objects.
If you have a bit of code that looks like
for (id obj in myArray) {
[obj doSomething];
}
This gets translated by the compiler into something roughly equivalent to
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 actual variables used are hidden, and the maximum size of the object buffer is also implementation-dependent, but the basic idea is there. It translates iteration over an obj-c collection into iteration over a C array.
GCC 8.9.4 Fast enumeration
protocol
GNUstep libs/base/trunk/Source/NSEnumerator.m countByEnumeratingWithState:objects:count:
It is not same as Apple's implementation but it is helpful to understand.
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state
objects: (id*)stackbuf
count: (NSUInteger)len
{
IMP nextObject = [self methodForSelector: #selector(nextObject)];
int i;
state->itemsPtr = stackbuf;
state->mutationsPtr = (unsigned long*)self;
for (i = 0; i < len; i++)
{
id next = nextObject(self, #selector(nextObject));
if (nil == next)
{
return i;
}
*(stackbuf+i) = next;
}
return len;
}
NSArray *array = something;
array = { {1,2}, {2,3}, {3,4} }
that means array is an array of array. so how can you access all the arrays and their values.
we can use for loop like this
for (int i = 0; i < array.count; i++)
{
NSArray x = [array objectAtIndex:i];
}
or a fast enum works like this
for(NSArray array2 in array)
{
// do what ever you want with this new array2.
}
this is a sample example.
PS. I forgot how the array looks in console.