I want to shuffle/randomize the order of the items inside my NSMutableArray when method reloadData is executed. I tried the below, but console keeps throwing me the following error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[__NSArrayI
exchangeObjectAtIndex:withObjectAtIndex:]: unrecognized selector sent
to instance 0x1748b2c60'
Any idea why this might be? I'm stumped.
ViewController.h
#property (strong, retain) NSMutableArray *neighbourData;
ViewController.m
- (void)reloadData:(id)sender
{
NSMutableDictionary *viewParams = [NSMutableDictionary new];
[viewParams setValue:#"u000" forKey:#"view_name"];
[DIOSView viewGet:viewParams success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.neighbourData = (NSMutableArray *)responseObject;
[self.tableView reloadData];
NSUInteger count = [self.neighbourData count];
for (NSUInteger i = 0; i < count; ++i) {
// Select a random element between i and end of array to swap with.
int nElements = count - i;
int n = (arc4random() % nElements) + i;
[self.neighbourData exchangeObjectAtIndex:i withObjectAtIndex:n];
}
NSLog(#"This is the neighbourdata %#",self.neighbourData);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
The error indicates that responseObject is actually a non-mutable NSArray. The cast you applied simply lies to the compiler but it doesn't actually change anything at runtime.
Change:
self.neighbourData = (NSMutableArray *)responseObject;
to:
self.neighbourData = [(NSArray *)responseObject mutableCopy];
For production you really ought to use the built in Fisher-Yates shuffle in Gamekit. If you are doing this for leaning purposes then the problem is in the line:
int n = (arc4random() % nElements) + i;
You are making a random number from the first to the last element and then you are adding i to it. Clearly this means that your index can now be out of bounds. Get rid of the + i.
self.neighbourData = (NSMutableArray *)responseObject;
You have to make sure that your responseObject is an instance of NSMutableArray here. Even though you cast the type responseObject to NSMutableArray, it will crash if it's not an instance of NSMutableArray because it dose not have a exchangeObjectAtIndex:withObjectAtIndex:. In this case your responseObject is a NSArray instance, your could change the code to:
NSArray *tmp = (NSArray *)responseObject;
self.neighbourData = [tmp mutableCopy];
I think this works for you.
Related
I'm trying to check if NSString 'testing' (47) exists inside of my NSMutableArray 'self.checkfriendData'. I'm using the code below, though after logging my if statement it appears as though it's never executed (even though the statement is true - see console data below, uid = 47, and thus hiding my object should fire?) Any idea as to why this isn't working? Help is much appreciated!
ViewController.m
NSMutableDictionary *viewParams3 = [NSMutableDictionary new];
[viewParams3 setValue:#"accepted_friends" forKey:#"view_name"];
[DIOSView viewGet:viewParams3 success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.checkfriendData = (NSMutableArray *)responseObject;
NSString *testing = #"47";
NSArray *friendorNo = self.checkfriendData;
if ([friendorNo containsObject:testing]) // YES
{
self.addFriend.hidden = YES;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
Here's what's inside self.checkfriendData:
2017-05-18 19:36:07.266529-0700 This is the friend data check (
{
body = "My name is Britt";
friendphoto = "/sites/default/files/stored/x.jpg";
"node_title" = "Britt";
uid = 47;
}
)
It appears that your NSArray contains NSDictionarys and you are asking if the array contains an NSString. The answer will always be no as the array doesn't directly contain any NSStrings.
If you want to search for the uid of 47 you will have to iterate over the array and check the uid key of each NSDictionary for the value 47.
The code for this would look something like:
for (NSDictionary *dict in friendorNo) {
if ([dict[#"uid"] isEqualToString:testing]) {
self.addFriend.hidden = YES;
}
}
Hi in my application I am setting the value for NSManagedObject while I am trying to set a value app is crashing.Here is the code and error message.
NSManagedObject *object3 = [threadManagedObjectContext objectWithID:[object1 objectID]] ;
for (int i=0;i<[array1 count];i++)
{
NSDictionary *keyValue=[array1 objectAtIndex:i];
[object3 setValue:[[keyValue allValues] lastObject] forKey:[[keyValue allKeys] lastObject]] ;
}
Error: Terminating app due to uncaught exception 'NSGenericException', reason:was mutated while being enumerated
Can any one please help me.
In your code (not the provided) , you're changing a collection while looping/iterating.
Example (not allowed):
for (MyClass *myClassObj in collectionOfMyClass) {
[myClassObj setClassVar: aVar]
}
Solution:
Make a temporary collection of the objects you want to set. Set all of them back outside of your loop.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
for (MyClass *myClassObj in collectionOfMyClass) {
[tempArray addObject:aVar];
}
[myClassObj setClassVars: tempArray];
You can try below solution for updating the object...
NSManagedObject *object3 = [threadManagedObjectContext objectWithID:[object1 objectID]] ;
int i=0;
for (NSDictionary *keyValue in array1)
{
[[object3 setValue:[[keyValue allValues] lastObject] forKey:[[keyValue allKeys] lastObject]] ;i++;
}
NSError *error;
bool result = [[fetchedResultsController threadManagedObjectContext] save:&error];
if (!result) {
NSLog(#" error saving context, %#, %#", error, error.userInfo);
}
In your code while Looping/Enumerating you are setting value..So it is crashing...!
Hope it helps you....
I have this code, but when I log the mediaDictionaryArray, I get null. Does the receiver array have to be initialized with a value first or can I add objects to an empty array? Does [NSArray array] vs. [[NSArray alloc]init] have anything to do with it?
Adding dictionary from API call that happens i times. Asynch call will return the dictionary - can't be sure if NSMutableArray will work in catchJSONArray since asynch nature of call will make the array of indeterminate size which will make it hard to use later on.
Updated with relevant bit.
for (int i = 0; i<[array count]; i++) {
NSString *getString = array[i];
NSLog(#"getstring %#", getString);
[client GET:getString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
if (httpResponse.statusCode == 200) {
dispatch_async(dispatch_get_main_queue(), ^{
_locationMediaArray = (NSArray*)responseObject[#"data"];
[self catchJSONArray:_locationMediaArray];
then here is method with the array issue
-(void)catchJSONArray:(NSArray*)array{
NSArray *catchJSONArray = [NSArray array];
_mediaDictionaryArray = [catchJSONArray arrayByAddingObjectsFromArray:array];
NSLog(#"mediaDictionaryArray %#", _mediaDictionaryArray);
}
arrayByAddingObjectsFromArray returns a new array containing your objects, as an NSArray can not be changed once created.
If you want to change an existing array, you should be using an NSMutableArray.
The best way you could do this is:
_mediaDictionaryArray=[NSArray arrayWithArray:otherArray];
That will create a new array with the contents of otherArray and assign it to _mediaDictionary.
No need to alloc init your array just pass the refrence of your other array. If
_mediaDictionaryArray is mutable array then use below:-
_mediaDictionaryArray=[array mutableCopy];
If it is non mutable array then use below
_mediaDictionaryArray=[array copy];
I have an NSArray of Object Classes which consist of two textfields. I would like to sort these objects in ascending order, I have done this with NSDictionary objects before however I have now changed then to an Object Class that I have made so I dont really know how to compare the values to get the sorted array.
The object variables are NSNumbers but contain only number values, which I think will effect things.
This is how I was sorted the NSDictionary value with my old code.
NSArray *tempSortedItemsArray = [installArray sortedArrayUsingDescriptors:
#[[NSSortDescriptor
sortDescriptorWithKey:#"first" ascending:YES],
[NSSortDescriptor
sortDescriptorWithKey:#"second" ascending:YES]]];
sortedItemsArray = [tempSortedItemsArray mutableCopy];
tempSortedItemsArray = nil;
So if I have an array of object like this
(first / second)
2 1
0 0
1 0
1 1
2 2
2 0
3 0
if would sort like this
0 0
1 0
1 1
2 0
2 1
2 2
3 0
any help adjusting this for NSObject class with NSNumber variables first and second would be greatly appreicated.
[arr sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSString *first=[NSString stringWithFormat:#"%d%d",obj1.num1.intValue,obj1.num2.intValue];
NSString *second=[NSString stringWithFormat:#"%d%d",obj2.num1.intValue,obj2.num2.intValue];
return [first compare:second options:NSNumericSearch];
}];
You can use the other sort methods of NSArray such as:
sortedArrayUsingFunction:context:
sortedArrayUsingSelector:
sortedArrayUsingComparator:
Given an Object that works like this:
#interface Object : NSObject
#property (copy) NSNumber *first;
#property (copy) NSNumber *second;
+(Object *)objectWithWithFirst:(NSNumber *)first second:(NSNumber *)second;
#end
#implementation Object
+(Object *)objectWithWithFirst:(NSNumber *)first second:(NSNumber *)second {
Object *object = [[Object alloc] init];
object.first = first;
object.second = second;
return object;
}
-(NSString *)description {
return [NSString stringWithFormat:#"%# %#", _first, _second];
}
#end
Your could sort with
NSArray *sorted = [array sortedArrayUsingComparator:^NSComparisonResult(Object *obj1, Object *obj2) {
NSComparisonResult result = [obj2.second compare:obj2.second];
if (result == NSOrderedSame)
result = [obj1.first compare:obj2.first];
return result;
}];
Example:
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSArray *array = #[
[Object objectWithWithFirst:#2 second:#1],
[Object objectWithWithFirst:#0 second:#0],
[Object objectWithWithFirst:#1 second:#0],
[Object objectWithWithFirst:#1 second:#1],
[Object objectWithWithFirst:#2 second:#2],
[Object objectWithWithFirst:#2 second:#0],
[Object objectWithWithFirst:#3 second:#0]
];
NSLog(#"%#", array);
NSArray *sorted = [array sortedArrayUsingComparator:^NSComparisonResult(Object *obj1, Object *obj2) {
NSComparisonResult result = [obj2.second compare:obj2.second];
if (result == NSOrderedSame)
result = [obj1.first compare:obj2.first];
return result;
}];
NSLog(#"%#", sorted);
}
return 0;
}
Which will work perfectly on your sample data. Of course, you could put the comparator code directly into your Object class - cleaning thins up even more.
If your custom class has the properties first and second then the code you've posted will just work.
NSNumbers already know how to compare themselves to other numbers. NSSortDesvriptors work with any object that implements the reading part of key-value coding. All NSObject subclasses automatically do so for any property that follows ordinary conventions. #propertys follow ordinary conventions.
I am new to iOS development, I encounter error when replaceObjectAtIndex. Any wrong with my codes? Please help.Thanks.
self.myArray =array;
for (NSDictionary *data in array) {
NSString *fbid = [data objectForKey:#"id"];
for (int index = 0; index < self.myPersonArray.count; index ++) {
for (IP_PERSON *person in self.myPersonArray) {
if ([person.UserDef2 isEqualToString:fbid]) {
[self.myArray replaceObjectAtIndex:index withObject:person];
break;
}
}
}
Error is :
Terminating app due to uncaught exception NSGenericException, reason: '*** Collection <__NSArrayM: 0xa34f6c0> was mutated while being enumerated.
You cannot use fast enumeration and mutate collection at the same time, hence the error message. You can resort to using an usual for-loop.
You're iterating over array, which is equal to self.myArray.
Further down, you're editing this array when you do: [self.myArray replaceObjectAtIndex:index withObject:person];
To resolve this, just make self.array a mutableCopy of the original array:
self.myArray = [array mutableCopy];
You can take another temporary array and iterate over that array, so you are enumerating and mutating different array.
NSArray *tempArray = [yourArray copy];
for (IP_PERSON *person in tempArray) {
if ([person.UserDef2 isEqualToString:fbid]) {
[self.myArray replaceObjectAtIndex:index withObject:person];
break;
}
}
[tempArray release];
Alternatively you can iterate without an enumerator, you can use regular for loop with starting index, exit condition and increment as you have done in outer loop.
You can..
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]
self.myArray =array;
for (NSDictionary *data in array) {
NSString *fbid = [data objectForKey:#"id"];
for (int index = 0; index < self.myPersonArray.count; index ++) {
for (IP_PERSON *person in self.myPersonArray) {
if ([person.UserDef2 isEqualToString:fbid]) {
[dictionary setObject:person forKey:#(index)]; //Notice this line
break;
}
}
}
}
And then..
for(id key in dictionary) {
[self.myArray replaceObjectAtIndex:[key intValue] withObject:[dictionary objectForKey:key]];
}