Group from Array to Dictionary - ios

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.

Related

How to add the elements with the same ending number in the same array? (Swift 3)

I have my code below and I am attempting to create new arrays, in which the numbers in the elements increment by 1 whenever the user adds a book. For instance, when the user already has 1 book added and when he adds another one the array should read as ["bookTitle2,"bookAuthor2", "bookPublisher2", "bookNumOfPages2"]
let bookDetails = ["bookTitle", "bookAuthor", "bookPublisher", "bookNumOfPages"]
var bookDetail = ""
var bookNumber = Int()
var bookNumArray = [Int]()
if bookNumArray.contains(bookNumber) {
print("Book already exists")
} else {
while bookNumber < 2 {
bookNumber += 1
bookNumArray.append(bookNumber)
for detail in bookDetails {
bookDetail = "\(detail)" + String(bookNumber)
let newBookArray = [bookDetail]
print(newBookArray)
}
}
}
When I run the code above, this shows up instead:
["bookTitle1"]
["bookAuthor1"]
["bookPublisher1"]
["bookNumOfPages1"]
["bookTitle2"]
["bookAuthor2"]
["bookPublisher2"]
["bookNumOfPages2"]
So I want all the strings that end with 1 in one array and those that end in 2 in another array.
When you do:
for detail in bookDetails {
bookDetail = "\(detail)" + String(bookNumber)
let newBookArray = [bookDetail]
print(newBookArray)
}
For every iteration you are creating a new array with the current detail e.g bookTitle into an array of your string var bookDetail declared outside of this loop's scope.
Also note that newBookArray is a local variable, so it will be destroyed when it gets out of the loop. You would need an array of bookDetail to store the newBookArray.
let bookDetails = ["bookTitle", "bookAuthor", "bookPublisher", "bookNumOfPages"]
var bookDetailArray : [Array<String>] = [] //Your array to store all the bookDetails
var bookDetail : [String] = [] //Your array to store a bookDetail
var bookNumber = Int()
var bookNumArray = [Int]()
Then you can do:
bookDetail.removeAll() //Clear all objects before appending new one
for detail in bookDetails {
bookDetail.append("\(detail) + String(bookNumber)")
}
bookDetailArray.append(bookDetail)
Just a suggestion: As other people said, a dictionary or a class for the bookDetail properties would be a better model in your case. Read up on Object-oriented programming if you ever plan to use a class.
I would do a dictionary or create a class bookDetails with properties like bookTitle, bookAuthor, etc. And then I would create an array of the instances of this class.
If you want to do it your way, Why not create a two-way array, something like:
var arrayFinal = [[""]]
var bookNumber = 0
// Whenever the action is triggered
bookNumber += 1
var bookDetails = ["bookTitle", "bookAuthor", "bookPublisher", "bookNumOfPages"]
for detail in bookDetails
{
detail = "\(detail) +\(bookNumber)"
}
arrayFinal.add(bookDetails)
Or something like that...

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
}

Sort arrays of protocols in Swift?

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().

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)

Swift: Properly initialize and access my classes in an array

This is from a Swift playground; I want to collect my 'Task' class instances in an array so each task's properties can be used in a tableview. I'm searching for documentation but I don't see a discussion of how to properly initialize and access my classes in an array.
The last line of code gives an error that reads AnyObject does not have member named deadline. It's the same whether I use a Swift Array of AnyObjects, or an empty NSMutableArray. I tried various ways of casting it as an Integer, but it insists on this error. Am I going about this the wrong way to store and then access data? I'm javascript-brained, sometimes the similarity of the syntax leads me to delusions.
import UIKit
let todays_date = NSDate() // now
let cal = NSCalendar.currentCalendar()
let day_number = cal.ordinalityOfUnit(.CalendarUnitDay, inUnit: .CalendarUnitYear, forDate: todays_date)
var tasks = [] as NSMutableArray
class Task {
var title: String
var interval: Int
var dateActivated: Int
var deadline: Int
var flag_count: Int = 0
var isActivated: Bool = false
init(title: String, dateActivated: Int, interval: Int) {
//initialized by selecting new
self.title = title
self.dateActivated = dateActivated
self.interval = interval
//calculated
self.deadline = dateActivated + interval
}
}
var task1 = Task(title: "Laundry", dateActivated: day_number, interval: 7)
var task2 = Task(title: "Dishes", dateActivated: day_number, interval: 7)
task1.deadline
task2.flag_count
tasks.addObject(task1)
tasks.addObject(task2)
tasks[0].deadline //AnyObject does not have member named deadline
No need to use foundation Arrays here, simply declare your array as [Task] (after declaring the Task class):
var tasks = [Task]()
Then use append instead of addObject:
tasks.append(task1)
tasks.append(task2)
tasks[0].deadline // 83

Resources