NSpredicate one child item per parent - ios

I'm using Realm to store all the data in my app.
I have a shop item that contains a list of promotion ids and a promotion that have a parent property that is an id to the shop.
I now filter every promotion that is available now, and I like to add one more filter, so that the results list only contains one promotion per shop.
How can I complete the filter, so I only get one available promotion per shop?
Thanks!
// Kim
//EDIT
let startDate: Double = currentDate.timeIntervalSince1970
let endDate: Double = currentDate.timeIntervalSince1970
let parentPredicate = NSPredicate(format: "")
let datePredicate = NSPredicate(format: "SUBQUERY(dates, $d, $d.start <= \(startDate) && $d.end >= \(endDate)).#count > 0")
self.realm?.objects(CSPromotions.self).filter(datePredicate).filter(parentPredicate)

Realm Swift currently doesn't natively support distinct, so there's no great way to do this. What you can do is manually build an array of the distinct values:
let shops = NSMutableSet()
let promotions = self.realm?.objects(CSPromotions.self).filter(datePredicate).filter(parentPredicate).filter {
let id = $0.shop.id
if shops.contains(id) {
return false
}
shops.add(id)
return true
}

Related

Coredata relationships how make ordered collection or added in particular index in iOS swift

I am working with core-data relationships add the student in profile entities.
Profile and Student entities are multiple relationship with each others.
Profile entities for create students, it successfully created.
I want to add or append some information that profile entities through student entities its also added.
(Its Like: Profile entities have a array of dictionary of student entities )
But when in display in UItableview added info of student it display in unordered.
I want to display the added student should be display in last or first.
Coredata is unordered collection of set. how to make it order.
Also selected ordered Arrangement. its shows error students not be ordered.
How can achieve this. Help me
Here my code:
func create(record: ProfileModel) {
let cdProfile = CDProfile(context: PersistentStorage.shared.context)
cdProfile.emailID = record.emailID
cdProfile.gender = record.gender
cdProfile.getDate = record.getDate
cdProfile.id = record.id
if(record.toStudent != nil && record.toStudent?.count != 0){
var studentSet = Set<CDStudent>()
record.toStudent?.forEach({ (student) in
let cdStudent = CDStudent(context: PersistentStorage.shared.context)
cdStudent.activity = student.activity
cdStudent.currentPage = Int16(student.currentPage ?? 0)
cdStudent.getPercentage = student.getPercentage
studentSet.insert(cdStudent)
})
cdProfile.toStudent = studentSet
}
PersistentStorage.shared.saveContext()
}
#IBAction func saveBtnClick(_ sender: Any) {
let studentArr = StudentModel(_activity: "S-\(self.sectionString)", _studentComments: self.infotextView.text, _getPercentage: "-", _result: String(self.audioValueKey.count), _sectionID: self.sectionString, _sessionDate: self.convertedDate, _timeSpend: self.timeSpendStr, _currentPage: self.allPageNumber, _selectedValue: self.audioValueKey)
if let getStudentData = userProfileArr![indexNumber].toStudent?.count{
self.personArrCount = getStudentData
let getArr = userProfileArr![indexNumber].toStudent!
if getArr.count == 0{
}else{
for j in 0..<getArr.count{
self.student.append(getArr[j])
// self.student.insert(getArr[j], at: 0)
}
self.student.append(studentArr)
}
}else{
self.personArrCount = 0
self.student.append(studentArr)
print("student-empty",student)
}
let getProfileData = userProfileArr![indexNumber]
let updatePerson = ProfileModel(_id: selectedUserIndex!.id, _profileComments: getProfileData.profileComments!, _emailID: getProfileData.emailID!, _gender: getProfileData.gender!, _profileImage: getProfileData.profileImage!, _getDate: "NO", _studentDOB: getProfileData.studentDOB!, _studentName: getProfileData.studentName!, _toStudent: student)
print("student",self.student)
if(dataManager.update(record: updatePerson))
{
print("Update added")
}else{
print("Not-- added")
}
}
How can i fix this issue help me... Thanks advance.
first of all Core data only provides sorting on parent table only
if you wanna sort data in a subtable you can do as below
First you need to add a field named student_id(int16) in student table
Then you need to assign value as count + 1
As core data does not provide autoincrement field need to manage manually.
Follow the below code to sort data as last to first
// assume you have fetched profile in stud_profile var
if let student_list = stud_profile.toStudent as? Set<CDStudent> {
let arrStudents = Array(student_list).sorted(by: {$0.student_id > $1.student_id})
}
4.You can use arrStudents as it will return sorted [CDStudent]

Core Data predicate to fetch based on two of relationship's properties

I have two simple Entities:
Part {
#NSManaged var states: Set<PartState>
}
PartState {
#NSManaged var date: Date
#NSManaged var state: Int
}
I want to fetch those entities from Part, that newest PartState has state == 1.
I've tried to create NSPredicate:
NSPredicate(format: "SUBQUERY(states, $s, $s.date == max($s.date) AND $s.state == 1).#count > 0")
It looks like max($.date) checks all existing PartState entities, not just those belonging to a given Part and always returns 1 or 0 results.
NSPredicate(format: "ANY (states.state == 1 AND states.date == states.#max.date)")
Fails with: Unable to parse the format string...
NSPredicate(format: "ANY states.state == 1 AND ANY states.date == states.#max.date")
Returns all entities that have ever had state == 1 (which is what I would expect).
The only other solution that comes to my mind, is to have newestState property in Part but I still looking for some NSPredicate-based way.
You can fetch latest entry based on date using sort Descriptor
and apply predicate to fetch which has state == 1
let fetchRequest = NSFetchRequest<Part>.init(entityName: "Part")
fetchRequest.sortDescriptors = [NSSortDescriptor.init(key: "states.date", ascending: false)]
let predicate = NSPredicate.init(format: "states.state = %#","1")

Using CoreData how to configure a fetch request to find upcoming birthdays?

I’m using Xcode 7.3 and Swift 2.2 with an NSFetchedResultsController. Is it possible to create the required fetch request configured with a predicate and sort descriptors to solve this problem?
Given a Person entity that has a birthDate attribute how do I configure a fetch request to return upcoming birthdays? Here’s what I have tried:
I created a transient attribute called birthDateThisYear and configured it to return the person’s birthday this year. But I discovered that you can’t use a transient attribute in a fetch request with Core Data.
I tried the accepted answer here by denormalizing with birthDateDayNumber and birthDateMonthNumber attributes along with a custom setter for birthDate but I couldn’t figure out the predicate. What if today was Dec 31? Somehow it would need to wrap around to include Jan, Feb, and Mar.
I read that it could be done with expressions and comparison predicates. But I couldn’t figure out a solution. Anyone got this working?
I thought it work to create a denormalized attribute called birthDateInYear2000 but, again, that suffers from the same overlap problem.
Any ideas?
Suggestion:
Fetch all birthdays.
Map the birthdays to the next occurrence from now
let calendar = NSCalendar.currentCalendar()
let nextBirthDays = birthdays.map { (date) -> NSDate in
let components = calendar.components([.Day, .Month], fromDate: date)
return calendar.nextDateAfterDate(NSDate(), matchingComponents: components, options: .MatchNextTime)!
}
Sort the dates
let sortedNextBirthDays = nextBirthDays.sort { $0.compare($1) == .OrderedAscending }
Now sortedNextBirthDays contains all upcoming birthdays sorted ascending.
In Core Data you could fetch records as dictionary with birthday and objectID (or full name), create a temporary struct, map and sort the items and get the person for the objectID (or use the full name) – or you even could apply the logic to an NSManagedObject array
Edit
Using an NSFetchedResultsController you can sort the table view only if the information about the next birthday is stored in the persistent store (assuming it's the MySQL-store), because you can't apply sort descriptors including keys pointing to transient or computed properties.
The best place to update the nextBirthDate property is just before creating the NSFetchedResultsController instance of the view controller.
Create an (optional) attribute nextBirthDate in the entity Person
Create a extension of NSDate to calculate the next occurrence of a date from now
extension NSDate {
var nextOccurrence : NSDate {
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Day, .Month], fromDate: self)
return calendar.nextDateAfterDate(NSDate(), matchingComponents: components, options: .MatchNextTime)!
}
}
In the closure to initialize the NSFetchResultsController instance add code to update the nextBirthDate property of each record
lazy var fetchedResultsController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest(entityName: "Person")
do {
let people = try self.managedObjectContext.executeFetchRequest(fetchRequest) as! [Person]
people.forEach({ (person) in
let nextBirthDate = person.birthDate.nextOccurrence
if person.nextBirthDate == nil || person.nextBirthDate! != nextBirthDate {
person.nextBirthDate = nextBirthDate
}
})
if self.managedObjectContext.hasChanges {
try self.managedObjectContext.save()
}
} catch {
print(error)
}
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptors = [NSSortDescriptor(key:"nextBirthDate", ascending: true)]
fetchRequest.sortDescriptors = sortDescriptors
...
If the view controller can edit the birthDate property don't forget to update the nextBirthDate property as well to keep the view in sync.
my suggestion is similar:
fetch all customers/persons
filter all birthdates that range 3 days before and 3 days after today
Use closure like the following:
func fetchBirthdayList(){
// this example considers all birthdays that are like
// |---TODAY---|
// Each hyphen represents a day
let date = Date()
let timeSpan = 3
let cal = Calendar.current
nextBirthDays = self.list.filter { (customer) -> Bool in
if let birthDate = customer.birthDate {
let difference = cal.dateComponents([.day,.year], from: birthDate as! Date, to: date)
return (( (difference.day! <= timeSpan) || ((365 - difference.day!) <= timeSpan) ) && difference.day! >= 0)
}
return false
}
}
If you want, you can order the result afterwards.
Cheers
Oliver

How can I call aggregate functions for properties of relationships in Realm?

Is there a way to use aggregate functions for properties in a one-to-one and one-to-many relationship? Here's a simple example of the structure I'm using:
class Toy: Object {
dynamic var purchaseDate: NSDate?
dynamic var name: String = ""
let ratings = List<Rating>()
dynamic var toyOwner: Person?
}
class Rating: Object {
var stars: Int = 0
var comments: String?
}
class Person: Object {
dynamic var name: String = ""
dynamic var age: Int = 0
}
I'd like to do something along the lines of this:
// Average age of owner of all toys purchased within a date range
let avgAge: Double = realm.objects(Toy)
.filter("purchaseDate >= %# && purchaseDate <= %#", startDate, endDate)
.avg("person.age")
// Sum up all stars given to all toys purchased within a date range
let totalStars: Double = realm.objects(Toy)
.filter("purchaseDate >= %# && purchaseDate <= %#", startDate, endDate)
.sum("sum(ratings.stars)")
Is there a way to do these aggregate functions within Realm?
Although Realm does have aggregate value functions (See Aggregate Operations docs on RealmCollectionType), those don't currently work with multi-level keypaths like person.age.
However, you can do most of the heavy lifting with Realm by slightly modifying your queries:
// Average age of owner of all toys purchased within a date range
let ages = realm.objects(Toy)
.filter("purchaseDate BETWEEN %#", [startDate, endDate])
.valueForKeyPath("toyOwner.age") as! [Double]
let avgAge = ages.reduce(0, combine: +) / Double(max(ages.count, 1))
// Sum up all stars given to all toys purchased within a date range
let totalStars = realm.objects(Toy)
.filter("purchaseDate BETWEEN %#", [startDate, endDate])
.reduce(0) { sum, toy in
return sum + toy.ratings.sum("stars")
}

Using NSPredicate to filter Core Data entities without property equal to any of array property

I have a set of Price in Core Data
price1 = Price.newInManagedObjectContext(self.managedObjectContext!)
price1.price = 1
price1.size = 1
price1.market = market1
price2 = Price.newInManagedObjectContext(self.managedObjectContext!)
price2.price = 2
price2.size = 1
price2.market = market1
price3 = Price.newInManagedObjectContext(self.managedObjectContext!)
price3.price = 3
price3.size = 1
price3.market = market1
self.save()
Then I have an update set
let p1 = PriceUpdate(price: 1, size: 2)
let p2 = PriceUpdate(price: 4, size: 2)
let updates = [p1, p2]
And I want to fetch all Price objects in persistent store that don't have a price equal to any of the updates price. In the example that would be the price2 and price3 as price 2 and 3 are not in updates (1 and 4)
I've tried something like this but I'm not sure.
let request = NSFetchRequest()
request.entity = NSEntityDescription.entityForName("Price", inManagedObjectContext: self.managedObjectContext!)
request.predicate = NSPredicate(format: "%K == %#.%K", "price", updates, "price")
i think your can make one more attribute and insert 0 in your table attribute... and when you update your table than insert 1 in new attribute.. so when you fetched the data from table simple make a query with new attribute having 1 in table ...
You don't provide details of PriceUpdate, but I assume it's a struct or similar. Start by constructing an array holding just the prices:
let updatePrices = updates.map() { $0.price }
Then build a predicate which filters out the Price objects if their price is in the above array:
request.predicate = NSPredicate(format: "NOT %K IN %#", "price", updatePrices)

Resources