iOS Swift: Sort array into three dimensional array - ios

I have an array of CKRecords that I would like to sort into a three dimensional array. Within the first array is an array of dates, and each date is an array of names, where a name is an Int between 0 and 4. I'm successfully sorting my records into a two dimensional array currently (code below).
Name can be retrieved with record.objectForKey("Name") as Int
func buildIndex(records: [CKRecord]) -> [[CKRecord]] {
var dates = [NSDate]()
var result = [[[CKRecord]]]()
for record in records {
var date = record.objectForKey("startTime") as NSDate
if !contains(dates, date) {
dates.append(date)
}
}
for date in dates {
var recordForDate = [CKRecord]()
for (index, exercise) in enumerate(records) {
let created = exercise.objectForKey("startTime") as NSDate
if date == created {
let record = records[index] as CKRecord
recordForDate.append(record)
}
}
result.append(recordForDate)
}
return result
}
Not sure the best way to approach this problem. Even general guidance would be appreciated.

General Overview:
Step 1 - choose your sort algorithm. I find that the insertion sort algorithm is the easiest for me to understand and is fast too.
Step 2 - decide on your data structure. You could use a 2-dimensional array. The first dimension represents your dates, and the second dimension represents your records. So the array might be defined like this List<List<CKRecord>>. So the first entry would contain a list (List<CKRecord>) of all the records with the earliest date (it may be one or many).
Basic Steps
(with a 2-D array)
So start with the empty data structure
Figure out which Date list it should go into
If the date does not exist yet, you need to sort the date into the correct position and add a new array/list with the new entry as the only contents
If the date already exists, you need to sort the record into the correct position of the already existing list of records
Enjoy

Related

Realm iOS: count the distinct objects in a collection

What is the most efficient way to get the count of the unique values on a table?
For example:
fruitType
---------
banana
banana
apple
apple
apple
bananas : 2
apples : 3
By using fruitsCollection.distinct(by: ["fruitType"]) i can get the distinct values but not the count.
Any help would be appreciated.
you could try something simple like this (assuming fruits are Strings, adjust accordingly to your object types):
let fruits = fruitsCollection.distinct(by: ["fruitType"])
var results = [String:Int]()
Set(fruits).forEach{ fruit in results[fruit] = (fruits.filter{$0 == fruit}).count }
print("---> results: \(results)")
or
let results: [String:Int] = Set(fruits).reduce(into: [:]) { dict, next in
dict[next] = (fruits.filter{$0 == next}).count }
print("---> results: \(results)")
The Set(fruits) gives you the unique set of fruit names. The filter{...} gives you the count of each. The forEach or the reduce turns the results into a dictionary of key values.
#workingdog answer works very well but here's a more Realmy option. Something to keep in mind is that Realm Results objects are lazily loaded - meaning working with very large datasets has a low memory impact.
However, as soon high level Swift functions are used, that lazy-ness is lost and every object gobbles up memory.
For example, loading 50,000 Realm objects into a Results object doesn't have any significant memory impact - however, loading 50,000 objects into an Array could overwhelm the device as the objects loose their lazy-loading nature.
With this solution, we rely on Realm to present unique values and store them in Results (lazy!) then iterating over those we filter for matching objects (lazy) and return their count.
I created a FruitClass to hold the fruit types
class FruitClass: Object {
#Persisted var fruitType = ""
}
and then to code
This is a very memory friendly solution
//get the unique types of fruit. results are lazy!
let results = realm.objects(FruitClass.self).distinct(by: ["fruitType"])
//iterate over the results to get each fruit type, and then filter for those to get the count of each
for fruit in results {
let type = fruit.fruitType
let count = realm.objects(FruitClass.self).filter("fruitType == %#", type).count
print("\(type) has a count of \(count)")
}
and the results
apple has a count of 3
banana has a count of 2
orange has a count of 1
pear has a count of 1

Using CoreData to save array containing dictionaries

I'm trying to use CoreData framework inside an iOS app for the first time and I have some troubles with it.
I have a kind of complex type of array, containing tuples that contains dictionaries:
let DataSet: [( Float, Float, [String : String])] = ...
I created an NSManagedObjectContext and an NSManagedObject, I added the DataSet array to the NSManagedObject, but when I try to save the 1st NSManagedObjectContext, the app is crashing.
That's not happening using normal arrays but only with more complex ones like the one above.
Is there a way to deal with that problem? Do I need to adjust my xcdatamodel?
I would appreciate any help a lot.
First you need to declare 2 separate entities. One for Floats and another for Dictionary.
Then create "One to Many" relation ship between Float entity and Dictionary. It will look like this,
Then you need to convert your tuple into these 2 entities, Like this,
dataSet.forEach{ (member) in
let tuple = Test.(context: mainContext)
tuple.first = member.0
tuple.second = member.1
let dictionary = member.2
dictionary.forEach{ (key, value) in
let dick = Dick.(context: mainContext)
dick.key = key
dick.value = value
tuple.addToContains(dick)
}
}
Finally you need to save the values.
if mainContext.hasChanges {
do {
try mainContext.save()
} catch {
print(\(error))
}
}

Swift - adding Data to core data with a for loop

I have two entities:
AgendaEvent
AgendaDate
AgendaEvent has a one to many relationship with AgendaDate (the relationship name
is agendaDates).
Agenda Dates has just one attribute (agendaDates of type Date).
I am trying to save all the values of an array into Agendadate.
This saving could happens in two different way.
The user is creating a new AgendaDate (so a new context)
The user is updating an existing AgendaDate
This is my code:
var agendaDateToEdit: AgendaDate // this value is passed through a segue if the user clicked to modify an existing one.
var dateToAdd: AgendaDate!
if agendaDateToEdit == nil {
dateToAdd = AgendaDate(context: context)
} else {
dateToAdd = agendaDateToEdit
}
now i perform the for loop to insert the items form the array:
let agendaEvent: AgendaEvent!
if agendaEventToEdit != nil {
agendaEvent = agendaEventToEdit
} else {
agendaEvent = AgendaEvent(context:context)
}
for date in dates {
dateToAdd.agendaDates = date
agendaEvent.addToAgendaDates(dateToAdd)
ad.saveContext()
}
However in this way I store just the last value of the array.
I'm new to swift and i don't really understand where the error is.
I would really appreciate if anybody can help.
Thank you!
dateToAdd is defined above the scope of the for loop, therefore it sticks around on each iteration of the loop…
You create or assign dateToAdd
First pass though the loop starts
The date of dateToAdd is set to date
dateToAdd is appended to the agendaEvent
Context gets saved
Next pass through the loop starts
The date is again set on dateToAdd (this changes the date in the copy that is already in the agendaEvent)
agendaEvent is told to append the same dateToAdd it just added which it ignores as that entity is already in its list of agendaDates
Context gets saved and the for loop continues until it runs out of dates.
To-many relationships in Core Data are stored with NSSet which ensures each item only exists once. That is why you only get one AgendaDateadded to the event.
Since you hold on to the one AgendaDate instance and keep changing its date you see whatever the last date in the array of dates is. If you add a breakpoint to the loop you should see the one AgendaDate in the AgendaEvents gets each date in turn.
If you create a new dateToAdd each time through the loop I predict you will see much better results
for date in dates {
let dateToAdd = AgendaDate(context: context)
dateToAdd.agendaDates = date
agendaEvent.addToAgendaDates(dateToAdd)
ad.saveContext()
}

Get elements from array where date is today

I have an array of objects where a property is date of type NSDate. The array includes future days and past days.
I want to get an array of elements from that array that only includes dates that are in today, not the last 24 hours.
I ordered the elements by descending (new to old):
allDashboards.sortInPlace({ $0.date!.compare($1.date!) == NSComparisonResult.OrderedDescending
I tried to do a for loop, check if date is yesterday and cut the array there, but it doesn't work as it may be no element with a yesterdays date:
for dash in allDashboards {
if (NSCalendar.currentCalendar().isDateInYesterday(dash.date!)) {
print(i)
allDashboards.removeRange(Range.init(start: 0, end: i))
break
}
i += 1
}
Is there a method to see if date is past a day instead of if the date is part of that day?
One-liner:
let todayDashboards = allDashboards.filter { NSCalendar.currentCalendar().isDateInToday($0.date!) }
You can use NSDateComponents to create the dates which represent the start and end of your acceptable range. These can be anything you want.
Once you have those dates, you can create an NSPredicate which matches dates > the start and < the end dates. This will generate a filtered array for you.
You could use isDateInToday to filter the content too.
What you must not do is to iterate the array and mutate it at the same time, which is what your current code does with allDashboards.removeRange inside the for loop.
You can filter all dates which are today with the filter function and isDateInToday of NSCalendar
let filteredDashboards = allDashboards.filter{ NSCalendar.currentCalendar().isDateInToday($0.date!) }

Swift automatically creating temporary dictionaries

I'm working with a datasource for a UITableView where I need an array with dictionaries.
I tried making an array, and filling it with dictionaries in a for loop like this.
temporaryDataDict = [:]
temporaryDataDict = ["name":stockName, "ticker":ticker, "lastPrice":lastPrice, "purchasePrice":purchasePrice, "weight":weight, "daysHeld":daysHeld]
temporaryDataArray.append(temporaryDataDict)
But of course, when I start filling my tableView with the dataSource. I end up for 23 of the exact same dictionaries (the last one in the for loop).
This is of course, because it's changing the dictionary every time.
I havn't been able to find a way to keep the data in the dictionaries, or programatically make a new dictionary every time (since they need a new name otherwise it'll overwrite the data).
So how can I programatically make dictionaries everytime a for loop runs, then get the keys & values of these dictionaries?
or am I going about this completely wrong?
If it helps, here's the kind of data I'm working with.
I have a stock (or item) with 6 properties. So I think it makes the most sense to have an array where every item in the array is the "stock" as a dictionary that contains the 6 properties.
So is it possible to make swift automatically create these dictionaries for me? Since I don't know the amount of dictionaries needed.
PS.
I know this is what CoreData is for. I'm using these arrays and Dictionaries to later fill in my CoreData.
If the above isn't possible I am aware that I can probably create a new CoreData entity to accomplish what I want, but it doesn't seem like the best way to go about it.
Any help would be greatly appreciated! Thanks
I don't believe that a dictionary is the best sort of data structure to use in this case. Since typically a dictionary is composed of a unique key and a value.
I have edited this answer. Originally I suggested creating a class containing a property for each record field. Then I figured that this could be done using a tuple via a typealias for each record. Using a typealias gets around an issue related to creating arrays of tuples.
ETA: However please read the comments because Zaph who knows more about this than me reckons that using a Class is a stronger solution.
This is my tuple based solution. Most of what follows is just about quickly creating some dummy data:
typealias StockRecord = (stockName:String, ticker: String, lastPrice: Double, purchasePrice: Double, weight: Double, daysHeld: Int)
var temporaryDataArray = [StockRecord]()
// use a loop to create dummy records and add each to the array
// rough and ready - just to test the solution
var loopCounter: Int
for loopCounter = 0; loopCounter <= 23; loopCounter++ {
//some dummy field values for each record
var stockName = "stockName" + ("\(loopCounter)")
var ticker = "ticker" + ("\(loopCounter)")
var lastPrice = Double(loopCounter)
var purchasePrice = Double(loopCounter)
var weight = Double(loopCounter)
var daysHeld = loopCounter
var newRecord = (stockName, ticker, lastPrice, purchasePrice, weight, daysHeld)
temporaryDataArray.append(newRecord)
}
ETA: Iterate over the array - eg:
for recordEntry in temporaryDataArray {
var a = recordEntry.stockName
var b = recordEntry.ticker
// etc
}
Or with enumeration - eg:
for (count,recordEntry) in enumerate(temporaryDataArray) {
println("\(count) \(recordEntry)")
}
Output:
0 (stockName0, ticker0, 0.0, 0.0, 0.0, 0)
1 (stockName1, ticker1, 1.0, 1.0, 1.0, 1)
2 (stockName2, ticker2, 2.0, 2.0, 2.0, 2)
3 (stockName3, ticker3, 3.0, 3.0, 3.0, 3)
etc

Resources