Isuru
Isuru

Reputation: 31283

Filtering arrays based on a property value

There are two classes called Event and Agenda.

class Event {
    let id: Int
    var title: String
    var allDay: Int = 0
    let location: String
    var description: String?
    var startDate: NSDate!
    var endDate: NSDate!
}

class Agenda {
    var date: NSDate!
    var events = [Event]()
}

And I have an array of these Event objects.

I need to filter this events array out based on it's startDate property.

For example say there are 11 events. (I'm only showing the startDates here)

2015-07-20
2015-07-21
2015-07-21
2015-07-22
2015-07-22
2015-07-22
2015-07-23
2015-07-23
2015-07-24
2015-07-24
2015-07-24

I need to filter this array and create an array of Agenda objects. Like this.

+------------+----------------+
|    date    | events (count) |
+------------+----------------+
| 2015-07-20 |              1 |
| 2015-07-21 |              2 |
| 2015-07-22 |              3 |
| 2015-07-23 |              2 |
| 2015-07-24 |              3 |
+------------+----------------+

I tried iterating through the events array and do it like below.

public func filterByDate(events: [Event]) {
    var agendas = [Agenda]()
    for event in events {
        if agendas.isEmpty {
            var agenda = Agenda()
            agenda.date = event.startDate!
            agenda.events.append(event)

            agendas.append(agenda)
        } else {
            let lastAgenda = agendas.last!
            let lastEventOfLastAgenda = lastAgenda.events.last!

            if isSameDate(date1: event.startDate!, date2: lastEventOfLastAgenda.startDate!) {
                lastAgenda.events.append(event)
            } else {
                var agenda = Agenda()
                agenda.date = event.startDate!
                agenda.events.append(event)

                agendas.append(agenda)
            }
        }
    }
}

func isSameDate(#date1: NSDate, date2: NSDate) -> Bool {
    let calendar = NSCalendar.currentCalendar()
    let components: NSCalendarUnit = .CalendarUnitDay | .CalendarUnitMonth | .CalendarUnitYear

    let date1Components = calendar.components(components, fromDate: date1)
    let date2Components = calendar.components(components, fromDate: date2)

    let date1 = calendar.dateFromComponents(date1Components)!
    let date2 = calendar.dateFromComponents(date2Components)!
    let result = date1.compare(date2)
    if result == NSComparisonResult.OrderedSame {
        return true
    }
    return false
}

But the result I get is incorrect. I get 6 objects in the agendas array. When I just iterate through it, I get the following output.

2015-07-20  -   1
2015-07-21  -   1
2015-07-21  -   2
2015-07-22  -   4
2015-07-24  -   1
2015-07-24  -   2

I can't figure out where it's going wrong. Any help would be appreciated.

By the way even if I get this method to work it's still very messy and not very Swift-like. Is there a more elegant way to do this in Swift?

Upvotes: 2

Views: 170

Answers (2)

simons
simons

Reputation: 2400

Edit: I have further simplified this by adding an itit() to the Agenda class. I believe that this concisely does what you have asked (this is Swift 2 in Xcode 7 beta 4). It creates an array of Agenda objects [Agenda] called agendaArray.

Assuming we have an array of Event objects called eventArray where Event is declared as in your question.

Here is my solution:

var agendaArray = [Agenda]()
for date in Set(eventArray.filter{$0.startDate != nil}.map{$0.startDate}) {
    agendaArray.append(Agenda(date: date, events: eventArray.filter({$0.startDate == date})))
} // populates [Agenda] for each unique date

Here is the modified Agenda class:

class Agenda {
// ...
init(date: NSDate!, events: [Event]){
    self.date = date
    self.events = events
}

Quickly test it (first have to sort by date since resulting array has no fixed order):

// quickly test it
agendaArray = agendaArray.sort({ $0.date.compare($1.date) == NSComparisonResult.OrderedAscending }) // sort it by date
for eachAgenda in agendaArray {
    print("\(eachAgenda.date)")
    print("count = \(eachAgenda.events.count)")
}

Upvotes: 0

icodesign
icodesign

Reputation: 876

Try a more elegant way:

var events: [Event] = Your Event Array Here ...

var groupedDates: [NSDate: Int] = [:] 

for event in events {
    if let number = groupedDates[event.startDate] {
        groupedDates[event.startDate] = number + 1
    }else{
        groupedDates[event.startDate] = 1
    }
}

println(groupedDates)

Upvotes: 1

Related Questions