Objective-C Split an array into two separate arrays based on even/odd indexes - objective-c

I have an array, NSMutableArray *stringArray that looks like this
stringArray =
[0]String1
[1]String2
[2]String3
[3]String4
[4]String5
[5]String6
How would I go about splitting this array into two arrays based on even/odd indexes?
Example:
NSMutableArray *oddArray = ([1], [3], [5]);
NSMutableArray *evenArray = ([0], [2], [4]);
Thanks in advance!

create two mutable arrays, use enumerateObjectsWithBlock: on the source array and check idx % 2 to put it into first or second array
Using the ternary operator:
NSArray *array = #[#1,#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12];
NSMutableArray *even = [#[] mutableCopy];
NSMutableArray *odd = [#[] mutableCopy];
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
NSMutableArray *evenOrOdd = (idx % 2) ? even : odd;
[evenOrOdd addObject:object];
}];
If you like super compact code you could use the ternary operator like
[((idx % 2) ? even : odd) addObject:object];
If you want to split the array to N arrays, you can do
NSArray *array = #[#1,#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12];
NSArray *resultArrays = #[[#[] mutableCopy],
[#[] mutableCopy],
[#[] mutableCopy]];
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
[resultArrays[idx % resultArrays.count] addObject:object];
}];
In Objective-C Categories should come to your mind to create re-uasable code:
#interface NSArray (SplittingInto)
-(NSArray *)arraysBySplittingInto:(NSUInteger)N;
#end
#implementation NSArray (SplittingInto)
-(NSArray *)arraysBySplittingInto:(NSUInteger)N
{
NSAssert(N > 0, #"N cant be less than 1");
NSMutableArray *resultArrays = [#[] mutableCopy];
for (NSUInteger i =0 ; i<N; ++i) {
[resultArrays addObject:[#[] mutableCopy]];
}
[self enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
[resultArrays[idx% resultArrays.count] addObject:object];
}];
return resultArrays;
}
#end
Now you can do
NSArray *array = [#[#1,#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12] arraysBySplittingInto:2];
array contains
(
(
1,
3,
5,
7,
9,
11
),
(
2,
4,
6,
8,
10,
12
)
)

Create two NSIndexSets, one for the even indexes and one for the odd, then use objectsAtIndexes: to extract the corresponding slices of the array.

There are following ways you can achieve that:-
The first and second one solution are already mentioned by the above two. Below are the implementation of the same:-
//First Solution
NSArray *ar=#[#"1",#"2",#"3",#"4",#"5"];
NSMutableArray *mut1=[NSMutableArray array];
NSMutableArray *mut2=[NSMutableArray array];
[ar enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
if (idx%2==0)
{
[mut1 addObject:object];
}
else
{
[mut2 addObject:object];
}
}];
//Second Solution
NSMutableIndexSet *idx1 = [NSMutableIndexSet indexSet];
NSMutableIndexSet *idx2 = [NSMutableIndexSet indexSet];
for (NSUInteger index=0; index <ar.count(); index++)
{
if(index%2==0)
{
[idx1 addIndex:index];
}
else{
[idx2 addIndex:index];
}
}
NSArray *evenArr=[ar objectsAtIndexes:idx1];
NSArray *oddArr=[ar objectsAtIndexes:idx2];
NSLog(#"%#",evenArr);
NSLog(#"%#",oddArr);

Got some time for benchmarking and it turns out that when the input array has more than 10 million, it’s faster to use parallel execution.
Here is the concurrent solution that enumerates the input array twice to prevent race conditions on the output arrays.
static NSArray * concurrent(NSArray *input) {
NSUInteger capacity = input.count / 2;
NSArray *split = #[
[NSMutableArray arrayWithCapacity:capacity],
[NSMutableArray arrayWithCapacity:capacity],
];
[split enumerateObjectsWithOptions:NSEnumerationConcurrent
usingBlock:^(NSMutableArray *output, NSUInteger evenOdd, BOOL *stop) {
[input enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
if (index % 2 == evenOdd) {
[output addObject:object];
}
}];
}];
return split;
}
I consider this to be the best serial solution, so I used it for benchmarking:
static NSArray * serial(NSArray *input) {
NSUInteger capacity = input.count / 2;
NSMutableArray *even = [NSMutableArray arrayWithCapacity:capacity];
NSMutableArray *odd = [NSMutableArray arrayWithCapacity:capacity];
[input enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
NSMutableArray *output = (index % 2) ? odd : even;
[output addObject:object];
}];
return #[ even, odd ];
}
Results
1 million elements
Serial: 54.081 ms
Concurrent: 65.958 ms (18% worse)
10 million elements
Serial: 525.851 ms
Concurrent: 412.691 ms (27% better)
100 million elements
Serial: 5244.798 ms
Concurrent: 4137.939 ms (27% better)
Average of 5 runs.
Input filled with NSNumbers.
Fastest smallest optimization -Os.

Related

Using enumerateObjectsAtIndexes or a for-loop to iterate through all indexes of an NSArray BEFORE a given index

What's the most concise way to iterate through the indexes of an NSArray that occur before a given index? For example:
NSArray *myArray = #[ #"animal" , #"vegetable" , #"mineral" , #"piano" ];
[myArray enumerateObjectsAtIndexes:#"all before index 2" options:nil
usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// this block will be peformed on #"animal" and #"vegetable"
}];
Also, this should not loop at all if the given index is 0.
What's the most concise, elegant way to do this? So far I've only cobbled together clumsy multi-line answers that use annoying NSRanges and index sets. Is there a better way I'm overlooking?
NSArray *myArray = #[ #"animal" , #"vegetable" , #"mineral" , #"piano" ];
NSUInteger stopIndex = 2;
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (idx == stopIndex) {
*stop = YES; // stop enumeration
} else {
// Do something ...
NSLog(#"%#", obj);
}
}];
[myArray enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, idx)]
options:0
usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
}];
What about :
index = 2;
for (int i = 0; i < [myArray count] && i < index; ++i) {
id currObj = [myArray objectAtIndex:i];
// Do your stuff on currObj;
}
Personally I'd go with a block-based enumeration as shown by Martin R or yourfriendzak, the accepted answer by giorashc is probably the worst, as it doesn't provide a mutation guard.
I want to add a (correct) fast enumeration example
NSUInteger stopIndex = 2;
NSUInteger currentIndex = 0;
for (MyClass *obj in objArray) {
if (currentIndex < stopIndex) {
// do sth...
} else {
break;
}
++currentIndex;
}

Shuffle Two NSMutableArray independently

I'm creating two NSMutableArray in my viewDidLoad, I add it in a NSMutableDictionary. When I tried shuffling it with one array, It is okay. But the problem is when Im shuffling two arrays independently its not working,somehow the indexes got mixed up.
Here is my code for my array (1st Array):
self.items1 = [NSMutableArray new];
for(int i = 0; i <= 100; i++)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"[Images%d.png", i]];
if([[NSFileManager defaultManager] fileExistsAtPath:savedImagePath]){
self.container = [[NSMutableDictionary alloc] init];
[container setObject:[UIImage imageWithContentsOfFile:savedImagePath] forKey:#"items1"];
[container setObject:[NSNumber numberWithInt:i] forKey:#"index1"];
[items1 addObject:container];
}
}
NSLog(#"Count : %d", [items1 count]);
[items1 enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
NSLog(#"%# images at index %d", object, index);
}];
(2nd Array):
self.items2 = [NSMutableArray new];
for(int i = 0; i <= 100; i++)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"secondImages%d.png", i]];
NSLog(#"savedImagePath=%#",savedImagePath);
if([[NSFileManager defaultManager] fileExistsAtPath:savedImagePath]){
self.container = [[NSMutableDictionary alloc] init];
[container setObject:[UIImage imageWithContentsOfFile:savedImagePath] forKey:#"items2"];
[container setObject:[NSNumber numberWithInt:i] forKey:#"index2"];
[items2 addObject:container];
}
}
NSLog(#"Count : %d", [items2 count]);
[items2 enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
NSLog(#"%# images at index %d", object, index);
}];
My view for the iCarousel where im using my array:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
if (carousel == carousel1)
{
NSDictionary *obj = [items1 objectAtIndex:index];
view = [[UIImageView alloc] initWithImage:[obj objectForKey:#"items1"]];
view.tag = index;
}
else
{
NSDictionary *obj = [items2 objectAtIndex:index];
view = [[UIImageView alloc] initWithImage:[obj objectForKey:#"items2"]];
view.tag = index;
}
return view;
}
then my shuffle code(Which I tried duplicating for the other array,but not working also):
srandom(time(NULL));
NSUInteger count = [items1 count];
for (NSUInteger i = 0; i < count; ++i) {
int nElements = count - i;
int n = (random() % nElements) + i;
[items1 exchangeObjectAtIndex:i withObjectAtIndex:n];
}
How am I going to shuffle it using above code (or if you have other suggestions) with two arrays? Thanks
My other problem is when I tries subclassing the class for the shuffle method or either use the above code, their index mixed. For example:
Object: apple, ball, carrots, dog
Indexes: 1 2 3 4
but in my View when shuffled:
Object: carrots, apple, dog, balle
Indexes: 2 4 1 3
I also have a method, that when the carousel stop, it will delete the image on view.
the cleanest solution: use a Objective-C Category
NSMutableArray+RandomUtils.h
#import <Foundation/Foundation.h>
#interface NSMutableArray (RandomUtils)
-(void)shuffle;
#end
NSMutableArray+RandomUtils.m
#import "NSMutableArray+RandomUtils.h"
#implementation NSMutableArray (RandomUtils)
-(void)shuffle
{
NSUInteger count = [self count];
for (NSUInteger i = 0; i < count; ++i) {
NSUInteger nElements = count - i;
NSUInteger n = (arc4random() % nElements) + i;
[self exchangeObjectAtIndex:i withObjectAtIndex:n];
}
}
#end
import it, wherever you what to shuffle a MSMutableArray
[array1 shuffle];
[array2 shuffle];
I tried to reproduce your mixed indexes problem, but I can't
NSMutableArray *sarray = [NSMutableArray arrayWithArray: #[#"carrots", #"apple", #"dog", #"balle"]];
NSLog(#"%#", sarray);
[sa enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"%# object at index %lu", obj, idx);
}];
[sarray shuffle];
NSLog(#"%#", sarray);
[sarray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"%# object at index %lu", obj, idx);
}];
results in
(
carrots,
apple,
dog,
balle
)
carrots object at index 0
apple object at index 1
dog object at index 2
balle object at index 3
(
balle,
dog,
carrots,
apple
)
balle object at index 0
dog object at index 1
carrots object at index 2
apple object at index 3
BTW: your example is aslo incorrect: with four objects in an array the indices should start with 0 and maximum is 3. you have 1 and 4.
so array is shuffled and indexes are ok. You must have some other problem in code, that you didn't show.
You should give up your real code.
I am still ot sure, if I understand, what you want. But from your comments I assume, you want to shuffle an array but keep the old indices. This is just not possible, as the index is the position of an object inside an array. But you are always free to save the original index somewhere.
Or you just copy an array, shuffle one of those. Now ho can ask for the index of an object in both arrays.
NSArray *originalArray = #[#"carrots", #"apple", #"dog", #"balle"];
NSLog(#"%#", originalArray);
[originalArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"%# object at index %lu", obj, idx);
}];
NSArray *shuffledArray = [originalArray arrayShuffled];
NSLog(#"%#", shuffledArray);
[shuffledArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"%# object at index %lu %lu", obj, idx, [originalArray indexOfObject:obj]);
}];
Assuming the idea is to shuffle two array in the same order then you just need to do the exchange of the 2nd array right after the 1st array?
NSUInteger count = [items1 count];
for (NSUInteger i = 0; i < count; ++i) {
int nElements = count - i;
int n = (random() % nElements) + i;
[items1 exchangeObjectAtIndex:i withObjectAtIndex:n];
[items2 exchangeObjectAtIndex:i withObjectAtIndex:n];
}
Assuming that you have two different array of images which you need to shuffle the images.
for(int i = 0; i <= 100; i++)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"[Images%d.png", i]];
if([[NSFileManager defaultManager] fileExistsAtPath:savedImagePath]){
//Lazy initialization of dictionary
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:[UIImage imageWithContentsOfFile:savedImagePath] forKey:#"items1"];
[dict setObject:[NSNumber numberWithInt:i] forKey:#"index1"];
[items1 addObject:dict];
[dict release];
}
}
add the object to 2nd array in the same way.
NSUInteger count = [items1 count];
for(int i = 0 ;i < count; i++)
{
int nElements = count - i;
int n = (random() % nElements) + i;
[items1 exchangeObjectAtIndex:i withObjectAtIndex:n];
}
second array shuffling
NSUInteger count = [items2 count];
for(int i = 0 ;i < count; i++)
{
int nElements = count - i;
int n = (random() % nElements) + i;
[items2 exchangeObjectAtIndex:i withObjectAtIndex:n];
}
this should work. If you still find the problem let me know.

NSMutableArray removeObjectAtIndex usage

I am trying to filter out an array of strings based on their length. I'm completely new to Objective C and OOP in general.
wordList=[[stringFile componentsSeparatedByCharactersInSet:[NSCharacterSetnewlineCharacterSet]] mutableCopy];
for (int x=0; x<[wordList count]; x++) {
if ([[wordList objectAtIndex:x] length] != 6) {
[wordList removeObjectAtIndex:x];
}else {
NSLog([wordList objectAtIndex:x]);
}
}
for (int x=0; x<[wordList count]; x++) {
NSLog([wordList objectAtIndex:x]);
}
The NSLog in the else statement will only output 6 letter words, but the second NSLog outputs the entire array. What am I missing here? Also any general pointers to clean up/improve the code are appreciated.
Depending on what you feel is the easiest to understand you could either filter the array with a predicate or iterate over the array and remove objects. You should chose the approach that you have easiest to understand and maintain.
Filter using a predicate
Predicates are a very concise way of filtering array or sets but depending on your background they may feel strange to use. You could filter your array like this:
NSMutableArray * wordList = // ...
[wordList filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSString *word = evaluatedObject;
return ([word length] == 6);
}]];
Enumerating and removing
You cannot modify the array while enumerating it but you can make a note of all the items what you want to remove and remove them all in a batch after having enumerated the entire array, like this:
NSMutableArray * wordList = // ...
NSMutableIndexSet *indicesForObjectsToRemove = [[NSMutableIndexSet alloc] init];
[wordList enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *word = obj;
if ([word length] != 6) [indicesForObjectsToRemove addIndex:idx];
}];
[wordList removeObjectsAtIndexes:indicesForObjectsToRemove];
The problem with your code is that when you remove an item at index x and move to the next index x++, the item that was at x+1 is never examined.
The best way of filtering a mutable array is using the filterUsingPredicate: method. Here is how you use it:
wordList=[[stringFile
componentsSeparatedByCharactersInSet:[NSCharacterSetnewlineCharacterSet]]
mutableCopy];
[wordList filterUsingPredicate:
[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary * bindings) {
return [evaluatedObject length] == 6; // YES means "keep"
}]];

Get matched string from two NSArrays

How can I save the string that match from one NSArray with one index difference in NSMutableArray?
For example, there are three "apple", four "pineapple", six "banana", two "cocoa" and the rest of words dont have duplicate(s) in the nsarray, i would like to know if the nsarray has at least two same words. If yes, I would like to save "apple", "pineapple, "banana" and "cocoa" once in nsmutablearray. If there are other alike words, I would like to add them to namutablearray too.
My code (which still doesn't work properly);
NSArray *noWords = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:#"words" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:NULL]
componentsSeparatedByString:#"\n"]];
NSUInteger scount = [noWords count];
int ii = 0;
NSString *stringline;
for (ii; ii < scount; ii++)
{
stringline = [noWords objectAtIndex:ii];
NSLog(#"stringline : %# ", stringline);
}
int i = 1;
NSString *line;
for (i ; i < 10; i++)
{
line = [noWords objectAtIndex:i];
NSLog (#"line : %# ", line);
NSMutableArray *douwords = [NSMutableArray array];
if ([stringline isEqualToString:line])
{
NSString *newword;
for (newword in douwords)
{
[douwords addObject:newword];
NSLog (#"detected! %# ", douwords);
}
}
}
Here's a solution using two sets:
- (NSArray *)getDuplicates:(NSArray *)words
{
NSMutableSet *dups = [NSMutableSet set],
*seen = [NSMutableSet set];
for (NSString *word in words) {
if ([seen containsObject:word]) {
[dups addObject:word];
}
[seen addObject:word];
}
return [dups allObjects];
}
Assuming NSSet uses hash tables behind the scenes (which I'm betting it does), this is going to be faster than the previously suggested O(n^2) solution.
Here's something off the top of my head:
NSMutableSet* duplicates = [NSMutableSet set];
NSArray* words = [NSArray arrayWithObjects:#"Apple", #"Apple", #"Orange", #"Apple", #"Orange", #"Pear", nil];
[words enumerateObjectsUsingBlock:^(NSString* str, NSUInteger idx, BOOL *stop) {
for (int i = idx + 1; i < words.count; i++) {
if ([str isEqualToString:[words objectAtIndex:i]]) {
[duplicates addObject:str];
break;
}
}
}];
NSLog(#"Dups: %#", [duplicates allObjects]); // Prints "Apple" and "Orange"
The use of an NSSet, as opposed to an NSArray, ensures strings are not added more than once. Obviously, there are optimizations that could be done, but it should be a good starting point.
I assume that you want to count appearances of words in your array and output those with a count of more than one. A basic and verbose way to do that would be:
// Make an array of words - some duplicates
NSArray *wordList = [[NSArray alloc] initWithObjects:
#"Apple", #"Banana", #"Pencil",
#"Steve Jobs", #"Kandahar",
#"Apple", #"Banana", #"Apple",
#"Pear", #"Pear", nil];
// Make an mutable dictionary - the key will be a word from the list
// and the value will be a number representing the number of times the
// word appears in the original array. It starts off empty.
NSMutableDictionary *wordCount = [[NSMutableDictionary alloc] init];
// In turn, take each word in the word list...
for (NSString *s in wordList) {
int count = 1;
// If the word is already in the dictionary
if([wordCount objectForKey:s]) {
// Increse the count by one
count = [[wordCount objectForKey:s] intValue] + 1;
}
// Save the word count in the dictionary
[wordCount setObject:[NSNumber numberWithInt:count] forKey:s];
}
// For each word...
for (NSString *s in [wordCount keysOfEntriesPassingTest:
^(id key, id obj, BOOL *stop) {
if ([obj intValue] > 1) return YES; else return NO;
}]) {
// print the word and the final count
NSLog(#"%2d %#", [[wordCount objectForKey:s] intValue], s);
}
The output would be:
3 Apple
2 Pear
2 Banana

How can I reverse a NSArray in Objective-C?

I need to reverse my NSArray.
As an example:
[1,2,3,4,5] must become: [5,4,3,2,1]
What is the best way to achieve this?
There is a much easier solution, if you take advantage of the built-in reverseObjectEnumerator method on NSArray, and the allObjects method of NSEnumerator:
NSArray* reversedArray = [[startArray reverseObjectEnumerator] allObjects];
allObjects is documented as returning an array with the objects that have not yet been traversed with nextObject, in order:
This array contains all the remaining objects of the enumerator in enumerated order.
For obtaining a reversed copy of an array, look at danielpunkass' solution using reverseObjectEnumerator.
For reversing a mutable array, you can add the following category to your code:
#implementation NSMutableArray (Reverse)
- (void)reverse {
if ([self count] <= 1)
return;
NSUInteger i = 0;
NSUInteger j = [self count] - 1;
while (i < j) {
[self exchangeObjectAtIndex:i
withObjectAtIndex:j];
i++;
j--;
}
}
#end
Some benchmarks
1. reverseObjectEnumerator allObjects
This is the fastest method:
NSArray *anArray = #[#"aa", #"ab", #"ac", #"ad", #"ae", #"af", #"ag",
#"ah", #"ai", #"aj", #"ak", #"al", #"am", #"an", #"ao", #"ap", #"aq", #"ar", #"as", #"at",
#"au", #"av", #"aw", #"ax", #"ay", #"az", #"ba", #"bb", #"bc", #"bd", #"bf", #"bg", #"bh",
#"bi", #"bj", #"bk", #"bl", #"bm", #"bn", #"bo", #"bp", #"bq", #"br", #"bs", #"bt", #"bu",
#"bv", #"bw", #"bx", #"by", #"bz", #"ca", #"cb", #"cc", #"cd", #"ce", #"cf", #"cg", #"ch",
#"ci", #"cj", #"ck", #"cl", #"cm", #"cn", #"co", #"cp", #"cq", #"cr", #"cs", #"ct", #"cu",
#"cv", #"cw", #"cx", #"cy", #"cz"];
NSDate *methodStart = [NSDate date];
NSArray *reversed = [[anArray reverseObjectEnumerator] allObjects];
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(#"executionTime = %f", executionTime);
Result: executionTime = 0.000026
2. Iterating over an reverseObjectEnumerator
This is between 1.5x and 2.5x slower:
NSDate *methodStart = [NSDate date];
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[anArray count]];
NSEnumerator *enumerator = [anArray reverseObjectEnumerator];
for (id element in enumerator) {
[array addObject:element];
}
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(#"executionTime = %f", executionTime);
Result: executionTime = 0.000071
3. sortedArrayUsingComparator
This is between 30x and 40x slower (no surprises here):
NSDate *methodStart = [NSDate date];
NSArray *reversed = [anArray sortedArrayUsingComparator: ^(id obj1, id obj2) {
return [anArray indexOfObject:obj1] < [anArray indexOfObject:obj2] ? NSOrderedDescending : NSOrderedAscending;
}];
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(#"executionTime = %f", executionTime);
Result: executionTime = 0.001100
So [[anArray reverseObjectEnumerator] allObjects] is the clear winner when it comes to speed and ease.
DasBoot has the right approach, but there are a few mistakes in his code. Here's a completely generic code snippet that will reverse any NSMutableArray in place:
/* Algorithm: swap the object N elements from the top with the object N
* elements from the bottom. Integer division will wrap down, leaving
* the middle element untouched if count is odd.
*/
for(int i = 0; i < [array count] / 2; i++) {
int j = [array count] - i - 1;
[array exchangeObjectAtIndex:i withObjectAtIndex:j];
}
You can wrap that in a C function, or for bonus points, use categories to add it to NSMutableArray. (In that case, 'array' would become 'self'.) You can also optimize it by assigning [array count] to a variable before the loop and using that variable, if you desire.
If you only have a regular NSArray, there's no way to reverse it in place, because NSArrays cannot be modified. But you can make a reversed copy:
NSMutableArray * copy = [NSMutableArray arrayWithCapacity:[array count]];
for(int i = 0; i < [array count]; i++) {
[copy addObject:[array objectAtIndex:[array count] - i - 1]];
}
Or use this little trick to do it in one line:
NSArray * copy = [[array reverseObjectEnumerator] allObjects];
If you just want to loop over an array backwards, you can use a for/in loop with [array reverseObjectEnumerator], but it's likely a bit more efficient to use -enumerateObjectsWithOptions:usingBlock::
[array enumerateObjectsWithOptions:NSEnumerationReverse
usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// This is your loop body. Use the object in obj here.
// If you need the index, it's in idx.
// (This is the best feature of this method, IMHO.)
// Instead of using 'continue', use 'return'.
// Instead of using 'break', set '*stop = YES' and then 'return'.
// Making the surrounding method/block return is tricky and probably
// requires a '__block' variable.
// (This is the worst feature of this method, IMHO.)
}];
(Note: Substantially updated in 2014 with five more years of Foundation experience, a new Objective-C feature or two, and a couple tips from the comments.)
After reviewing the other's answers above and finding Matt Gallagher's discussion here
I propose this:
NSMutableArray * reverseArray = [NSMutableArray arrayWithCapacity:[myArray count]];
for (id element in [myArray reverseObjectEnumerator]) {
[reverseArray addObject:element];
}
As Matt observes:
In the above case, you may wonder if -[NSArray reverseObjectEnumerator] would be run on every iteration of the loop — potentially slowing down the code. <...>
Shortly thereafter, he answers thus:
<...> The "collection" expression is only evaluated once, when the for loop begins. This is the best case, since you can safely put an expensive function in the "collection" expression without impacting upon the per-iteration performance of the loop.
Georg Schölly's categories are very nice. However, for NSMutableArray, using NSUIntegers for the indices results in a crash when the array is empty. The correct code is:
#implementation NSMutableArray (Reverse)
- (void)reverse {
NSInteger i = 0;
NSInteger j = [self count] - 1;
while (i < j) {
[self exchangeObjectAtIndex:i
withObjectAtIndex:j];
i++;
j--;
}
}
#end
The most efficient way to enumerate an array in reverse:
Use enumerateObjectsWithOptions:NSEnumerationReverse usingBlock. Using #JohannesFahrenkrug's benchmark above, this completed 8x quicker than [[array reverseObjectEnumerator] allObjects];:
NSDate *methodStart = [NSDate date];
[anArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
//
}];
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(#"executionTime = %f", executionTime);
NSMutableArray *objMyObject = [NSMutableArray arrayWithArray:[self reverseArray:objArrayToBeReversed]];
// Function reverseArray
-(NSArray *) reverseArray : (NSArray *) myArray {
return [[myArray reverseObjectEnumerator] allObjects];
}
Reverse array and looping through it:
[[[startArray reverseObjectEnumerator] allObjects] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
...
}];
To update this, in Swift it can be done easily with:
array.reverse()
As for me, have you considered how the array was populated in the first place? I was in the process of adding MANY objects to an array, and decided to insert each one at the beginning, pushing any existing objects up by one. Requires a mutable array, in this case.
NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithCapacity:1];
[myMutableArray insertObject:aNewObject atIndex:0];
Or the Scala-way:
-(NSArray *)reverse
{
if ( self.count < 2 )
return self;
else
return [[self.tail reverse] concat:[NSArray arrayWithObject:self.head]];
}
-(id)head
{
return self.firstObject;
}
-(NSArray *)tail
{
if ( self.count > 1 )
return [self subarrayWithRange:NSMakeRange(1, self.count - 1)];
else
return #[];
}
There is a easy way to do it.
NSArray *myArray = #[#"5",#"4",#"3",#"2",#"1"];
NSMutableArray *myNewArray = [[NSMutableArray alloc] init]; //this object is going to be your new array with inverse order.
for(int i=0; i<[myNewArray count]; i++){
[myNewArray insertObject:[myNewArray objectAtIndex:i] atIndex:0];
}
//other way to do it
for(NSString *eachValue in myArray){
[myNewArray insertObject:eachValue atIndex:0];
}
//in both cases your new array will look like this
NSLog(#"myNewArray: %#", myNewArray);
//[#"1",#"2",#"3",#"4",#"5"]
I hope this helps.
I don't know of any built in method.
But, coding by hand is not too difficult. Assuming the elements of the array you are dealing with are NSNumber objects of integer type, and 'arr' is the NSMutableArray that you want to reverse.
int n = [arr count];
for (int i=0; i<n/2; ++i) {
id c = [[arr objectAtIndex:i] retain];
[arr replaceObjectAtIndex:i withObject:[arr objectAtIndex:n-i-1]];
[arr replaceObjectAtIndex:n-i-1 withObject:c];
}
Since you start with a NSArray then you have to create the mutable array first with the contents of the original NSArray ('origArray').
NSMutableArray * arr = [[NSMutableArray alloc] init];
[arr setArray:origArray];
Edit: Fixed n -> n/2 in the loop count and changed NSNumber to the more generic id due to the suggestions in Brent's answer.
If all you want to do is iterate in reverse, try this:
// iterate backwards
nextIndex = (currentIndex == 0) ? [myArray count] - 1 : (currentIndex - 1) % [myArray count];
You can do the [myArrayCount] once and save it to a local variable (I think its expensive), but I’m also guessing that the compiler will pretty much do the same thing with the code as written above.
Swift 3 syntax :
let reversedArray = array.reversed()
Try this:
for (int i = 0; i < [arr count]; i++)
{
NSString *str1 = [arr objectAtIndex:[arr count]-1];
[arr insertObject:str1 atIndex:i];
[arr removeObjectAtIndex:[arr count]-1];
}
Here is a nice macro that will work for either NSMutableArray OR NSArray:
#define reverseArray(__theArray) {\
if ([__theArray isKindOfClass:[NSMutableArray class]]) {\
if ([(NSMutableArray *)__theArray count] > 1) {\
NSUInteger i = 0;\
NSUInteger j = [(NSMutableArray *)__theArray count]-1;\
while (i < j) {\
[(NSMutableArray *)__theArray exchangeObjectAtIndex:i\
withObjectAtIndex:j];\
i++;\
j--;\
}\
}\
} else if ([__theArray isKindOfClass:[NSArray class]]) {\
__theArray = [[NSArray alloc] initWithArray:[[(NSArray *)__theArray reverseObjectEnumerator] allObjects]];\
}\
}
To use just call: reverseArray(myArray);