How to create a streak from an array of tuples in Swift - arrays

I have an array of tuples that looks like:
[("07-20-2016", 2), ("07-22-2016", 5.0), ("07-21-2016", 3.3333333333333335)]
It consists of a String that is a date and a Double that is a mood (Moods less than or equal to 3 means that day was a good day).
I am trying to figure out how to create a streak of good days.
So what I think I need to do first is sort the array of tuples to put them in order by date.
Then I need to count the tuples that are below the mood value 3 if they are in a row. In this case the result would be 1 because there is only 1 day that is below 3.
If I had an array of tuples like:
[("07-22-2016", 5.0), ("07-21-2016", 3), ("07-20-2016", 2), ("07-19-2016", 1), ("07-18-2016", 1), ("07-16-2016", 2), ("07-15-2016", 3)]
It would have a result of 4 because the highest number of days in a row that are below 3 is 4. The reason it wouldn't be 6 is because a date of "07-17-2016" doesn't exist.
Please help me solve this problem. I know there are many users on here that are smarter then me. Can you solve this?

First, as matt suggested, make a nicer datatype. The language makes this super convenient with structs. We need each entry to have a date and a mood score. Easy-peasy.
import Foundation
struct JournalEntry {
let date: NSDate
let score: Double
var isGoodDay: Bool { return self.score <= 3.0 }
}
(The computed isGoodDay property will make life even easier later on.)
Now we need to turn the raw list of tuples into a list of these structs. That's straightforward. Create a date parser, and use map to convert between the tuple type and the struct:
typealias RawEntry = (String, Double)
func parseRawEntries<S: SequenceType where S.Generator.Element == RawEntry>
(rawEntries: S)
-> [JournalEntry]
{
let parser = NSDateFormatter()
parser.dateFormat = "MM-dd-yyyy"
// Use guard and flatMap because dateFromString returns an Optional;
// you might prefer to throw an error to indicate that the data are
// incorrectly formatted
return rawEntries.flatMap { rawEntry in
guard let date = parser.dateFromString(rawEntry.0) else {
return nil
}
return JournalEntry(date: date, score: rawEntry.1)
}
}
After converting we'll need to go through the list testing two conditions: first, that days are "good" and then that pairs of good days are consecutive. We have that helper method in the data structure itself to test the former. Let's create another helper function for the consecutive part:
/* N.B. for brevity this assumes first and second are already sorted! */
func entriesConsecutive(first: JournalEntry, _ second: JournalEntry) -> Bool {
let calendar = NSCalendar.currentCalendar()
let dayDiff = calendar.components(.Day,
fromDate: first.date,
toDate: second.date,
options: NSCalendarOptions(rawValue: 0))
return dayDiff.day == 1
}
At each enumeration step, we'll have the current entry, but we also need either the previous or the next, so that we can test for consecutivity. We also obviously want to keep track of the streaks' counts as we find them. Let's make another datatype for that. (The reason for lastEntry rather than nextEntry will be clear in a moment.)
struct Streak {
let lastEntry: JournalEntry
let count: UInt
}
This is the fun part: iterating over the array, constructing Streaks. This can be done with a for loop. But consuming sequences by calculation and agglomeration on each new item is a known pattern. It's called "folding"; Swift uses the alternate term reduce().
Reducing a sequence into groups, or while keeping track of something, is also a well-known pattern: the combined value is itself a collection. At each step, you inspect the new item along with the most recent value in the reduction. The new value for the reduction -- what you return from the combining function -- is still the collection, either with the previous value updated or a new value appended.
The Streak datatype from above will make the elements in the reduction: that's why it uses the last entry. Each step, we will save the JournalEntry and inspect it on the next.
For streaks, we only care about "good" days. Let's make life simple and filter the list right off the bat: let goodDays = journal.filter { $0.isGoodDay } We'll need the first entry so that there's something to compare to on the first step. This is also a chance to bail out if for some reason there are no good days.
guard let firstEntry = goodDays.first else {
return []
}
That first entry goes into a Streak object, which goes into the collection that will become the reduction: let initial = [Streak(lastEntry: firstEntry, count: 1)], and we're ready to go. The full function looks like this (I'll let the comments explain the procedure inside the reduce):
func findStreaks(in journal: [JournalEntry]) -> [Streak] {
let goodDays = journal.filter { $0.isGoodDay }
guard let firstEntry = goodDays.first else {
return []
}
let initial = [Streak(lastEntry: firstEntry, count: 1)]
return
// The first entry is already used; skip it.
goodDays.dropFirst()
.reduce(initial) {
(reduction: [Streak], entry: JournalEntry) in
// Get most recent Streak for inspection
let streak = reduction.last! // ! We know this exists
let consecutive = entriesConsecutive(streak.lastEntry,
entry)
// If this is the same streak, increment the count by
// replacing the previous value.
// Otherwise add a new Streak with count 1.
let newCount = consecutive ? streak.count + 1 : 1
let streaks = consecutive ?
Array(reduction.dropLast()) :
reduction
return streaks +
[Streak(lastEntry: entry, count: newCount)]
}
}
Now, back at the top level, things are very simple. Raw data:
let rawEntries = [("07-22-2016", 5.0), ("07-21-2016", 3), ("07-20-2016", 2), ("07-19-2016", 1), ("07-18-2016", 1), ("07-16-2016", 2), ("07-15-2016", 3)]
Convert that into the relevant datatype, immediately sorting by date while we're at it.
let journal = parseRawEntries(rawEntries).sort { $0.date < $1.date }
Calculate streaks:
let streaks = findStreaks(in: journal)
And the result you want is then let bestStreakLen = streaks.map({ $0.count }).maxElement()
(Small note: comparing NSDate using < like that requires a new function:
func <(lhs: NSDate, rhs: NSDate) -> Bool {
let earlier = lhs.earlierDate(rhs)
return earlier == lhs
}
)

Here is the answer I found.
func datesToStreaks(input: [NSDate]) -> [[NSDate]] {
var ranges:[[NSDate]] = [[]]
input.forEach { currentDay in
var lastRange = ranges.last ?? []
if let lastDate = lastRange.last {
if lastDate.dateByAddingTimeInterval(86400).isEqualToDate(currentDay) {
lastRange.append(currentDay)
ranges.removeLast()
ranges.append(lastRange)
}
else {
ranges.append([currentDay])
}
}
else {
lastRange.append(currentDay)
ranges.removeLast()
ranges.append(lastRange)
}
}
return ranges
}
let input = [("07-22-2016", 5.0), ("07-21-2016", 3), ("07-20-2016", 2), ("07-19-2016", 1), ("07-18-2016", 1), ("07-16-2016", 2), ("07-15-2016", 3)]
let sortedDays:[NSDate] = input
.filter { (date, mood) in
mood <= 3.0}
.map { (date, mood) in
return date}
.flatMap{ date in
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM/dd/yyyy"
return dateFormatter.dateFromString(date)
}
.sort{$0.0.earlierDate($0.1).isEqualToDate($0.0)}
let maxStreak = datesToStreaks(sortedDays)
.map{$0.count}
.reduce(0, combine: {return max($0,$1)})
print(maxStreak) // 4

You can first build an array of streaks of good and bad moods, and then filter or reduce it to analyse the streeks:
let dateData = [("07-22-2016", 5.0), ("07-21-2016", 3), ("07-20-2016", 2), ("07-19-2016", 1), ("07-18-2016", 1), ("07-16-2016", 2), ("07-15-2016", 3)]
var streaks:[(String,Bool,Int)] = []
for (date,value) in dateData
{
if let goodMood = streaks.last?.1
where goodMood == (value < 3)
{ streaks[streaks.count-1].2 += 1 }
else
{ streaks += [(date, value<3, 1)] }
}
// at this point streaks contains an array of tupples with counts of consecutive days of a given mood
//
// [("07-22-2016", false, 2), ("07-20-2016", true, 4), ("07-15-2016", false, 1)]
//
// you can then process that as you need
let goodStreaks = streaks.filter{$0.1}.map{($0.0,$0.2)} // [("07-20-2016", 4)]
let badStreaks = streaks.filter{!$0.1}.map{($0.0,$0.2)} // [("07-22-2016", 2), ("07-15-2016", 1)]
let longestGoodStreak = streaks.filter{$0.1}.reduce(0, combine: { max($0,$1.2) }) // 4

Related

Merging arrays of dictionaries based on dates

I have 2 arrays, both of kind [[String:Any]] , where each element :
["date":Date,"value":CGFloat] //always looks like this
I might even have more than 2 (!)
I would like to create a single array with the same structure that sums all of them (2 or more) for each date that appears in all of them.
If the date of array1 does not appear on the others(array2, etc) I will simply add 0 to the value at array 1 for this specific date.
Is there a simple efficient way to do so ?
Instead of dictionaries use structs, it's more convenient:
struct MyStruct {
let date: Date
let value: CGFloat
}
Let's create 3 arrays of MyStructs:
let now = Date()
let later = now.addingTimeInterval(3600)
let earlier = now.addingTimeInterval(-3600)
let array1: [MyStruct] = [MyStruct(date: now, value: 1),
MyStruct(date: later, value: 2)]
let array2: [MyStruct] = [MyStruct(date: now, value: 3),
MyStruct(date: later, value: 4)]
let array3: [MyStruct] = [ MyStruct(date: earlier, value: 5),
MyStruct(date: later, value: 6)]
Now, let's group the elements and add the values for the elements with the same date property:
let allArrays = array1 + array2 + array3
let dict = Dictionary(allArrays.map { ($0.date, $0.value) },
uniquingKeysWith: { $0 + $1 })
All you have to do now is convert it back to an array of MyStruct:
let newArray = dict.map { MyStruct(date: $0.key, value: $0.value) }
And you can check the results like so:
for element in newArray {
print("date =", element.date, "value =", element.value)
}
I found a way, assuming data is inside a structure(not a dic) which is a better practice.
I will put all arrays into a single large array, sort it by dates, loop on it and as long as date is equal previous date(or close enough to equality), I will sum the values up. When the next date is different, I will save the date and the sum.
//create a combined array from all given arrays
var combined = [RootData]()
for list in dataSets {combined.append(contentsOf: list)}
//sort it by dates
let sortedArray = combined.sorted { $0.date < $1.date }
//new array - sum of all
var sumData = [RootData]()
var last:Date = sortedArray[0].date //set starting point
var sum:CGFloat = 0
for element in sortedArray
{
//same date - accumulate(same is less than 1 sec difference)
if(abs(element.date.seconds(from: last)) <= 1) {
sum+=element.value
}
//save
else {
sumData.append(RootData(value:sum,date:last))
sum=element.value
}
last=element.date
}
//last object
sumData.append(RootData(value:sum,date:last))
return averageData
Here RootData is a simple structure for the data with :
value:CGFloat
date:Date
Works as expected.
Because dates are not always completely equal , I check equality by assuming 1 second or less is the same date.

How to apply a dictionary to an array to have it sort properly?

This is in a swift playground and the final objective is to get the dictionary to be sorted using the Months: [String] array. Here is what the code looks like right now:
Class YearlyTemps {
var Months: [String] = ["January", "February"...]
var Temperatures: [String,(temp1: Int, temp2: Int)] = [:]
func SetMonthlyTemps(month: String, temp1: Int, temp2: Int) -> Void {
Temps [month] = (low, high)
}
func ShowResult() -> Void {
for key in Temps.key{
print(key)
}
for values in Temps.value{
print(values)
}
}
}
It currently displays the dictionary as:
December
November
January
(23, 40)
(20, 55)
(-2, 34)
With no real order to it. I need it to be like this in order (January-December with temps on same line):
January (-2, 34)
November (20, 55)
December (23, 40)
I've tried to push the dictionary into an array with no luck as of yet. And I'm not sure if I should have default values in the Months array or not or if that should be populated in the SetMonthlyTemps function.
Dictionaries do not have an order in swift along with many other languages. If you have an array with all the month names called monthsArray, I would iterate over that and use the value at each index as a key to get the values from the dictionary.
var monthsArray = ["January", "November", "December"]
var tempsDict: [String : (Int, Int)] = [:]
tempsDict["November"] = (11, 11)
tempsDict["January"] = (1, 1)
tempsDict["December"] = (12, 12)
for month in monthsArray {
//Check to make sure dictionary has key
if let temps = tempsDict[month] {
print("\(month): \(temps)")
}
}
You can iterate over the key-value pairs with
for entry in Temps {
print("\(entry.key) \(entry.value)")
}
Also you can sort your dictionary the same way you would sort an array with .sorted(by: ). All in one it would give
for entry in Temps.sorted(by: { $0.key < $1.key }) {
print("\(entry.key) \(entry.value)")
}
Now since you want to sort by month as they are sorted in the year, you will need to tell your code what it means. The easiest way is, instead of using String variables for your months, define them using an enum type. Moreover, if you chose Int as rawValue for your enum they will automatically be ordered in the way you define them in the enum. This will look like this:
enum Month: Int {
case january
case february
case march
...
case december
}
Then you simply need to change your Temps variable to be of type [Month: (Int, Int)] instead of [String: (Int, Int)]. Finally, you can simply use
for entry in Temps.sorted(by: { $0 < $1 }) {
print("\(entry.key) \(entry.value)")
}
Finally I would recommend using lower case camelCase for your variable names.

Sorting an array of struct objects using two parameters in Swift

I have the following code:
struct MyData {
var company = String();
var score:Int;
}
let data = [
MyData(company: "smiths", score: 4 ),
MyData(company: "lukes", score: 4),
MyData(company: "lukes", score: 9)
]
extension MyData: CustomStringConvertible {
var description: String {
return "(\(company), \(score))"
}
}
data.sorted { ($0.company, $1.score) < ($1.company, $0.score) }
print(data)
My output is:
[(smiths, 4), (lukes, 4), (lukes, 9)]
However, I want it to be the other way around:
[(lukes, 9), (lukes, 4), (smiths, 4)]
Can someone show me what it is I'm doing wrong?
You want to sort by company ascending then by score descending.
First of all – as mentioned in the comments – you have to assign the result to a new variable, print(data) prints the unsorted original array.
The sort algorithm is supposed to be
if the companies are equal sort the scores descending otherwise sort the companies ascending
let sortedData = data.sorted { ($0.company == $1.company) ? $0.score > $1.score : $0.company < $1.company}
print(sortedData)
Using the sorting rule described in comments, this is how your custom sorting function should look like:
let sorted = data.sorted(by: { this, next in
if this.company < next.company {
return true //first sort based on ascending company name
} else if this.company == next.company {
return this.score > next.score //if company name is the same, sort by descending score
} else {
return false //if company name is later in the alphabet, put it later in the sorted output
}
})
print(sorted)
Output:
[(lukes, 9), (lukes, 4), (smiths, 4)]
Moreover, the code you used didn't work for two reasons: sorted returns a new array containing the sorted results, it's not a mutating function. It couldn't even work with a mutating function, since data is immutable.
So I discovered that my solution was almost there. Because the sorted function returns a new array, and doesn't actually sort the array it is being called on, my print statement was always printing the original collection. The solution to my problem was:
var myArray = data.sorted { ($0.company, $1.score) < ($1.company, $0.score) }
print(myArray)
Thanks very much for the help. :-)

Calculate total per day of a key from an array of custom objects

I know that my title is slightly confusing but let me explain:
I have been at this for a while and I can't seem to figure this out.
First of all here is some of my code:
struct CalorieLog {
var date: Date
var calories: Int
}
var logs: [CalorieLog] = []
func logCalories() {
//... calculate calories to log
let currentDate: Date = Date()
let calories: Int = calculatedCalories
logs.append(CalorieLog(date: currentDate, calories: calculatedCalories))
}
Now how do I group the CalorieLog items in the logs array by day and get the sum of all the calories logged per day? And maybe sort them into an array of dictionaries? e.g. Dictionary(String: Int) so that (Day: Total Calories)
Please don't be harsh I am still a novice developer. Thanks in advance.
A great deal depends on what you mean by a "day". So, in this highly simplified example, I simply use the default definition, that is, a day as the Calendar defines it for a particular Date (not taking time zone realities into account).
Here's some basic data:
struct CalorieLog {
var date: Date
var calories: Int
}
var logs: [CalorieLog] = []
logs.append(CalorieLog(date:Date(), calories:150))
logs.append(CalorieLog(date:Date(), calories:140))
logs.append(CalorieLog(date:Date()+(60*60*24), calories:130))
Now we construct a dictionary where the key is the ordinality of the date's Day, and the value is an array of all logs having that day as their date:
var dict = [Int:[CalorieLog]]()
for log in logs {
let d = log.date
let cal = Calendar(identifier: .gregorian)
if let ord = cal.ordinality(of: .day, in: .era, for: d) {
if dict[ord] == nil {
dict[ord] = []
}
dict[ord]!.append(log)
}
}
Your CalorieLogs are now clumped into days! Now it's easy to run through that dictionary and sum the calories for each day's array-of-logs. I don't know what you ultimately want to do with this information, so here I just print it, to prove that our dictionary organization is useful:
for (ord,logs) in dict {
print(ord)
print(logs.reduce(0){$0 + $1.calories})
}
A lot of what you're attempting to do can be accomplished using Swift's map, sorted, filtered, and reduce functions.
struct CalorieLog {
var date: Date
var calories: Int
}
var logs: [CalorieLog] = []
// I changed your method to pass in calculatedCalories, we can make that random just for learning purposes. See below
func logCalories(calculatedCalories: Int) {
let currentDate: Date = Date()
logs.append(CalorieLog(date: currentDate, calories: calculatedCalories))
}
// This is a method that will calculate dummy calorie data n times, and append it to your logs array
func addDummyCalorieData(n: Int, maxRandomCalorie: Int) {
for _ in 1...n {
let random = Int(arc4random_uniform(UInt32(maxRandomCalorie)))
logCalories(calculatedCalories: random)
}
}
// Calculate 100 random CalorieLog's with a max calorie value of 1000 calories
addDummyCalorieData(n: 100, maxRandomCalorie: 1000)
// Print the unsorted CalorieLogs
print("Unsorted Calorie Data: \(logs)")
// Sort the logs from low to high based on the individual calories value.
let sortedLowToHigh = logs.sorted { $0.calories < $1.calories }
// Print to console window
print("Sorted Low to High: \(sortedLowToHigh)")
// Sort the CalorieLogs from high to low
let sortedHighToLow = logs.sorted { $1.calories < $0.calories }
// Print to console window
print("Sorted High to Low: \(sortedHighToLow)")
// Sum
// This will reduce the CaloreLog's based on their calorie values, represented as a sum
let sumOfCalories = logs.map { $0.calories }.reduce(0, +)
// Print the sum
print("Sum: \(sumOfCalories)")
If you wanted to map your CalorieLogs as an array of dictionaries you could do something like this:
let arrayOfDictionaries = logs.map { [$0.date : $0.calories] }
However that's kind of inefficient. Why would you want an array of dictionaries? If you just wanted to track the calories consumed/burned for a specific date, you could just make one dictionary where the date is your key, and an array of Int is the value which represents all the calories for that day. You probably would only need one dictionary, i.e.
var dictionary = [Date : [Int]]()
Then you could find all the calories for a date by saying dictionary[Date()]. Although keep in mind that you would have to have the exact date and time. You may want to change the key of your dictionary to be something like a String that just represents a date like 2/19/2017, something that could be compared easier. That will have to be taken into account when you design your model.
To get the logs sorted by date, you can simply do:
logs.sorted(by: { $0.date < $1.date })
To get a dictionary that maps a day to a sum of calories on that day you can do this:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd MMM yyyy"
var calorieCalendar = [String: Int]()
for log in logs {
let date = dateFormatter.string(from: log.date)
if let _ = calorieCalendar[date] {
calorieCalendar[date]! += log.calories
} else {
calorieCalendar[date] = log.calories
}
}
For logs setup like this
logs.append(CalorieLog(date: Date(), calories: 1))
logs.append(CalorieLog(date: Date.init(timeIntervalSinceNow: -10), calories: 2))
logs.append(CalorieLog(date: Date.init(timeIntervalSinceNow: -60*60*24*2), calories: 3))
The code above will produce a dictionary like this:
["17 Feb 2017": 3, "19 Feb 2017": 3]

Combining two NSPredicates with independent NSSortDescriptors in Swift

I am using CoreData and Swift 3, but I think this is more of a general question about NSPredicates, NSSortDescriptors, and NSFetchedResultsController...
I have an entity with a integer attribute that I want to sort on, but I need to split the results based on a passed in integer and sort the two halves independently in descending order and then combine them. All the entities with the attribute <= the splitting value go at the front (descending), and all the entities that are > the splitting value go at the end (descending).
Here's an example:
Normal fetched results sorted by the integer attribute in descending order:
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
Given a splitting value of "5", the final result should be:
[5, 4, 3, 2, 1, 10, 9, 8, 7, 6]
Or with a splitting value of 8, the final result should be:
[8, 7, 6, 5, 4, 3, 2, 1, 10, 9]
What I am doing is creating a roll-over point, the way days a month roll-over at 28, 29, 30, or 31. In my case the roll-over point is dynamic, and I am not working with days.
I cannot figure out how to do this with predicates and sort descriptors. Is this even possible?
I have read about doing multiple fetches, using a "sortOrder" attribute in the entity. Basically, fetching once, the putting the results in an array, sorting the array the way I want, then setting the "sortOrder" attribute on every entity, saving the context, then fetching again, sorting by "sortOrder". I can do that, but I was hoping for something more elegant.
Here's some code that does the proper sorting on an array:
func rolloverSort(withArray: [Int], andIndex index: Int) -> [Int] {
let inputArray = withArray.sorted()
var newArray = [Int]()
var splitIndex: Int = 0
for item in inputArray {
if item <= index {
newArray.insert(item, at: 0)
splitIndex += 1
}
if item > index {
newArray.insert(item, at: splitIndex)
}
}
return newArray
}
The result will be driving a UITableView, so I need to do this within the context of an NSFetchedResultContoller, which is why I was thinking of predicates and sort descriptors.
Here is my NSFetchedResultsController and some surrounding context:
// sortMethod (used in sortDescriptor) is set via an action sheet button.
// Currently it's just a string for the entity attribute to sort on.
// The closure on each button sets self._fetchedRequestController = nil,
// sets sortMethod to the desired sorting string, then calls
// tableView.reloadData().
var fetchedResultsController: NSFetchedResultsController<Entity> {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest: NSFetchRequest<Entity> = Entity.fetchRequest()
fetchRequest.fetchBatchSize = 20
let sortDescriptor = NSSortDescriptor(key: sortMethod, ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
let aFetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: managedObjectContext!,
sectionNameKeyPath: nil,
cacheName: "Master")
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
do {
try _fetchedResultsController!.performFetch()
} catch {
// FIXME: Replace error handling stub.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController<Card>? = nil
That works with simple sorting, but I am struggling to figure out how to slot in the sorting function from A.Jam.
Lets say you have a data store with 10 entities called Number already inserted. The model entity has one attribute called value of type Int32 which you want to sort on (the values in your data store range from 1 to 10).
Use this function to split your data based on your key value and sort them using an instance of NSSortDescriptor:
func fetchSortEntites(basedOn integer: Int32) -> ([Int32], [Number]){
var sortedNumberArray: [Int32] = []
var sortedManagedObjects: [Number] = []
// Phase 1
let predicate1 = NSCompoundPredicate(format: "value <= %ld", integer)
let sortDescriptor1 = NSSortDescriptor(key: "value", ascending: false)
let fetchRequest1 = NSFetchRequest<Number>(entityName: "Number")
fetchRequest1.predicate = predicate1
fetchRequest1.sortDescriptors = [sortDescriptor1]
do{
let managedObjects = try managedObjectContext.fetch(fetchRequest1)
for managedObject in managedObjects{
sortedNumberArray.append(managedObject.value)
sortedManagedObjects.append(managedObject)
}
} catch let error as NSError {
print(error.debugDescription)
fatalError("*** Failed to fetch managed objects from the context!")
}
// Phase 2
let predicate2 = NSCompoundPredicate(format: "value > %ld", integer)
let sortDescriptor2 = NSSortDescriptor(key: "value", ascending: false)
let fetchRequest2 = NSFetchRequest<Number>(entityName: "Number")
fetchRequest2.predicate = predicate2
fetchRequest2.sortDescriptors = [sortDescriptor2]
do{
let managedObjects = try managedObjectContext.fetch(fetchRequest2)
for managedObject in managedObjects{
sortedNumberArray.append(managedObject.value)
sortedManagedObjects.append(managedObject)
}
} catch let error as NSError {
print(error.debugDescription)
fatalError("*** Failed to fetch managed objects from the context!")
}
return (sortedNumberArray, sortedManagedObjects)
}
}
Result:
let myTuple = fetchSortEntites(basedOn: 5)
for number in myTuple.0{
print (number)
}
let myTuple = fetchSortEntites(basedOn: 8)
for number in myTuple.0{
print (number)
}
Hope this helped!
You can create wrapper around two NSFetchedResultsController (next resultsController).
First resultsController should fetch values before (<=) the threshold and second resultsController should fetch values after (>) the threshold.
After that wrapper can return required data as union of datas from the requestControllers.
For example:
//Get count cells
int countCells = resultsController1.sections.first.numberOfObjects + resultsController2.sections.first.numberOfObjects;
<...>
//Get value for cell
int value = 0;
if (cellIndex >= <counts_in_first_resultsController>)
{
value = <Get_from_the_second>;
}
else
{
value = <Get_from_the_first>;
}

Resources