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.
My challenge this week has been to come to terms with blocks in objective-c. There is something about the syntax that does my head in. Getting there.
I have the following code to achieve a merge of two arrays in a specific way (see comment in code below).
NSArray *keys = #[#"name", #"age"];
NSArray *data = #[
#[#"mark", #"36 years"],
#[#"matt", #"35 years"],
#[#"zoe", #"7 years"]
];
// desired outcome is
// # { #"name" : #[#"mark", #"matt", #"zoe"],
// #"age" : #[#"36 years", #"35 years", #"7 years"]
// }
NSMutableArray *mergedData = [[NSMutableArray alloc] initWithCapacity:keys.count];
for (NSString *key in keys) {
NSLog(#"key: %#", key);
NSInteger keyIndex = [keys indexOfObject:key];
NSMutableArray *dataItemsForKey = [[NSMutableArray alloc] initWithCapacity:data.count];
for (NSArray *row in data) {
// double check the array count for row equals the expected count for keys - otherwise we have a 'match up' issue
if (row.count == keys.count) {
[dataItemsForKey addObject:[row objectAtIndex:keyIndex]];
}
}
[mergedData addObject:[NSDictionary dictionaryWithObject:dataItemsForKey forKey:key]];
}
NSLog (#"mergedData: %#", mergedData);
While this code works fine, in the interest of my challenge and learning, I was wondering if there is a more 'elegant' (aka less code, easier to read) way to do this using enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) ??
I can't quite see a way to make it work, but in the interests of self-education, wonder if those more learned in blocks and arrays may have a more elegant solution.
The first issue that I notice is that you are asking for the index of the current object while enumerating the array. This is a waste of operations, because at every loop iteration you have to look over all array elements (potentially O(N)) to find where the object is.
You could instead do this:
for(NSUInteger i=0; i<keys.count; i++)
{
NSString* key= keys[i];
<Rest of the code>
}
Or just keep track of the index manually incrementing it:
NSUInteger i=0;
for (NSString *key in keys)
{
<Your code>
i++;
}
Or like you wanted, with enumerateObjectsUsingBlock:, which is IMO the most elegant way to do it in this case. Here is an example:
NSMutableDictionary* dict=[NSMutableDictionary new];
[keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
NSMutableArray* fields=[NSMutableArray new];
for(NSArray* array in data)
{
[fields addObject: array[idx]];
}
[dict setObject: fields forKey: obj];
}];
In the case you haven't understood how it works, here is a further explanation:
This way at every execution of the block you can know which is the current object (obj) and it's index (idx). stop is just used to stop enumerating the array, but you don't need it in this case (say that you want to stop the enumeration, you set *stop=YES). In my code I just took every element at the index idx of data, and build an array which is the value that I put into the dictionary, that has obj (what you called key in your code) as key. For any further doubt feel free to ask any clarification through a comment.
The first thing to say is your code does not produce the desired output. You get an array with two dictionaries each with one key.
One way to solve the problem is like this:
NSMutableDictionary* mergedData = [[NSMutableDictionary alloc] init];
[keys enumerateObjectsUsingBlock: ^(id key, NSUInteger keyIndex, BOOL *stop)
{
NSMutableArray* keyValues = [[NSMutableArray alloc] init];
for (NSArray* row in data)
{
[keyValues addObject: [row objectAtIndex: keyIndex]];
}
[mergedData setObject: keyValues forKey: key];
}];
The above will throw an exception if a row doesn't have enough objects in it. You could either check it beforehand or allow the program to crash, it's up to you.
I have a word list stored in an NSArray, I want to find all the words in it with the ending 'ing'.
Could someone please provide me with some sample/pseudo code.
Use NSPredicate to filter NSArrays.
NSArray *array = #[#"test", #"testing", #"check", #"checking"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF ENDSWITH 'ing'"];
NSArray *filteredArray = [array filteredArrayUsingPredicate:predicate];
Let's say you have an array defined:
NSArray *wordList = // you have the contents defined properly
Then you can enumerate the array using a block
// This array will hold the results.
NSMutableArray *resultArray = [NSMutableArray new];
// Enumerate the wordlist with a block
[wordlist enumerateObjectsUsingBlock:(id obj, NSUInteger idx, BOOL *stop) {
if ([obj hasSuffix:#"ing"]) {
// Add the word to the result list
[result addObject:obj];
}
}];
// resultArray now has the words ending in "ing"
(I am using ARC in this code block)
I am giving an example using blocks because its gives you more options should you need them, and it's a more modern approach to enumerating collections. You could also do this with a concurrent enumeration and get some performance benefits as well.
Just loop through it and check the suffixes like that:
for (NSString *myString in myArray) {
if ([myString hasSuffix:#"ing"]){
// do something with myString which ends with "ing"
}
}
NSMutableArray *results = [[NSMutableArray alloc] init];
// assuming your array of words is called array:
for (int i = 0; i < [array count]; i++)
{
NSString *word = [array objectAtIndex: i];
if ([word hasSuffix: #"ing"])
[results addObject: word];
}
// do some processing
[results release]; // if you're not using ARC yet.
Typed from scratch, should work :)
I want a count on an array where a sub array meets a condition.
I thought I could do this, but I can't.
NSLog(#"%d",[[_sections enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[[obj objectAtIndex:4] isEqualToString:#"1"];
}] count]);
enumerateObjectsUsingBlock: doesn't return anything. I'd bet that code won't even compile (and, as your comment states, auto-completion doesn't work -- and it shouldn't).
Use NSArray's indexesOfObjectsPassingTest:
and take the count of the resulting NSIndexSet.
Documented here.
bbum is right; you should use indexesOfObjectsPassingTest. It's more straightforward.
But you could use enumerateObjectsUsingBlock to count test-passers, like this:
NSArray *sections = [NSArray arrayWithObjects:#"arb", #"1", #"misc", #"1", #"extra", nil];
NSMutableArray *occurrencesOf1 = [NSMutableArray array];
[sections enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([(NSString*)obj isEqualToString:#"1"])
[occurrencesOf1 addObject:obj];
}];
NSLog(#"%d", [occurrencesOf1 count]); // prints 2
It's inefficient because it requires that extra mutable array.
(So you should check bbum's answer as the accepted one -- but I'm new at block functions too, and appreciated the puzzle.)
It's faster to use a for loop (and, IMO, more readable):
NSLog(#"%lu", (unsigned long)[self countSectionsWithValue:#"1" atIndex:4]);
// ...
}
// ...
- (NSUInteger) countSectionsWithValue:(NSString *)value atIndex:(NSInteger)idx
{
NSUInteger count = 0
for (id section in _sections)
{
if ([[section objectAtIndex:idx] isEqualToString:value])
{
count++;
}
}
return count;
}
Also note that I used the proper %lu format and (unsigned long) type in the NSLog. %d isn't descriptive and doesn't act the same in all scenarios.
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);