Core Data / Range Update - ios

Here is a simple mySQL request :
UPDATE myTable SET X=X-1 WHERE (X>100)
Now my question goes like this.
How would I write a similar request in Core Data ?
I am starting to find out my way in Core Data; but the last part of the above SQL order gives me problems in Core Data.
In other word : how do I handle the WHERE (X>100) part.
If the mySQL request concerned one record only, I would use some thing like this :
[matchItem setValue:VALUE forKey:#"X"];
But how do I do if is a range of records, like here, where I want to perform some actions for all records where X>100.

Use an NSPredicate and add it to your NSFetchRequest.
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"X > %#", [NSNumber numberWithInt:100]];
Iterate through your results array and set the value as appropriate.
for (NSManagedObject *match in resultsArray) {
NSInteger newValue = [[match objectForKey:#"X"] intValue] +1;
[match setValue:[NSNumber numberWithInt:newValue] forKey:#"X"];
}

Related

use NSPredicate to filter object

Here say I have a array of objects with two attributes:
// array of object
NSArray *objects
// object
NSString *primaryTag;
NSArray *secondaryTag;
Since what I want is when the this object contains the givenTag, it could be passed to a new array called results;
Here is my codes:
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"primaryTag == %# || secondaryTag CONTAINS[c] %#", givenTag, givenTag];
results = [objects filteredArrayUsingPredicate:resultPredicate];
It seems that the primaryTag works well, but the secondaryTag doesn't work, can someone help me out. I am not that familiar with NSPredicate filtering. Thanks in advance.
The most efficient way to do that is with a NSCompoundPredicate like so:
NSArray *subPredicates = #[tag1, tag2, tag3];
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Your question is a little unclear so you might also want:
andPredicateWithSubpredicates
Depending on exactly what the nature of the result set you are looking for.
See Apple Docs here: NSCompoundPredicate Docs
i implemented the following custom class:
#interface CustomObject : NSObject
#property (copy, nonatomic) NSString *primaryTag;
#property (strong, nonatomic) NSArray *secondaryTag;
#end
and overrode it's description method for the NSLog statement to print something we understand:
- (NSString *)description {
return [NSString stringWithFormat:#"primaryTag: %#, secondaryTag: %#", _primaryTag, [_secondaryTag componentsJoinedByString:#", "]];
}
then i created some objects from the custom class and added them to an array:
NSMutableArray *objects = [NSMutableArray array];
CustomObject *obj1 = [CustomObject new];
obj1.primaryTag = #"stringToSearchFor";
obj1.secondaryTag = #[#"notTheStringToSearchFor", #"somethingElse"];
[objects addObject:obj1];
CustomObject *obj2 = [CustomObject new];
obj2.primaryTag = #"differentString";
obj2.secondaryTag = #[#"nothingWeAreLookingFor"];
[objects addObject:obj2];
CustomObject *obj3 = [CustomObject new];
obj3.primaryTag = #"anotherOne";
obj3.secondaryTag = #[#"whoCaresForThisString", #"stringToSearchFor"];
[objects addObject:obj3];
finally i created a string to search for and the predicate:
NSString *givenTag = #"stringToSearchFor";
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"primaryTag == %# || secondaryTag CONTAINS[c] %#", givenTag, givenTag];
when i log out the result i get the correct results:
NSLog(#"%#", [objects filteredArrayUsingPredicate:resultPredicate]);
logs:
(
"primaryTag: stringToSearchFor, secondaryTag: notTheStringToSearchFor, somethingElse",
"primaryTag: anotherOne, secondaryTag: whoCaresForThisString, stringToSearchFor"
)
which is obj1 and obj3. correct! if it does not work for you there's gotta be something else wrong with your code...
If my understanding of the original question is incorrect, please let me know, and I will adjust my answer.
Problem: You have an array of objects with 2 properties. One is primaryTag, which is a string. The second is an array of secondaryTags, which is a collection of strings. You want to filter all objects where either the primaryTag matches, or where the search string matches one of the secondaryTags.
Answer The proper way to match strings is via MATCHES or CONTAINS.
NSPredicate *pPredicate =
[NSPredicate predicateWithFormat:#"%K CONTAINS[cd] %#",
#"primaryTag", searchString];
NSPredicate *sPredicate =
[NSPredicate
predicateWithFormat:#"SUBQUERY(%K, $st, $st CONTAINS[cd] %#).#count > 0",
#"secondaryTags", searchString];
NSCompoundPredicate *searchPredicate =
[NSCompoundPredicate orPredicateWithSubPredicates:#[ pPredicate, sPredicate ]];
How it works: The first predicate is a straightforward match. You can replace CONTAINS with MATCHES, if that better fits the kind of comparison you wish to make. The [cd] suffix means case-insensitive and diacritic-insensitive. It's normal to include those when searching/filtering, but again, it's up to you. Instead of embedding the property name in the predicate format string, I use %K and a replacement parameter. In production code, that replacement parameter would be a constant.
The second predicate is a little trickier. It uses a SUBQUERY() to filter the secondaryTags array, and returns the object as matching if at least one secondary tag matches the search string. SUBQUERY() is a function with 3 parameters. The first is the collection being searched. The second is a temporary variable that represents each item in the collection, in turn; it is used in the 3rd parameter. The 3rd parameter is a regular predicate. Each item in the collection that matches the filter is included in the output of SUBQUERY(). At the end, the matching secondary tags are counted (via #count), and if the count is greater than zero, the original object is considered to have matched, so will be included in the filtered output.
Finally, we combine these two predicates into one searchPredicate, which can now be used to filter your array of objects.
I seen this issue,
My normal approch is to use the NSPredicate twice,
So that I can track the result at every steps:
Option 1:
NSPredicate *resultPredicate1 = [NSPredicate predicateWithFormat:#"primaryTag == %#", givenTag];
results1 = [objects filteredArrayUsingPredicate:resultPredicate1];
NSPredicate *resultPredicate2 = [NSPredicate predicateWithFormat:#"secondaryTag CONTAINS[c] %#", givenTag];
finalResults = [results1 filteredArrayUsingPredicate:resultPredicate2];
Option 2:
Use NSCompoundPredicate to compound multiple filtering. You can easily find many examples on google and stackOverFlow.
Hope this will help,
Thanks

Predicate for range of NString in a Array

I have an array(NSArray) let's say
NSArray *cityArry = #[#"Chennai", #"Mumbai", #"Kolkata"];
For example I have mentioned the NSArray with 3 objects, but actually in runtime I have an array which contain more than 1000 objects.
Now I have the string(NSString) "chennai Tnagar"
NSString *scanedStr = #"Chennai Tnagar";
So, now I want to find out that the whether range of scanedStr contain in cityArry. For example I should get output as #"Chennai" from the cityArry.
I know we can do this by looping the NSArray and by using rangeofstring we can get the output. Since I have more than 1000 objects I don't want to do the looping. Please guide me and help me out how to solve this.
Thanks in advance
You could basically split the string and search for the array on the cityArry.
NSString *scannedStr = #"Chennai Tnager";
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"SELF in[cd] %#", [scannedStr componentsSeparatedByString:#" "]];
[cityArray filteredArrayUsingPredicate: predicate];
I am certain that this is a duplicate of another answer, but I can't find it. If anybody can, please close this as a dupe.
Anyway, the IN operator works nicely on strings. Apparently it is undocumented, so you will have to decide if you want to rely on it:
NSArray *cityArray = #[#"Chennai",#"Mumbai",#"Kolkata"];
NSString *scannedStr = #"Chennai Tnagar";
NSPredicate *pred = [NSPredicate predicateWithFormat: #"SELF IN[cd] %#", scannedStr];
NSArray *result = [cityArray filteredArrayUsingPredicate: pred];
This solution has the advantage of keeping the object of the predicate on the left, which I happen to find easier to read - but as mentioned, it is not documented.
In case you wish to use a documented construct, you can swap the order of the predicate and use a normal CONTAINS:
pred = [NSPredicate predicateWithFormat: #"%# CONTAINS[cd] SELF", scannedStr];
I propose that you make use of SQL query like by using sqlite3 as a database of the list of all cities you have. Its a bit painful implementing a database but it solves the problem I think.

How to find maximum id which is integer in a given NSSet through NSPredicate in core data?

I am using core data.I have two entities.one is session and second is scan.here is one to many relationship, means one session can have multiple scans.
I have scans_id in scan Entity.Suppose in a session entity i have 3 scans means a NSSet of 3 scans.i want to find out that scan which is having maximum scans_id among the set.I want to do it from predicate.
I am using this function.
-(void)CallForGetMaximumScanIDInSession:(Session *)ObjTempSession
{
NSSet *temp=ObjTempSession.scan;
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"scans_id==max(scans_id)"];
NSSet *setTemp = [temp filteredSetUsingPredicate:predicate];
NSArray *arrTemp=[setTemp allObjects];
if (arrTemp.coun>0 && arrTemp.coun==1)
{
// arrTemp must having a single object of scan.
}
}
It is giving me crash on predicate line.
Please let me know if i am making wrong predicate.
Thanks in Advance.
This should work i guess
NSSet *temp=ObjTempSession.scan;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.scans_id == %#.#max.scans_id", temp];
You can find a similar question here

Sorting an NSArray and case precedence

I have a list of managed objects stored in core data. I use these objects to populate a tableview controller that is sectioned according to alphabetical order. The data in these objects is obtained via web service, so I have no control over their case (which really doesn't make much difference in this case).
Most of the data is returned in all caps. I've noticed that, on the rare occasions where the case is NOT all caps, those items do not fall into alphabetic order. In the following code sample, stationIndex is an array of sorted first letters:
for(NSString *character in stationIndex){
NSPredicate *pred = [NSPredicate predicateWithFormat:#"name beginswith[c] %#", character];
// sort the list
NSArray *filteredGaugeList = [[tempGaugeList filteredArrayUsingPredicate:pred] sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSString *first = [(Gauge*)a name];
NSString *second = [(Gauge*)b name];
return [first compare:second];
}];
if([filteredGaugeList count] > 0){
[[self allGauges] addObject:filteredGaugeList];
}
}
I'm aware that there is a way to ignore case when using a selector, but in my case, I'm sorting on properties of objects, so I'm assuming I need a comparator. Is there a way to handles case in this situation? Thanks!
You can sort ignoring case in a comparator as well, just use
return [first caseInsensitiveCompare:second];
Alternatively, use a sort descriptor specifying the selector and the sort key:
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"name"
ascending:YES
selector:#selector(caseInsensitiveCompare:)];
NSArray *sorted = [array sortedArrayUsingDescriptors:#[sort]];
Remark: To display Core Data objects in a table view, you can also use
NSFetchedResultsController. Then you would add the predicate and the sort
descriptor to the fetch request. A fetched results controller has also methods
to group a table view into sections, and to update a table view automatically
when objects are inserted/deleted/modified.

Core Data Where clause between two tables?

I have two tables that I would like to get data and from by seeing if they match a particular attribute.
I want to write a predicate or something like this but I do not seem to be able to come up with a solution: #"Data.item == Item.code".
The reason why I don't use relationships is because my database was imported from mysql. So all the data is coming from outside of the app its being synced from downloaded mysql tables.
---------------------------EDIT-------------------------
What I have tried so far lots of things here is the crappy way I am doing this now perhaps from this you can understand more of what I am trying to do .
NSPredicate * Newpredicate = [NSPredicate predicateWithFormat:#"hid == 2"];
NSArray *row2 = [db DLook:Newpredicate table:#"Data"];
for (NSManagedObject *data in row2) {
NSLog(#"\n\n\n\nid\n\n\n\n: %#", [data valueForKey:#"id"]);
NSString *itemToCode = [data valueForKey:#"item"];
NSPredicate *itemPredicate = [NSPredicate predicateWithFormat:#"code == %#",itemToCode];
NSArray *itemRow = [db DLook:itemPredicate table:#"Item"];
for (NSManagedObject *item in itemRow) {
NSLog(#"\n\n\n\ncode : %#\n\n\n\n",[item valueForKey:#"code"]);
}
// NSLog(#"id: %#", [data valueForKey:#"id"]);
//NSManagedObject * itemhid= [data valueForKey:#"testRel"];
//NSLog(#"code: %#",[itemhid valueForKey:#"code"]);
}
NSLog(#"\n\n\n\n%d\n\n\n\n",[row2 count]);
The DLook is a convince method that just fetches the data using the predicate on the table that I pass. Then take the returned area of NSmanaged objects looping through them.
I wish I could just make a magical relationship that would let me get a all the Item.data that match the Data.items!!!
I don't want to do it like this I want to make a relationship that would work like that.
Help
Thanks
Your equality in the predicate would only be true if the two objects are actually the same object. You could do this:
[NSPredicate predicateWithFormat:#"item = %#", code];
However, you should really try to get the relationships right when importing your model. Then you would not even have to do a fetch from the store but just use
dataObject.item;
So, when you import do something like this:
// get one data object from your source
// get the corresponding item object from your source
Data *dataObject = [NSEntityDescription insertNewObjectForEntityForName:#"Data"
inManagedObjectContext:self.managedObjectContext];
Item *itemObject = [NSEntityDescription insertNewObjectForEntityForName:#"Item"
inManagedObjectContext:self.managedObjectContext];
[dataObject addItemObject:itemObject];

Resources