iterate list and remove items from it in objective-c? - objective-c

Objective-c have a built-in list iterantor via 'for(a in b) syntax that works fine with NSArray and other collections. But is it possible to remove items during such iteration without ugly tricks like
for( int i = 0, i < [array count]; i ++ )
{
if( condition )
{
[array removeItemAtIndex : i];
i --;
}
}

You can iterate array in the reverse order so you won't need to extra adjust index:
for( int i = [array count]-1; i >=0; --i)
{
if( condition )
{
[array removeItemAtIndex : i];
}
}
Or accumulate indexes to delete in indexset while enumerating and then delete all elements at once:
NSMutableIndexSet *indexes = [[NSMutableIndexSet alloc] init];
for( int i = 0, i < [array count]; i ++ )
{
if( condition )
{
[indexes addIndex : i];
}
}
[array removeObjectsAtIndexes:indexes];
[indexes release];
I would choose 2nd option because changing array while enumerating may be not a good style (although it won't give you errors in this particular case)

No, mutating a collection while enumerating it is explicitly forbidden. You could add the objects that should be removed to a second temporary mutable array and then, outside the loop, remove the objects in that array from the original with removeObjectsInArray:.

I wouldn't Call it a dirty trick to iterate backwards through the array.
for(int i=array.count-1;i>=0;i--)
if(condition)
[array removeItemAtIndex:i];

If I need it I copy my array and enumerate one while the other is mutating
NSArray *arr = firstArr;
for( int i = [array count]-1, i >=0; --i)
{
if( condition )
{
[firstArr removeItemAtIndex : i];
}
}
but I think Vladimir's example is the better one ;) (just to have all possibilities)

Related

Getting a random object from NSArray without duplication

I have an NSArray with 17 objects, something like this:
NSArray *objArray = [[NSArray alloc]initWithObjects: #"1",#"2",#"3",#"4",#"5",#"6"
,#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17", nil];
and an int with a random number as follows:
int random = arc4random()%17+1;
I want to get a random object from this NSArray without it being a duplicate, even if I closed the app (maybe by using NSUserDefaults).
If I've gotten all the objects I want to generate a new random sequence for the same objects.
You could do this by making a mutable copy of the array, and after you make a random selection from that array, remove that same object. When you want to save the array, save the mutable array itself, so can resume where you left off when the app restarts. This little test app does that, and just logs the value of the random pick:
- (void)viewDidLoad {
[super viewDidLoad];
self.objArray = #[#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17"];
self.mut = [self.objArray mutableCopy];
}
-(IBAction)pickNumber:(id)sender {
int index = arc4random_uniform(self.mut.count);
NSLog(#"%#", self.mut[index]);
[self.mut removeObjectAtIndex:index];
if (self.mut.count == 0) {
self.mut = [self.objArray mutableCopy];
NSLog(#"*******************");
}
}
As a starting point, you could shuffle your array:
+ (NSArray *)arrayByShufflingArray:(NSArray *)array
{
// Fisher-Yates algorithm
NSMutableArray *result = [array mutableCopy];
NSUInteger count = [result count];
for (NSInteger i = ((NSInteger) count) - 1; i > 0; i--) {
NSUInteger firstIndex = (NSUInteger)i;
NSUInteger secondIndex = arc4random() % (NSUInteger)(i + 1);
[result exchangeObjectAtIndex:firstIndex withObjectAtIndex:secondIndex];
}
return result;
}
Step through each shuffled element and when you get to the end, reshuffle.
It can still happen that an item is selected twice in a row when the last item of one shuffle is the same as the first item in the next shuffle. If you want to avoid this you'll have to add some additional code.
Just Copy and Paste
-(NSMutableArray*)getRandomValueFromArray:(NSMutableArray*)arrAllData randomDataCount:(NSInteger)count {
NSMutableArray *arrFilterData = [[NSMutableArray alloc]init];
for(int i=0; i<count; i++){
NSInteger index = arc4random() % (NSUInteger)(arrAllData.count);
[arrFilterData addObject:[arrAllData objectAtIndex:index]];
[arrAllData removeObjectAtIndex:index];
}
return arrFilterData;
}
Note: count = number of random values you want to fetch from array.

Array - find how many times an object repeats consecutively

My array objects are as follows:
10,10,10
20,23,14
10,10,10
10,10,10
10,10,10
32,23,42
32,23,42
10,10,10
32,23,23
32,23,23
How can I go through this array and find out how many times the same object repeats sequentially, then add a , and the number of times it repeats?
Then save a new array with objects like:
10,10,10,1
20,23,14,1
10,10,10,3
32,23,42,2
10,10,10,1
32,23,23,2
Any help would be appreciated.
Thanks!
Try this:
NSMutableArray *outArray = [[NSMutableArray alloc] init];
for (NSUInteger j = 0; j < [theArray count]; j++) {
id object = [theArray objectAtIndex:j];
NSUInteger repeats = 1;
while (j + 1 < [theArray count] && [[theArray objectAtIndex:j + 1] isEqual:object]) {
j++;
repeats++;
}
[outArray addObject:object];
[outArray addObject:[NSNumber numberWithUnsignedInteger:repeats]];
}
return outArray;
This can also be done in place if the input array is mutable. I leave that as an exercise for the reader.
Break up every three integers into its own array (make sure they are strings).
Then iterate through each one of those arrays, and input into an NSMutableDictionary, the key is the string (your number), the value is a counter (if seen once, add 1, etc...)
Keep a pointer to the highest key (if newCount > highestCountPointer, then highestCountPointer=newCount)
At the end of that iteration, add the number that the highestCountPoints to to the end of the array.
I'm not an Objective C programmer, so please pardon any language gaffes. Something like the following should do the job:
NSMutableArray *result = [[NSMutableArray alloc] init];
id pending = nil;
NSUInteger count = 0;
for (NSUInteger i = 0; i < [theArray count]; i++) {
id object = [theArray objectAtIndex:i];
if ([object isEqual:pending]) {
count++;
} else {
if (pending != nil) {
[result addObject:[NSString stringWithFormat:#"%#,%d", pending, count]];
}
pending = object;
count = 1;
}
}
if (pending != nil) {
[result addObject:[NSString stringWithFormat:#"%#,%d", pending, count]];
}
Just run "uniq -c" from command line :)

Add objects to NSMutableArray

I'm trying to add objects to NSMutableArray (categoriasArray), but its not done by the iterator:
#synthesize categoriasArray;
for (int i = 0; i < [categories count]; i++) {
categoria *cat = [[categoria alloc] initWithDictionary:[categories objectAtIndex:i]];
[self.categoriasArray addObject:cat];
cat=nil;
}
After the for iterator, categoriasArray has 0 objects.
Many thanks
Check that the array is not nil before the loop starts:
NSLog(#"%#", self.categoriasArray); // This will output null
for (int i = 0; i < [categories count]; i++) {
// ...
}
What you should understand is that synthesizing the property categoriasArray doesn't initialize it, it just generates the setter and the getter methods. So, to solve your problem, initialize the array before the loop, (or in the init method of your class):
self.categoriasArray = [[NSMutableArray alloc] init];
The other possibility is that categories is itself nil or doesn't contain any items. To check that, add NSLogs before the loop:
NSLog(#"%#", self.categoriasArray);
NSLog(#"%#", categories);
NSLog(#"%d", [categories count]);
for (int i = 0; i < [categories count]; i++) {
// ...
}
try this
for(categoria *cat in categoria){
[self.categoriasArray addObject:cat];
// check you go here or not
}
I would advise getting in the habit of initializing your arrays with autorelease formatting such as the following.This is not only less to type but also good practice for mem management purposes. Of course if you are using ARC then both will work. This goes the same for NSString and many others (i.e. self.categoriasString = [NSMutableString string];)
self.categoriasArray = [NSMutableArray array];
Afterword you can add objects to that array by calling [self.categoriasArray addObject:cat];

Duplicate element elimination from NSMutableArray

I have an NSMutableArray. I have synthesized it as well. I am adding objects to it from a method by xml extraction of some file names etc. Now in my array there is the occurrence of same elements multiple times. I need to eliminate the multiple objects and get only unique ones. How can this be done? I tried the code below but then there arise some errors.
NSString *AName = [CompleteFileName_string1 substringWithRange:ArtistRange];
[array_artist addObject:AName];
for(int i=0; i < [array_artist count]; i++)
{
if([[array_artist objectAtIndex:i] isEqualToString:[array_artist objectAtIndex:i+1]])
{
[[array_artist objectAtIndex:i+1]=NULL];
}
else
{
}
}
EDIT*** i need to eliminate by ignoring case sensitivity too...
Go With NSSet
Take a tempArray as golbally
When you are adding objects from XML you have to just check
NSString *upCaseString=[stringFromXML uppercaseString];
if(![tempArray containsObject:upCaseString]) {
[array_artist addObject:stringFromXML];
[tempArray addObject:upCaseString];
}
if([[array_artist objectAtIndex:i] &&[array_artist objectAtIndex:i+1] caseInsensitiveCompare:#"True"] == NSOrderedSame])
{
Add element
}
Just simply check if the array contains the object:
if(![yourArray containsObject:stringFromXML])
{
[yourArray addObject:stringFromXML]
}
What about:
NSArray *out = [NSSet setWithArray:in].allObjects;
?
Use Following Code..
First Add All values in array_artist
int i1=0;
tempArray = [NSMutableArray alloc] init];
while (i1 < [array_artist count]) {
if ([tempArray containsObject:[array_artist objectAtIndex:i1]) {
//Do not Add
}
else {
//Add object
[tempArray addObject:array_artist:i1];
}
i1++;
}

Remove items in a for loop without side effects?

Can I remove items that I am looping through in an Objective-C for loop without side effects?
For example, is this ok?
for (id item in items) {
if ( [item customCheck] ) {
[items removeObject:item]; // Is this ok here?
}
No, you'll get an error if you mutate the array while in a fast enumeration for loop. Make a copy of the array, iterate over it, and remove from your original.
NSArray *itemsCopy = [items copy];
for (id item in itemsCopy) {
if ( [item customCheck] )
[items removeObject:item]; // Is this ok here
}
[itemsCopy release];
Nope:
Enumeration is “safe”—the enumerator has a mutation guard so that if you attempt to modify the collection during enumeration, an exception is raised.
Options for changing an array that you want to enumerate through are given in Using Enumerators: either copy the array and enumerate through, or build up an index set that you use after the loop.
you can remove like this:
//Create array
NSMutableArray* myArray = [[NSMutableArray alloc] init];
//Add some elements
for (int i = 0; i < 10; i++) {
[myArray addObject:[NSString stringWithFormat:#"i = %i", i]];
}
//Remove some elements =}
for (int i = (int)myArray.count - 1; i >= 0 ; i--) {
if(YES){
[myArray removeObjectAtIndex:i];
}
}