How to move an item on NSMutableArray? - objective-c

I want to move a string item to the top of the list.
NSMutableArray animal = "cat", "lion", "dog", "tiger";
How do I move dog to top of the list?

You would remove the item and insert it at the correct space:
id tmp=[[animals objectAtIndex:2] retain];
[animals removeObjectAtIndex:2];
[animals insertObject:tmp atIndex:0];
[tmp release];
You have to retain the object or when you tell the array to remove it, it will release the object.
If you don't know the index you could do something like this:
NSMutableArray* animals = [NSMutableArray arrayWithObjects:#"cat", #"lion", #"dog", #"tiger",nil];
for (NSString* obj in [[animals copy] autorelease]) {
if ([obj isEqualToString:#"dog"]) {
NSString* tmp = [obj retain];
[animals removeObject:tmp];
[animals insertObject:tmp atIndex:0];
break;
}
}
This method will go over all your list and search for "dog" and if it finds it will remove it from the original list and move it to index 0.

I would like to point out an inefficiency in your method. By removing/inserting an object from a NSMutableArray you potentially affect every row after the deletion/insertion. I say ‘potentially’ because it’s not clear what internal method Apple uses to maintain their mutable arrays. However, assuming it’s a simple c-array, then every row after that deletion/insertion index will be need to be moved down/up. In a very large array, this could be inefficient if the items moved are at the beginning. However, replacing items in an array are not inefficient at all. Thus the following is a category on NSMutableArray (note this code is under ARC, so no memory management):
- (void) moveObjectAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex{
if (fromIndex == toIndex) return;
if (fromIndex >= self.count) return; //there is no object to move, return
if (toIndex >= self.count) toIndex = self.count - 1; //toIndex too large, assume a move to end
id movingObject = [self objectAtIndex:fromIndex];
if (fromIndex < toIndex){
for (int i = fromIndex; i <= toIndex; i++){
[self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : [self objectAtIndex:i + 1]];
}
} else {
id cObject;
id prevObject;
for (int i = toIndex; i <= fromIndex; i++){
cObject = [self objectAtIndex:i];
[self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : prevObject];
prevObject = cObject;
}
}
}
Also, a small bonus to further increase functionality, if you're performing operations on the items moved (like updating a db or something), the following code has been very useful to me:
- (void) moveObjectAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex withBlock:(void (^)(id, NSUInteger))block{
if (fromIndex == toIndex) return;
if (fromIndex >= self.count) return; //there is no object to move, return
if (toIndex >= self.count) toIndex = self.count - 1; //toIndex too large, assume a move to end
id movingObject = [self objectAtIndex:fromIndex];
id replacementObject;
if (fromIndex < toIndex){
for (int i = fromIndex; i <= toIndex; i++){
replacementObject = (i == toIndex) ? movingObject : [self objectAtIndex:i + 1];
[self replaceObjectAtIndex:i withObject:replacementObject];
if (block) block(replacementObject, i);
}
} else {
id cObject;
id prevObject;
for (int i = toIndex; i <= fromIndex; i++){
cObject = [self objectAtIndex:i];
replacementObject = (i == toIndex) ? movingObject : prevObject;
[self replaceObjectAtIndex:i withObject:replacementObject];
prevObject = cObject;
if (block) block(replacementObject, i);
}
}
}

You can remove an existing element, e.g dog and then reinsert it at the beginning of the array.
NSMutableArray *animals = [NSMutableArray arrayWithObjects:#"cat", #"lion", #"dog", #"tiger",nil];
NSString *dog = #"dog";
// Check to see if dog is in animals
if ( [animals containsObject:dog] ) {
// Remove dog from animals and reinsert
// at the beginning of animals
[animals removeObject:dog];
[animals insertObject:dog atIndex:0];
}

Related

UITableView checking for detail view

I don't speak english very well so I hope that you understand.
I have to control my source data(an array of array from a csv) to see if it have the same element double, if this is true I want to insert only one of this in my List of TableView, but I have to store this, cause I need to know which of this element in the list must visualize a detail view. If the element is double in the source data I have to show the detail view, else nothing.
I tried a bit of method but I haven't found the solution, yet.
This is my code:
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *listaNonOrdinata = [[NSMutableArray alloc]init];
self.navigationItem.title = #"Tipologia";
NSString *fileString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Lista1" ofType:#"csv"] encoding:NSUTF8StringEncoding error:nil];
record = [fileString csvRows];
dettaglio = [[NSMutableArray alloc]init];
id doppio = nil;
for (int i=1; i < record.count; i++) {
for (int j=0; j < listaNonOrdinata.count; j++) {
doppio = [[record objectAtIndex:i] firstObjectCommonWithArray:listaNonOrdinata];
if (doppio == nil) {
[dettaglio addObject:[NSNumber numberWithBool:NO]];
} else {
[dettaglio addObject:[NSNumber numberWithBool:YES]];
}
}
if (doppio == nil) {
[listaNonOrdinata addObject:[[record objectAtIndex:i]objectAtIndex:0]];
}
}
//Ordino array in ordine alfabetico
lista = [[NSArray alloc]init];
lista = [listaNonOrdinata sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
[listaNonOrdinata release];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
Can everyone help me?
Thanks.
I'VE FOUND THE SOLUTION:
I had badly configured the array, now it works fine!

How do I parse through an array of objects in Objective-C?

Coming from C++, here's my question :
I have created objects of this type :
Size *one = [[Size alloc] initWithX: 3 andY: 1];
Size *two = [[Size alloc] initWithX: 4 andY: 7];
// etc...
Size *thirtythree = [[Size alloc] initWithX: 5 andY: 9];
( with a #property int x; & #property int y; for each object.. )
that I have stored in an array as follows :
NSArray *arrayOfSizes;
arrayOfSizes = [NSArray arrayWithObjects:one,two,three,four,five,six,
seven,eight,nine,ten,eleven,twelve,thirteen,
fourteen,fifteen,sixteen,seventeen,eighteen,
nineteen,twenty,twentyone,twentytwo,
twentythree,twentyfour,twentyfive,twentysix,
twentyseven,twentyeight,twentynine,thirty,
thirtyone,thirtytwo,thirtythree nil];
now I have a single object of type :
Myobject *myObject = [[Myobject alloc] initWithX: 5 andY: 3];
that also has a #property int x; & #property int y; ...
and I want to compare its values to the values of the objects found in the array, until I find an array object of similar values.. But I don't know how to do that in Obj-C. (in c++ I would simply use a vector v; with v.size(); and v[x]; ..etc... I suppose..)
here's what I'm looking for.. :)
while( !wholeOfArrayOfSizesChecked && !found)
{
if ( // x & y of object in array is equal to x & y of myObject )
{
found = YES;
}
else if( // whole of array checked)
{
wholeOfArrayOfSizesChecked = YES;
}
else
{
//move on to the next object of the array..
}
}
Thanks in advance for any help!
Well, you could just use fast enumeration on the array. Something like this:
Myobject *myObject = [[Myobject alloc] initWithX: 5 andY: 3];
for (Size *s in arrayOfSizes)
{
if (s.x == myObject.x && s.y == myObject.y)
{
// Found one
// Do something useful...
break;
}
}
Another one:
NSUInteger index = [arrayOfSizes indexOfObjectPassingTest:
^BOOL(Size *s, NSUInteger idx, BOOL *stop)
{
return (s.x == myObject.x) && (s.y == myObject.y);
}
];
if (index != NSNotFound) {
id object = [arrayOfSizes objectAtIndex:index];
}
Just to use your given structure. There are smarter ways of doing it though :)
wholeOfArrayOfSizesChecked = NO;
int currObj = 0
while( !wholeOfArrayOfSizesChecked && !found)
{
Size *current = (Size *)[arrayOfSizes objectAtIndex:i];
if (myObject.x == current.x && myObject.y == current.y)
{
found = YES;
}
else if(currObj == [arrayOfSizes count] -1 )
{
wholeOfArrayOfSizesChecked = YES;
}
else
{
currObj++;
}
}
Try something like this:
for (int i = 0; i < [arrayOfSizes size]; i++)
{
Size *current = (Size *)[arrayOfSizes objectAtIndex:i];
if (myObject.x == current.x && myObject.y == current.y)
{
// found
break;
}
}
How'bout a for-in loop?
for (Size *item in array) {
// compare 'item' to myObject
if (/* equal condition here */) break;
}
-(BOOL) isSize:(Size*)size equalToMyObject:(MyObject*)object{
return (size.x == object.x) && (size.y == object.y);
}
//In some method where you are checking it:
for (Size* size in arrayOfSizes){
if ([self isSize:size equalToMyObject:myObject]){
//You found it! They're equal!
break;
}
}

NSMutableArray removeAllObjects issue

I'm facing a problem with NSMutableArray that give me a Exc_Bad_Access without a reason.
I have a UITableView that contain around 700 records, i wanna to activate a filter process to it, i'm using the following method to filter the content of the UITableView:
- (void) filterTableContentWith:(int)_minValue andWith:(int)_maxValue {
[tableContent removeAllObjects];
tableContent = [[NSMutableArray alloc] initWithArray:originalTableContent copyItems:YES];
if (_minValue == 0 && _maxValue == 0) {
NSLog(#"This is mean that no filter is activate here");
} else {
for (int x = ([tableContent count] - 1); x >= 0; x--) {
if ([[[tableContent objectAtIndex:x] objectForKey:#"price"] intValue] < _minValue && [[[tableContent objectAtIndex:x] objectForKey:#"price"] intValue] > _maxValue) {
[tableContent removeObjectAtIndex:x];
}
}
}
NSLog(#"tableContent count = %#",[tableContent count]);
[self.tableView reloadData];
}
When i'm calling this method, it gives me Exc_Bad_Access on NSLog(#"tableContent count ...
I think that [tableContent removeAllObjects]; is releasing the array, but it's unreasonable.
Any help will be appreciated.
count returns an int, so change your NSLog to:
NSLog(#"tableContent count = %d",[tableContent count]);
and you'll be fine.
In addition to change your NSLog for count with %d, I think the following codes are better than removing contents within your loop:
...
} else {
NSPredicate* predicate = [NSPredicate predicateWithFormat:
#"price < %d AND price > %d", _minValue, _maxValue];
NSArray* array = [tableContent filteredArrayUsingPredicate:predicate];
tableContent = array;
}
...

View-based NSTableView filtering + animation

I have a view based NSTableView that I sometimes filter using NSPredicate. Is there any way to animate the items being removed/added/reordered throughout the tableview to have the same effect as beginUpdates, endUpdates and insertRowsAtIndexes:withAnimation, etc?
I've explored ways such as manually filtering out my array but my attempts proved to be futile so now I am wondering if there is a better (or built in way) to do this. I have wondered if NSArrayController does this automatically but I don't think it does.
I've written code to do this myself - given 'before' and 'after' arrays, compute the required parameters to insertRowsAtIndexPaths:, deleteRowsAtIndexPaths:, etc. The code is a bit fiddly so probably has bugs - use at your discretion!
#interface NSArray (ArrayDifference)
- (void) computeDifferenceTo:(NSArray *)newArray returningAdded:(NSMutableArray **)rowsAdded andDeleted:(NSMutableArray **)rowsDeleted;
#end
#implementation NSArray (ArrayDifference)
// Given two arrays that are expected have items added or removed but not re-ordered, compute the differences
// in a way usable for UITable insertRows and deleteRows
- (void) computeDifferenceTo:(NSArray *)newArray returningAdded:(NSMutableArray **)rowsAdded andDeleted:(NSMutableArray **)rowsDeleted
{
NSArray *oldArray = self;
*rowsAdded = [[[NSMutableArray alloc] init] autorelease];
*rowsDeleted = [[[NSMutableArray alloc] init] autorelease];
NSUInteger oldCount = [oldArray count];
NSUInteger newCount = [newArray count];
// Step through the two arrays
NSInteger oldIndex = 0, newIndex=0;
for (; newIndex < newCount && oldIndex < oldCount; )
{
id newItem = [newArray objectAtIndex:newIndex];
id oldItem = [oldArray objectAtIndex:oldIndex];
// If the two objects match, we step forward on both sides
if (newItem == oldItem) {
++newIndex;
++oldIndex;
}
else {
// Look for the old item to appear later in the new array, which would mean we have to add the rows in between
NSRange range = { newIndex+1, newCount - newIndex-1 };
NSUInteger foundIndex = [newArray indexOfObject:oldItem inRange:range];
if (foundIndex != NSNotFound)
for (; newIndex < foundIndex; ++newIndex)
[*rowsAdded addObject:[NSIndexPath indexPathForRow:newIndex inSection:0]];
else {
// Look for the new item to appear later in the old array, which would mean we have to remove the rows in between
NSRange range = { oldIndex+1, oldCount - oldIndex-1 };
NSUInteger foundIndex = [oldArray indexOfObject:newItem inRange:range];
if (foundIndex != NSNotFound)
for (; oldIndex < foundIndex; ++oldIndex)
[*rowsDeleted addObject:[NSIndexPath indexPathForRow:oldIndex inSection:0]];
else {
// Old item must be removed and new item added, then we carry on
[*rowsAdded addObject:[NSIndexPath indexPathForRow:newIndex++ inSection:0]];
[*rowsDeleted addObject:[NSIndexPath indexPathForRow:oldIndex++ inSection:0]];
}
}
}
}
// Once the loop is finished, add in what's left in the new array and remove what is left in the old array
for (; newIndex < newCount; ++newIndex)
[*rowsAdded addObject:[NSIndexPath indexPathForRow:newIndex inSection:0]];
for (; oldIndex < oldCount; ++oldIndex)
[*rowsDeleted addObject:[NSIndexPath indexPathForRow:oldIndex inSection:0]];
}
#end
Then you call it like this:
NSMutableArray *rowsAdded=nil, *rowsDeleted=nil;
[myArray computeDifferenceTo:newArray returningAdded:&rowsAdded andDeleted:&rowsDeleted];
[myTableView beginUpdates];
[myTableView insertRowsAtIndexPaths:rowsAdded withRowAnimation:UITableViewRowAnimationBottom];
[myTableView deleteRowsAtIndexPaths:rowsDeleted withRowAnimation:UITableViewRowAnimationFade];
[myTableView endUpdates];

Problem with the duplicate values picking from Address book in Iphone sdk

Here I had a problem that I am adding contact from the address book and checking it whether it is already in the favourites list or not.If not I am adding the contact to favourite list.
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
ContactDTO* dtoObject = [[ContactDTO alloc] init];
ABRecordID personId = ABRecordGetRecordID(person);
NSString* personIdStr = [NSString stringWithFormat:#"%d", personId];
dtoObject.contactId = personIdStr;
NSString *lastNameString, *firstNameString;
firstNameString = [self getValueForProperty:kABPersonFirstNameProperty forContact:personIdStr];
lastNameString = [self getValueForProperty:kABPersonLastNameProperty forContact:personIdStr];
dtoObject.firstName = firstNameString;
dtoObject.lastName = lastNameString;
printf("\n *****************firstNameString %s",[firstNameString UTF8String]);
//ABMultiValueRef emailMultiValue =[(NSString *)ABRecordCopyValue(person, kABPersonEmailProperty) autorelease];
ABMultiValueRef phoneMultiValue =[(NSString *)ABRecordCopyValue(person, kABPersonPhoneProperty) autorelease];
if (ABMultiValueGetCount(phoneMultiValue) > 0)
{
ABMultiValueRef phoneMultiValue =[(NSString *)ABRecordCopyValue(person, kABPersonPhoneProperty) autorelease];
NSString* curentTypeLabel =[(NSString *)ABMultiValueCopyLabelAtIndex(phoneMultiValue,identifier) autorelease];
curentTypeLabel = [curentTypeLabel stringByReplacingOccurrencesOfString:#"_$!<" withString:#""];
curentTypeLabel = [curentTypeLabel stringByReplacingOccurrencesOfString:#">!$_" withString:#""];
dtoObject.numberType = curentTypeLabel;
NSString* currentPhone = [(NSString *)ABMultiValueCopyValueAtIndex(phoneMultiValue,identifier) autorelease];
dtoObject.mobNumber = currentPhone;
FavoritesAppDelegate* appDelegate = (FavoritesAppDelegate*) [[UIApplication sharedApplication] delegate];
if ([favoritesArray count] > 0)
{
for (int i=0; i< [favoritesArray count]; i++)
{
ContactDTO* dtoObject1 = [favoritesArray objectAtIndex:i];
printf("\n dtoObject1.contactId value = %s, Main value = %s",[dtoObject.firstName UTF8String],[dtoObject1.firstName UTF8String]);
printf("\n dtoObject1.mobNumber value = %s, Main mobNumber value = %s",[dtoObject1.mobNumber UTF8String],[dtoObject.mobNumber UTF8String]);
if ([dtoObject.firstName isEqualToString:dtoObject1.firstName])
{
printf("\n inside if....");
}
else
{
[appDelegate addContactToFavorites:dtoObject];
break;
printf("\n inside else....");
}
}
}
else
{
[appDelegate addContactToFavorites:dtoObject];
}
[self dismissModalViewControllerAnimated:YES];
}
/*else if(ABMultiValueGetCount(emailMultiValue) > 0)
{
NSString* currentEmail =(NSString *)ABMultiValueCopyValueAtIndex(emailMultiValue,identifier);
printf("\n *************currentEmail** %s",[currentEmail UTF8String]);
[self emailBtnAction:currentEmail];
}*/
[dtoObject release];
return NO;
}
For that the code I written was shown as above.
Eventhough I am getting the duplicate values the condition is failed and duplicate values are added.
Can anyone give suggestions to get rid of this?
Anyone's help will be much appreciated.
Thanks to all,
Monish.
You are adding to favorites array if the new contact doesn't match ONE of the existing items in the favorites array. But the new contact could be in the favorites array further along in the array. You need to check if the new contact doesn't match ALL of the existing favorites.
Also, shouldn't the match condition be more than just checking first names?
Something like this:
BOOL contactAlreadyExists = NO;
for (int i=0; i< [favoritesArray count]; i++)
{
ContactDTO* dtoObject1 = [favoritesArray objectAtIndex:i];
if ([dtoObject.firstName isEqualToString:dtoObject1.firstName])
{
contactAlreadyExists = YES;
break;
}
}
if (!contactAlreadyExists)
{
[appDelegate addContactToFavorites:dtoObject];
}