I need to check if a string in my array is empty using the NSComparisonPredicate. The predicate that is being executed is: string MATCHES[c] ""
But nothing shows up in the results, it does not list my empty strings.
Is my predicate wrong or there is just another way of dealing with empty strings with NSPredicate?
I'm not sure why you're using NSComparisonPredicate ,I've never used that one, so I'm not familiar with it. Have you tried predicateWithFormat?
NSPredicate *pred = [NSPredicate predicateWithFormat:#"self.length == 0"];
It's not clear how you want to use the predicate, but this should work if you're using it to filter an array.
If you want to know the indexes of strings in your array that are empty, then you could use indexesOfObjectsPassingTest: like so:
NSIndexSet *indxs = [array indexesOfObjectsPassingTest:^BOOL(NSString *aString, NSUInteger idx, BOOL *stop) {
return aString.length == 0;
}];
NSLog(#"%#",indxs);
As rdelmar mentions above, for such a task you should just check the length of the string by string.length==0
You can filter your array using blocks. I would recommend that approach against using NSPredicate. Here is some sample code:
//creating array
NSArray *myArray = [NSArray arrayWithObjects:#"a", #"b", #"", #"c", nil];
//filtering
NSIndexSet *iset = [myArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSString *str = obj;
return !str.length;
}
];
//creating my result array with empty strings
NSArray *emptyStrings = [myArray objectsAtIndexes:iset];
//logging all strings
for (NSString *str in myArray) {
NSLog(#"string: %#", str);
}
//logging empty strings
for (NSString *str in emptyStrings) {
NSLog(#"empty string: %#", str);
}
EDIT:
If you really need to use NSComparisonPredicate, here it is:
NSExpression *left = [NSExpression expressionForKeyPath:#"length"];
NSExpression *right = [NSExpression expressionForConstantValue:[NSNumber numberWithInt:0]];
NSComparisonPredicateModifier modifier = NSDirectPredicateModifier;
NSPredicateOperatorType operator = NSEqualToPredicateOperatorType;
NSPredicate *predicate = [NSComparisonPredicate predicateWithLeftExpression:left rightExpression:right modifier:modifier type:operator options:0];
NSArray *filtered = [myArray filteredArrayUsingPredicate:predicate];
Related
I am looking for a predicate to fetch all managed objects of type Entity whose values are duplicated in a property sessionId, where all groups' ("groups", meaning managed objects whose sessionId's are equal) contents' flags in a property processed is set to YES. This can be done (slowly), but I am looking for an efficient one liner for this. Thanks
This is the slow way:
NSFetchRequest *request = [NSEntityDescription entityForName:#"Entity"
inManagedObjectContext:context];
NSArray *all = [context executeFetchRequest:request error:nil];
NSArray *sessionIds = [all valueForKeyPath:#"#distinctUnionOfObjects.sessionId"];
NSMutableArray *objects = [NSMutableArray array];
for (NSString *sessionId in sessionIds) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"sessionId == %#", sessionId];
NSArray *inSession = [all filteredArrayUsingPredicate:predicate];
for(id obj in inSession) {
if(![obj valueForKey:#"processed"]) continue;
}
[objects arrayByAddingObjectsFromArray:processed];
}
NSLog(#"%#", objects);
Since booleans are stored as 0s and 1s, a group where all rows have processed = YES will have average(processed) = 1. Hence you can use NSFetchRequest's propertiesToGroupBy and havingPredicate to get the sessionIds that meet your criteria. A second fetch is then required to get the Entity objects with any of those sessionIds:
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"Entity"];
fetch.resultType = NSDictionaryResultType;
fetch.propertiesToFetch = #[#"sessionId"];
fetch.propertiesToGroupBy = #[#"sessionId"];
fetch.havingPredicate = [NSPredicate predicateWithFormat: #"average:(processed) == 1"];
NSArray *resultsArray = [context executeFetchRequest:fetch error:nil];
NSArray *sessionIdArray = [resultsArray valueForKeyPath:#"sessionId"];
NSFetchRequest *newFetch = [NSFetchRequest fetchRequestWithEntityName:#"Entity"];
newFetch.predicate = [NSPredicate predicateWithFormat:#"name IN %#",sessionIdArray];
NSArray *finalResults = [context executeFetchRequest:newFetch error:nil];
NSLog(#"Final results, %#", finalResults);
Sorry it's not a one-liner. And I leave it to you to determine whether it's any quicker than your own code.
EDIT
To do it all in one fetch, use NSFetchRequestExpression in place of the intermediate arrays:
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"Entity"];
fetch.resultType = NSDictionaryResultType;
fetch.propertiesToFetch = #[#"sessionId"];
fetch.propertiesToGroupBy = #[#"sessionId"];
fetch.havingPredicate = [NSPredicate predicateWithFormat: #"average:(processed) == 1"];
NSExpression *fetchExpression = [NSFetchRequestExpression expressionForFetch:[NSExpression expressionForConstantValue:fetch] context:[NSExpression expressionForConstantValue:context] countOnly:false];
NSFetchRequest *newFetch = [NSFetchRequest fetchRequestWithEntityName:#"Entity"];
newFetch.predicate = [NSPredicate predicateWithFormat:#"sessionId IN %#",fetchExpression];
NSArray *finalResults = [context executeFetchRequest:newFetch error:nil];
NSLog(#"Final results, %#", finalResults);
Note that on my (admittedly trivial) test setup this actually ran more slowly than the two-fetch solution.
FYI, if you use the SQLDebug build setting to examine the SQL that is generated, it looks something like this:
SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZSESSIONID, t0.ZPROCESSED FROM ZENTITY t0 WHERE t0.ZSESSIONID IN (SELECT n1_t0.ZSESSIONID FROM ZENTITY n1_t0 GROUP BY n1_t0.ZSESSIONID HAVING avg( n1_t0.ZPROCESSED) = ? )
I tried to add object to my mutable array using TFHlle parse html in this code but it return null for object in my array, but in the for loop, I log the result of '[element objectForKey:#"title"]' and it returns result I want.
How I can add the result of element [objectForKey:#"title"] to my array?
TFHpple *htmlParseResult = [TFHpple hppleWithHTMLData:self.responseData];
NSString *coursesXpathQueryString = #"//h2[#class='main']/a";
NSArray *coursesNodes = [htmlParseResult searchWithXPathQuery:coursesXpathQueryString];
NSMutableArray *fitCourse = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in coursesNodes) {
[fitCourse addObject:[element objectForKey:#"title"]];
}
I think
NSArray *coursesNodes = [htmlParseResult searchWithXPathQuery:coursesXpathQueryString];
should be change to:
NSArray *coursesNodes = [NSArray arrayWithArray:[htmlParseResult searchWithXPathQuery:coursesXpathQueryString]];
I am not sure this works but you can try.
I am trying to delete the value of some Attributes in two linked Entities. Here is the code I am using:
+ (MeetPhoto *)deletePhotoForSelectedMeet:(MarksFromMeets *)specificMeet
inManagedObjectContext:(NSManagedObjectContext *)context;
{
MeetPhoto *results = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"MarksFromMeets"];
request.predicate = [NSPredicate predicateWithFormat:#"uniqueResultID = %#", specificMeet.uniqueResultID];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"uniqueResultID" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (!matches || [matches count] > 1) {
NSLog(#"There has been an error in matches in %#: \n%#.\n matches count = %lu",
NSStringFromClass([self class]),
NSStringFromSelector(_cmd),
(unsigned long)[matches count]);
} else if ([matches count] == 0){
NSLog(#"There were no matches in %#: \n%#.\n matches count = %lu",
NSStringFromClass([self class]),
NSStringFromSelector(_cmd),
(unsigned long)[matches count]);
} else {
if (debug == 1) NSLog(#"In %# and matches = %lu",NSStringFromClass([self class]),(unsigned long)[matches count]);
for (MarksFromMeets* meetEntity in matches) {
if (debug == 1) NSLog(#" MarksFromMeet = %# \n thumbnail = %# \n photo = %#",meetEntity.meetName,meetEntity.photoThumbNail,meetEntity.whichPhoto.photo);
[context deleteObject:meetEntity.photoThumbNail];
[context deleteObject:meetEntity.whichPhoto.photo];
NSError *mySavingError = nil;
[meetEntity.managedObjectContext save:&mySavingError];
if (mySavingError) NSLog(#"In %# and mySavingError is %#",NSStringFromClass([self class]), mySavingError);
}
}
return results;
}
Where whichPhoto is the title of the link between the two Core Data Entities.
Here is NSLog Statement:
2014-08-13 14:40:45.334 ITrackXC[12054:60b] thisMeetsPhoto should equal nil and equals (null)
2014-08-13 14:41:37.095 ITrackXC[12054:60b] In MeetPhotoViewController and executing imagePickerController:didFinishPickingImage:editingInfo:
2014-08-13 14:41:37.583 ITrackXC[12054:60b] In MeetPhoto and matches = 1
2014-08-13 14:41:37.589 ITrackXC[12054:60b] whichMeetPhotographed = <MarksFromMeets: 0x1700c5be0> (entity: MarksFromMeets; id: 0xd000000000300004 <x-coredata://ED56838C-9F27-451D-97CA-01AF0FC38830/MarksFromMeets/p12> ; data: {
athleteGrade = "12th Grade";
event = "5000 Meters";
eventPR = 1;
eventSB = 1;
markInEvent = "19:03";
meetDate = "2013-11-02 07:00:00 +0000";
meetID = 78103;
meetName = "OSAA 3A/2A/1A State Championships";
photoThumbNail = "<UIImage: 0x170281b80>";
placeInEvent = 2;
raceSorter = 5000;
seasonName = 2013;
sortMark = 1143;
standardOrMetric = nil;
uniqueResultID = 10032696;
whichPhoto = "0x178236d80 <x-coredata:///MeetPhoto/t9C5E481D-5911-47C9-AA8B-6015AAD1C23C2>";
whoRan = "0xd000000000080000 <x-coredata://ED56838C-9F27-451D-97CA-01AF0FC38830/AthleteInfo/p2>";
})
photo = <UIImage: 0x17009b440>
thumbnail = <UIImage: 0x170281b80>
2014-08-13 14:41:37.599 ITrackXC[12054:60b] NSManagedObjects did change.
2014-08-13 14:41:39.444 ITrackXC[12054:60b] NSManagedContext did save.
I do not get any errors. However when I look at the TableView where the data is displayed, the photos are still part of the record.
I can get rid of the thumbnail and associated photo by setting them to "nil". Is there any problem with this approach?
Setting attribute values to nil is how you're supposed to do this, so no, there's no problem with it. The deleteObject: method is only for when you want to delete a managed object. Using it in other cases might not crash, but it also won't have the effect you're aiming for.
In the case of the photo, you might need to delete an object, but it's hard to tell from your code. You seem to want to get rid of meetEntity.whichPhoto.photo. I don't know what whichPhoto is; if it's a managed object, you might want to call deleteObject on myEntity.whichPhoto to get rid of it. If nobody else is using it, that is.
I have an NSMutableArray that contains NSIndexPath objects, and I'd like to sort them by their row, in ascending order.
What's the shortest/simplest way to do it?
This is what I've tried:
[self.selectedIndexPaths sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSIndexPath *indexPath1 = obj1;
NSIndexPath *indexPath2 = obj2;
return [#(indexPath1.section) compare:#(indexPath2.section)];
}];
You said that you would like to sort by row, yet you compare section. Additionally, section is NSInteger, so you cannot call methods on it.
Modify your code as follows to sort on the row:
[self.selectedIndexPaths sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSInteger r1 = [obj1 row];
NSInteger r2 = [obj2 row];
if (r1 > r2) {
return (NSComparisonResult)NSOrderedDescending;
}
if (r1 < r2) {
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}];
You can also use NSSortDescriptors to sort NSIndexPath by the 'row' property.
if self.selectedIndexPath is non-mutable:
NSSortDescriptor *rowDescriptor = [[NSSortDescriptor alloc] initWithKey:#"row" ascending:YES];
NSArray *sortedRows = [self.selectedIndexPaths sortedArrayUsingDescriptors:#[rowDescriptor]];
or if self.selectedIndexPath is a NSMutableArray, simply:
NSSortDescriptor *rowDescriptor = [[NSSortDescriptor alloc] initWithKey:#"row" ascending:YES];
[self.selectedIndexPaths sortedArrayUsingDescriptors:#[rowDescriptor]];
Simple & short.
For a mutable array:
[self.selectedIndexPaths sortUsingSelector:#selector(compare:)];
For an immutable array:
NSArray *sortedArray = [self.selectedIndexPaths sortedArrayUsingSelector:#selector(compare:)]
In swift:
let paths = tableView.indexPathsForSelectedRows() as [NSIndexPath]
let sortedArray = paths.sorted {$0.row < $1.row}
I'm making an app in which i want to access the contact's first name and store them in to nsmutable array so that i can get the values of that array like array[0] up to array[i-1] and print them in table view.Here is my code:
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (addressBook != nil)
{
contacts_Image_List=[[NSMutableArray alloc]init];
NSLog(#"Succesful.");
NSArray *allContacts = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
NSUInteger i = 0;
for (i = 0; i < [allContacts count]; i++)
{
Person *person = [[Person alloc] init];
ABRecordRef contactPerson = (__bridge ABRecordRef)allContacts[i];
NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(contactPerson, kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString *)ABRecordCopyValue(contactPerson, kABPersonLastNameProperty);
NSString *fullName = [NSString stringWithFormat:#"%# %#", firstName, lastName];
person.firstName = firstName;
person.lastName = lastName;
person.fullName = fullName;
// person.userThumb[i]=firstName;
//[person.userThumb[i] addObject:#"firstName"];
//above line gives null
NSLog(#"%#",person.userThumb[i]);
my mutable array is in Person.h class.
On each iteration just do:
[person.yourMutableArray addObject:fullName]
Just ensure that your MutableArray has already been allocated, and that you only allocate it once.