Sort arrays of protocols in Swift? - ios

I have a lot of arrays stored in a ViewController of the following:
protocol AlarmClockType {
var alarmLabel: String { get set }
var sound: AlarmSound { get set }
var snooze: Bool { get set }
var alarmType: AlarmType { get set }
var alarmOn: Bool { get set }
var alarm: NSTimer? { get set }
var attributedTitle: NSMutableAttributedString { get }
static var DocumentsDirectory: NSURL { get }
static var ArchiveURL: NSURL { get }
func timeToAlarm(prayerTimes: [String: String]?) -> NSDate
}
}
Each instance has a property alarmTime (which is NSDate). How can I sort the array based on time?
To make it more clear:
It's a TableViewController and the cells display alarm times. Each time the user adds an alarm, the tableView should be sorted by alarm times.

If you want to sort an array by dates, it's probably better if you use a custom data structure that includes an NSDate.
For example, you can create a struct that has properties for your data.
struct Alarm {
var alarmTime: NSDate = NSDate()
var description: String = ""
}
This struct can be used to create an array.
let alarms = [
Alarm(alarmTime: NSDate().dateByAddingTimeInterval(120),
description: "alarm 60 s in the future"),
Alarm(alarmTime: NSDate().dateByAddingTimeInterval(30),
description: "alarm 30 s in the future"),
Alarm(alarmTime: NSDate().dateByAddingTimeInterval(90),
description: "alarm 90 s in the future")
]
The array can be sorted using the sort function and some special syntax for handling NSDates.
let sortedAlarms = alarms.sort(
{ $0.alarmTime.compare($1.alarmTime) == NSComparisonResult.OrderedAscending }
)
Finally, in this example, sortedAlarms contains everything in alarms but it is sorted by the NSDate in alarmTime.
The data in the array can be easily accessed. For example, to get the alarm time from the first alarm in the sorted alarms array, use the following code.
let alarmTime = sortedAlarms[0].alarmTime
After the alarms are sorted, you will want to reload the data in your table view using tableView.reloadData().

Related

Filtering Arrays Containing Multiple Data Types in Swift3

I have an array like:-
var arrayData : Array<Dictionary<String, [BottleModel]>> = []
Bottle model :-
class BottleModel: NSObject {
var name : String
var price : Int
var reviews : Int
var category : String
var quantity : String
var id : String
var shopData : ShopModel
}
I want filtered array where price is > 2000
I tried let searchByInts = arrayData.filter({m in m.price < 200})
but getting below error:
Contextual closure
type '(Dictionary) -> Bool' expects 1 argument,
but 0 were used in closure body
How to filter such kind of array based on price
Working code:
let searchByInts = arrayData.filter { $0.values.contains { $0.contains { $0.price > 2000 } } }
By the way please write the following using literals:
var arrayData : [[String : [BottleModel]]] = []
Still no idea if that is what you actually want because your goal is very unclear. You have an array of dictionaries of arrays which actually contain the values you want to filter out. If a BottleModel costs more than 2000 do you want to keep the entire array it is contained in and the dictionary that array is in? You might want to map the entire data into one flat array before or after filtering.
Alternative using flatMap:
let flat = arrayData.flatMap { $0.values.flatMap { $0 } }
let searchByInts2 = flat.filter { $0.price < 200 } // or some other criteria

Group from Array to Dictionary

I have class:
class DisplayTime {
var time: Schedules
init (time: Schedules){
self.time = time
}
}
Schedules - class with next properties:
class Schedules: Object {
dynamic var hour = 0
dynamic var minute = 0
dynamic var groupIndex = 0
}
So, I'd like to get Dictionary, where key - hour, and value - array of Schedules - var dictTime: [Int:[Schedules]]
I try, but unsuccessfully:
func groupBy() -> [Int:[Schedules]]{
let displayTime = self.displayTimes
var dictTime: [Int:[Schedules]] = [:]
for i in 0..<displayTime.count {
dictTime[displayTime[i].time.hour] = [displayTime[i].time]
}
print (dictTime)
return dictTime
}
I understand - that function created only [key:[one_value]], but I may have several values for one key, how to handle that variant? (if key_now repeat key_before -> add to array Schedules for key_now to key_before.
Using the categorise(_:) method from the extension shown in this answer, you can simply say:
let dictTime = displayTimes.lazy.map{$0.time}.categorise{$0.hour}
map(_:) is used in order to iterate over the time of each DisplayTime and lazy is used in order to prevent the creation of an intermediate array.

How to map array to a key in dictionary in swift?

I want to create a dictionary with key as date & array of events.A date can have multiple events so i want to map one date as key of dictionary to the array of string.I will be dynamic a date can have no events or a date can have multiple events.I am getting data from array of dates i need to map it with events.
I have tried below code:
func addEventToDictionary(eventModal:CalenderEventModal,date:Date) {
var key:String = self.dateFormatter().string(from: date)
if let val = dict_events[key] {
} else {
dict_events[key] = [Any]()
}
dict_events[key] = eventModal
}
Here Event modal is an Object of Event.
Assuming dict_events is a dictionary with a declared type of [String: [Any]], then I believe all you're missing is appending to this array instead of assigning it. The value portion of the dictionary is Optional, so you need to append the value to a non-Optional array, then assign this back into your dictionary's key. Also, if you know you're only going to be storing CalenderEventModal objects, you could change the type of dict_events to [String: [CalenderEventModal]]. The fix for your code would look like this:
var dict_events: [String: [CalenderEventModal]] = [:]
func addEventToDictionary(eventModal: CalenderEventModal, date: Date) {
var key: String = self.dateFormatter().string(from: date)
if var val = dict_events[key] {
val.append(eventModal)
dict_events[key] = val
} else {
let events = [CalenderEventModal]()
events.append(eventModal)
dict_events[key] = events
}
}

Compare objects within Array and put equal values in seperate new object

In my App I have an array of objects 'class: EventObjects' with several properties like 'date: NSDate?' and 'stuffToDo: String?' that are fetched from a calendar database. What I try to achieve now is putting all EventObjects with the same date property together in another object of 'class: EventsAtSameDate'.
class EventsAtSameDate: NSObject
{
var date:NSDate?
var eventObjects:NSArray<EventObject>?
}
And finally have a nested array with the EventsAtSameDate.
var nestedArrayOfEventsAtSameDateObjects: Array<EventsAtSameDate>?
I know how to search and sort an array with .filter .sort .sortInPlace etc. functions. But how can I compare the dates in the array with each other and put them in another nested Array?
You could instantiate a dictionary to keep track of the dates present while doing one iteration over the initial array of type EventObject. Then iterate the the dictionary to instantiate classes of EventsAtSameDate and append them to the nestedArrayOfEventsAtSameDateObjects: [EventsAtSameDate]
The code would look something like:
var nestedArrayOfEventsAtSameDateObjects = [EventsAtSameDate]()
//instantiate a dictionary that uses Date as the key and an array of EventObject as the associated value
var dictionary = [Date: [EventObject]]()
//iterate through your intial array of EventObjects
for obj in eObjs {
//check if you have already seen the date in the collection
if dictionary[obj.date] != nil {
//if you have - then append it to that date
dictionary[obj.date]?.append(obj)
continue
}
//otherwise, add it to the dictionary for a check later in the loop
dictionary[obj.date] = [obj]
}
//then iterate through the dictionary
for (date, eObjs) in dictionary {
if eObjs.count > 1 {
let sameDate = EventsAtSameDate()
sameDate.date = date
sameDate.eventObjects = eObjs
nestedArrayOfEventsAtSameDateObjects.append(sameDate)
}
}
I would sort the array of your events by date and then iterate over them. Check if the date is newer then the previous one and create another storage if so.
class Event {
var date:Date!
}
class EventsList
{
var date:Date!
var events:[Event]!
}
func createEventsLists(sortedEvents: [Event]) -> [EventsList] {
guard sortedEvents.count > 0 else { return [] }
var currentEventList = EventsList()
var result = [currentEventList]
var lastDate = sortedEvents.first!.date
for event in sortedEvents {
let newDate = event.date.compare(lastDate!) != .orderedDescending
if newDate {
currentEventList = EventsList()
result.append(currentEventList)
}
currentEventList.events.append(event)
lastDate = event.date
}
return result
}

Dictionary inside dictionary

I am trying to use a list that is a value for a dictionary key/pair set, and this dictionary is itself a value in a key/pair set in a dictionary. To explain, this is how I initialize it.
var dictOfEvents = [Int: [Int: [PFObject]]]()
I am trying to add events to the list, with the inner dictionary's key being the day of month and the outer one being the month. For example, an event on May 1 would be:
dictOfEvents[5:[1:[ListOfEvents]]
Where ListOfEvents is an array of PFObjects. Before I added the month functionality, and thus the outer dictionary, the way I added new events was:
` self.dictOfEvents[components.day] = [event]
But now, when I try to extend this with:
self.dictOfEvents[components.month]?[components.day]! = [event]
It does not work. Any explanation on how to create new event lists and access this double layer dictionary would be greatly appreciated.
(Note: I don't know where to put the ! and the ? in the last piece of code so please excuse me if I made a mistake.)
Here is what I think could be a good use of optionals in your case (and should respond to your question):
var dic: [Int: [Int: [String]]] = [:]
dic[5] = [1:["Hello", "World"]]
if let list = dic[5]?[1] {
// your list exist and you can safely use it
for item in list {
println(item)
}
}
I just used String instead of PFObject.
A different approach could be:
/*
Define a struct to encapsulate your Month and Day
Make it Hashable so that you can use it as Dictionary key
*/
public struct MonthDay: Hashable {
let month: Int
let day: Int
public var hashValue: Int { return month * 100 + day }
}
public func ==(lhs: MonthDay, rhs: MonthDay) -> Bool {
return lhs.month == rhs.month && lhs.day == rhs.day
}
var dictOfEvents = [MonthDay :[String]]()
let aMonthAndDay = MonthDay(month: 5, day: 1)
dictOfEvents[aMonthAndDay] = ["Hello", "World"]
if let list = dictOfEvents[aMonthAndDay] {
// your list exist and you can safely use it
for item in list {
println(item)
}
}
U can simple change:
self.dictOfEvents[components.month]?[components.day]! = [event]
to :
self.dictOfEvents[components.month]![components.day]! = [event]
Because Dictionary has subscript, Dictionary? doesn't have subscript.
if U try add Events to Dictionary. I suggest to use this:
var dictOfEvents = [Int: [Int: [PFObject]]]()
var dictOfDayEvents = [Int:[PFObject]]()
dictOfDayEvents.updateValue([PFObject()], forKey: 1)
dictOfEvents.updateValue(dictOfDayEvents, forKey: 5)

Resources