Moving several objects in a NSMutableArray - objective-c

Let's suppose I have a NSMutableArray like this:
Object 0
Object 1
Object 2
Object 3
Object 4
and would like to take Object 0 and Object 1 and move them behind Object 4:
Object 2
Object 3
Object 4
Object 0
Object 1
I have this rather long code to achieve the re-ordering of multiple objects, but I was wondering if there is a more straightforward / elegant way:
int from = 0;
int to = 5;
int lastIndexOfObjectsToBeMoved = 1;
NSMutableArray *objectsToBeMoved = [[NSMutableArray alloc] init];
for (int i = from; i < lastIndexOfObjectsToBeMoved; i++) {
object *o = [self.objects objectAtIndex:i];
[objectsToBeMoved addObject:o];
}
NSUInteger length = lastIndexOfObjectsToBeMoved-from;
NSRange indicesToBeDeleted = NSMakeRange(from, length);
[self.objects removeObjectsInRange:indicesToBeDeleted];
NSIndexSet *targetIndices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(to, length)];
[self.objects insertObjects:objectsToBeMoved atIndexes:targetIndices];
Edit: sorry, I should have clarified, I'm not always moving objects to the very end, but would also like to be able to do things like moving object 2 and 3 to index 0.

Make an NSRange of the indexes you want to move to the back. Grab those objects with subarrayWithRange, remove them with removeObjectsInRange: and add them back to the end by calling addObjectsFromArray:. This is a much more concise way of writing what you have.

rowIndexes is a NSIndexSet with objects you like move
row is the index destination
NSArray *objectsToMove = [your_array objectsAtIndexes: rowIndexes];
// If any of the removed objects come before the row
// we want to decrement the row appropriately
row -= [rowIndexes countOfIndexesInRange: (NSRange){0, row}];
[your_array removeObjectsAtIndexes:rowIndexes];
[your_array replaceObjectsInRange: (NSRange){row, 0}
withObjectsFromArray: objectsToMove];
I hope this helps you

Related

NSMutableArray cannot remove duplicates

I have duplicates in my array and i want to get rid of them, so i run this loop, however it doesn't work. Any one know why?
The array currently has 3 items, 2 duplicates and 1 unique.
for (int x = 0; x <= [array count]; x++) {
if(x > 0){
if([[array objectAtIndex:x - 1] isEqualToString:[array objectAtIndex:x]]){
[array removeObjectAtIndex:x];
}
}
}
You can't iterate over an object and modify it at the same time. Once you remove an object, the indexes of all the objects change. You can try copying the array first and iterate that and make the modifications in the original array, but you still might have to change some of your logic depending on what you're trying to accomplish.
Your algorithm only ever compares items that are next to each other in the array (the items at positions x and x-1). If the duplicates are in any other positions, they won't be found.
The naïve way to fix this is to do a double loop. Compare each item in the array with every item after it. This will start taking an extremely long time as your array becomes bigger.
The correct way to do this is to let the framework handle the operation. Convert your array to a set (which does not have duplicates by definition) and then back to an array:
NSSet * s = [NSSet setWithArray:array];
NSArray * dedupedArray = [s allObjects];
If you need to preserve the order, you'll have to do this in a slightly roundabout way, although this is still faster than the double-loop:
NSMutableSet * itemsSeen = [NSMutableSet set];
NSMutableArray * dedupedArray = [NSMutableArray array];
for( id item in array ){
if( ![itemsSeen containsObject:item] ){
[itemsSeen addObject:item];
[dedupedArray addObject:item];
}
}
I would suggest simply using NSSet ( or NSMutableSet ). It will automatically ensure you have only one of every object.
BUT - notice it is one of every OBJECT. It can have 2 objects that are different but have the same inner value.
If you want to ensure that there are no duplicates in your array, it would be better to use an NSMutableSet rather than an NSMutableArray.
NSMutableSet maintains the invariant that every object in the set is unique.
For example:
NSMutableSet* set = [NSMutableSet set];
NSString* data = #"Data";
[set addObject:data];
[set addObject:data];
The second call to addObject: will do nothing as data is already in the set.

object inside NSMutableArray is not update immediately after updating

I have a very strange error using NSMutableArray in cocos2d/xcode
In my code, I have a NSMutableArray containing object A
NSMutableArray *peasantArray;
and another NSMutableArray contain object B
NSMutableArray *treeArray;
in the scheduled update function, I called another function which is essentially the following functionality:
I would loop through all the object A inside the *peasantArray, and if any peasant object has a variable var == GameEntityCommandIdling, I would modify the object B in the second NSMutableArray *treeArray,
The issue is that I notice sometimes after I modified the object A inside the peasantArray, the modified variable (var) is being modified/updated inside the object A after by printing out the variable status in a scheduled fashion; but if I am to loop through the NSMutableArray *peasantArray again in the next schedule (1/30s), I will again find the object A with the older/un-updated variable (var), and this is causing my algorithm to be wrong,
However, if I loop through the NSMutableArray *peasantArray less than 1second, each time I would see the variable (var) for object A correctly as the updated variable value,
So is there a limit on how fast I can iterate over the NSMutableArray?
here are some piece of code that I basically just mentioned,
NSMutableArray *peasantArray;
NSMutableArray *treeArray;
.....
peasantArray = [[[NSMutableArray alloc] initWithCapacity:1]retain];
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
}
....
//inside the update()
for (int i = 0;i < [peasantArray count];i++)
{
Peasant *A = [peasantArray objectAtIndex:i];
if (A.status == something)
{
printf("A.status is something\n");
A.status = sometingelse;
...
//update other things not related to this NSMutableArray or object
}
}
.....
SO the error is that sometimes I will have multiple printf of "A.status is something", although only one of it should be printed,
I really appreciate any help,
Thanks,
So is there a limit on how fast I can iterate over the NSMutableArray?
Definitely no. That would be the stupidest implementation of an array I'd ever encountered. :)
The simplest explanation is usually the answer. You say the message is printed more than once. Well have you checked that maybe more than one Peasant has its status set to something?
If that's not the case, are you certain the status update is printed twice in the same frame, and not twice in two subsequent updates?
Earlier on you seemed to indicate that the effect of iterating over one array and modifying the other array's objects is invalidated somehow. That made me want to point out that if you have the same object in both arrays, modifying the object's properties in array A will also modify the properties of the same object contained in array B.
You may want to give breakpoints a(nother) try. The problem should be easy to locate when you step through the code.
Here you have a memory leak
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
}
you should release A after adding it to the array since the addObject adds a reference count to the object.
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
[A release];
}

trying to loop through nested array , obj-c

I have an array with 5 sub arrays that I'm trying to loop through,
I can access the first sub array and its objects but when I increase the variable count and try to access the second sub array my program crashes any ideas on a better way to do this? This is the my general method:
-(void) accessArray {
NSArray *myArray; // my array that holds sub arrays
int count = 0; //used to hold which sub array im accesing
NSArray *subArray = [myArray objectAtIndex:count];
//do something with object = [subArray objectAtIndex:0];
//do something with object = [subArray objectAtIndex:1];
}
-(void) otherMethod {
count ++
[self accessArray];
}
for (NSArray *inner in outerArray)
for (id object in inner) {
... do stuff ...
}
}
In addition to bbums answer you can make sub arrays using NSRange (example from NSArray Class Reference):
NSArray *halfArray;
NSRange theRange;
theRange.location = 0;
theRange.length = [wholeArray count] / 2;
halfArray = [wholeArray subarrayWithRange:theRange];
Only saying this because it looks like this might be what you are trying to do. This doesn't iterate through objects, but it is handy when trying to make new arrays from others.
If you REALLY want to do it using your way (i.e. access the subarrays through 'otherMethod'), you'll need to make the 'count' variable accessible to both methods:
int count = 0; // used to hold which sub array I'm accessing
-(void) accessArray {
NSArray *myArray; // my array that holds sub arrays
NSArray *subArray = [myArray objectAtIndex:count];
// do something with object = [subArray objectAtIndex:0];
// do something with object = [subArray objectAtIndex:1];
}
-(void) otherMethod {
count++; // Cannot access count if defined inside 'accessArray'
[self accessArray];
}
Now you can use 'otherMethod' to access the subarrays. But I think the best way to do this is already given in the first answer above by bbum.

Declare Dynamic Array

How can I declare dynamic array? For example:
int k=5;
I want to have an array like below:
int myArray[k];
if i read the question right.. (unlikely at this point)
NSMutableArray *myArray = [[NSMutableArray alloc] initWithCapacity:k];
Sometimes true arrays (not NSArray) are really needed. See for example indexPathWithIndexes:length: in NSIndexPath, it take array of uintegers as parameter. For array allocation you should use the following approach:
NSUInteger *arr = (NSUInteger*)malloc(elementsCount * sizeof(NSUInteger) );
arr[0] = 100;
free(arr);
In Objective-C, the standard way to do this is to use the NSMutableArray class. This is a container that can hold any object (note that int is not an object! You'll have to wrap your integers in NSNumber.) Quick example:
NSMutableArray* someIntegers = [[NSMutableArray alloc] initWithCapacity:1];
[someIntegers addObject:[NSNumber numberWithInt:2]];
//I've added one thing to my array.
[someIntegers addObject:[NSNumber numberWithInt:4]];
//See how I can put more objects in than my capacity allows?
//The array will automatically expand if needed.
//The array now contains 2 (at index 0) and 4 (at index 1)
int secondInteger = [[someIntegers objectAtIndex:1] intValue];
//Retrieving an item. -intValue is needed because I stored it as NSNumber,
//which was necessary, because NSMutableArray holds objects, not primitives.
Well in my book it's ok to use VLAs in Objective-C.
So something like
int foo = 10;
int bar[foo];
is allowed. Of course this is not a dynamic array as in automatically adjusting its size. But if you only need a native array on the stack that's fine.
You can use Objetive-C++.
First rename your class like this: MyClass.mm the ".mm" extension tells Xcode that this clas is a Objetive-C++ class, not a Objetive-C class.
then you can use dynamics C++ arrays like this:
int *pixels = new int[self.view.size.width];
for (int offset = 0; offset = self.view.size.width; offset++) {
pixeles[offset] = rawData[offset];
}
then you can pass "pixels" in a method:
Scan *myScan = [[Scan alloc] initWhithArray:pixels];
the method "initWithScan" is declared like this:
-(id)initWithArray:int[]pixels;
the "initWithScan" implementation is like this:
-(id)initWithScan:int[]pixels {
if (self = [super init]) {
for (int i = 0; i < self.myView.size.width; i++) {
NSLog(#"Pixel: %i", pixels[i];
}
}
return self;
}
I hoppe this was useful.

Best way to remove from NSMutableArray while iterating?

In Cocoa, if I want to loop through an NSMutableArray and remove multiple objects that fit a certain criteria, what's the best way to do this without restarting the loop each time I remove an object?
Thanks,
Edit: Just to clarify - I was looking for the best way, e.g. something more elegant than manually updating the index I'm at. For example in C++ I can do;
iterator it = someList.begin();
while (it != someList.end())
{
if (shouldRemove(it))
it = someList.erase(it);
}
For clarity I like to make an initial loop where I collect the items to delete. Then I delete them. Here's a sample using Objective-C 2.0 syntax:
NSMutableArray *discardedItems = [NSMutableArray array];
for (SomeObjectClass *item in originalArrayOfItems) {
if ([item shouldBeDiscarded])
[discardedItems addObject:item];
}
[originalArrayOfItems removeObjectsInArray:discardedItems];
Then there is no question about whether indices are being updated correctly, or other little bookkeeping details.
Edited to add:
It's been noted in other answers that the inverse formulation should be faster. i.e. If you iterate through the array and compose a new array of objects to keep, instead of objects to discard. That may be true (although what about the memory and processing cost of allocating a new array, and discarding the old one?) but even if it's faster it may not be as big a deal as it would be for a naive implementation, because NSArrays do not behave like "normal" arrays. They talk the talk but they walk a different walk. See a good analysis here:
The inverse formulation may be faster, but I've never needed to care whether it is, because the above formulation has always been fast enough for my needs.
For me the take-home message is to use whatever formulation is clearest to you. Optimize only if necessary. I personally find the above formulation clearest, which is why I use it. But if the inverse formulation is clearer to you, go for it.
One more variation. So you get readability and good performace:
NSMutableIndexSet *discardedItems = [NSMutableIndexSet indexSet];
SomeObjectClass *item;
NSUInteger index = 0;
for (item in originalArrayOfItems) {
if ([item shouldBeDiscarded])
[discardedItems addIndex:index];
index++;
}
[originalArrayOfItems removeObjectsAtIndexes:discardedItems];
This is a very simple problem. You just iterate backwards:
for (NSInteger i = array.count - 1; i >= 0; i--) {
ElementType* element = array[i];
if ([element shouldBeRemoved]) {
[array removeObjectAtIndex:i];
}
}
This is a very common pattern.
Some of the other answers would have poor performance on very large arrays, because methods like removeObject: and removeObjectsInArray: involve doing a linear search of the receiver, which is a waste because you already know where the object is. Also, any call to removeObjectAtIndex: will have to copy values from the index to the end of the array up by one slot at a time.
More efficient would be the following:
NSMutableArray *array = ...
NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:[array count]];
for (id object in array) {
if (! shouldRemove(object)) {
[itemsToKeep addObject:object];
}
}
[array setArray:itemsToKeep];
Because we set the capacity of itemsToKeep, we don't waste any time copying values during a resize. We don't modify the array in place, so we are free to use Fast Enumeration. Using setArray: to replace the contents of array with itemsToKeep will be efficient. Depending on your code, you could even replace the last line with:
[array release];
array = [itemsToKeep retain];
So there isn't even a need to copy values, only swap a pointer.
You can use NSpredicate to remove items from your mutable array. This requires no for loops.
For example if you have an NSMutableArray of names, you can create a predicate like this one:
NSPredicate *caseInsensitiveBNames =
[NSPredicate predicateWithFormat:#"SELF beginswith[c] 'b'"];
The following line will leave you with an array that contains only names starting with b.
[namesArray filterUsingPredicate:caseInsensitiveBNames];
If you have trouble creating the predicates you need, use this apple developer link.
I did a performance test using 4 different methods. Each test iterated through all elements in a 100,000 element array, and removed every 5th item. The results did not vary much with/ without optimization. These were done on an iPad 4:
(1) removeObjectAtIndex: -- 271 ms
(2) removeObjectsAtIndexes: -- 1010 ms (because building the index set takes ~700 ms; otherwise this is basically the same as calling removeObjectAtIndex: for each item)
(3) removeObjects: -- 326 ms
(4) make a new array with objects passing the test -- 17 ms
So, creating a new array is by far the fastest. The other methods are all comparable, except that using removeObjectsAtIndexes: will be worse with more items to remove, because of the time needed to build the index set.
Either use loop counting down over indices:
for (NSInteger i = array.count - 1; i >= 0; --i) {
or make a copy with the objects you want to keep.
In particular, do not use a for (id object in array) loop or NSEnumerator.
For iOS 4+ or OS X 10.6+, Apple added passingTest series of APIs in NSMutableArray, like – indexesOfObjectsPassingTest:. A solution with such API would be:
NSIndexSet *indexesToBeRemoved = [someList indexesOfObjectsPassingTest:
^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [self shouldRemove:obj];
}];
[someList removeObjectsAtIndexes:indexesToBeRemoved];
Nowadays you can use reversed block-based enumeration. A simple example code:
NSMutableArray *array = [#[#{#"name": #"a", #"shouldDelete": #(YES)},
#{#"name": #"b", #"shouldDelete": #(NO)},
#{#"name": #"c", #"shouldDelete": #(YES)},
#{#"name": #"d", #"shouldDelete": #(NO)}] mutableCopy];
[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if([obj[#"shouldDelete"] boolValue])
[array removeObjectAtIndex:idx];
}];
Result:
(
{
name = b;
shouldDelete = 0;
},
{
name = d;
shouldDelete = 0;
}
)
another option with just one line of code:
[array filterUsingPredicate:[NSPredicate predicateWithFormat:#"shouldDelete == NO"]];
In a more declarative way, depending on the criteria matching the items to remove you could use:
[theArray filterUsingPredicate:aPredicate]
#Nathan should be very efficient
Here's the easy and clean way. I like to duplicate my array right in the fast enumeration call:
for (LineItem *item in [NSArray arrayWithArray:self.lineItems])
{
if ([item.toBeRemoved boolValue] == YES)
{
[self.lineItems removeObject:item];
}
}
This way you enumerate through a copy of the array being deleted from, both holding the same objects. An NSArray holds object pointers only so this is totally fine memory/performance wise.
Add the objects you want to remove to a second array and, after the loop, use -removeObjectsInArray:.
this should do it:
NSMutableArray* myArray = ....;
int i;
for(i=0; i<[myArray count]; i++) {
id element = [myArray objectAtIndex:i];
if(element == ...) {
[myArray removeObjectAtIndex:i];
i--;
}
}
hope this helps...
Why don't you add the objects to be removed to another NSMutableArray. When you are finished iterating, you can remove the objects that you have collected.
How about swapping the elements you want to delete with the 'n'th element, 'n-1'th element and so on?
When you're done you resize the array to 'previous size - number of swaps'
If all objects in your array are unique or you want to remove all occurrences of an object when found, you could fast enumerate on an array copy and use [NSMutableArray removeObject:] to remove the object from the original.
NSMutableArray *myArray;
NSArray *myArrayCopy = [NSArray arrayWithArray:myArray];
for (NSObject *anObject in myArrayCopy) {
if (shouldRemove(anObject)) {
[myArray removeObject:anObject];
}
}
benzado's anwser above is what you should do for preformace. In one of my applications removeObjectsInArray took a running time of 1 minute, just adding to a new array took .023 seconds.
I define a category that lets me filter using a block, like this:
#implementation NSMutableArray (Filtering)
- (void)filterUsingTest:(BOOL (^)(id obj, NSUInteger idx))predicate {
NSMutableIndexSet *indexesFailingTest = [[NSMutableIndexSet alloc] init];
NSUInteger index = 0;
for (id object in self) {
if (!predicate(object, index)) {
[indexesFailingTest addIndex:index];
}
++index;
}
[self removeObjectsAtIndexes:indexesFailingTest];
[indexesFailingTest release];
}
#end
which can then be used like this:
[myMutableArray filterUsingTest:^BOOL(id obj, NSUInteger idx) {
return [self doIWantToKeepThisObject:obj atIndex:idx];
}];
A nicer implementation could be to use the category method below on NSMutableArray.
#implementation NSMutableArray(BMCommons)
- (void)removeObjectsWithPredicate:(BOOL (^)(id obj))predicate {
if (predicate != nil) {
NSMutableArray *newArray = [[NSMutableArray alloc] initWithCapacity:self.count];
for (id obj in self) {
BOOL shouldRemove = predicate(obj);
if (!shouldRemove) {
[newArray addObject:obj];
}
}
[self setArray:newArray];
}
}
#end
The predicate block can be implemented to do processing on each object in the array. If the predicate returns true the object is removed.
An example for a date array to remove all dates that lie in the past:
NSMutableArray *dates = ...;
[dates removeObjectsWithPredicate:^BOOL(id obj) {
NSDate *date = (NSDate *)obj;
return [date timeIntervalSinceNow] < 0;
}];
Iterating backwards-ly was my favourite for years , but for a long time I never encountered the case where the 'deepest' ( highest count) object was removed first. Momentarily before the pointer moves on to the next index there ain't anything and it crashes.
Benzado's way is the closest to what i do now but I never realised there would be the stack reshuffle after every remove.
under Xcode 6 this works
NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:[array count]];
for (id object in array)
{
if ( [object isNotEqualTo:#"whatever"]) {
[itemsToKeep addObject:object ];
}
}
array = nil;
array = [[NSMutableArray alloc]initWithArray:itemsToKeep];