如何在swift中按日期分组对象数组?

时间:2017-05-29 12:09:01

标签: ios arrays swift date grouping

需要按日期分组。响应将以排序格式显示。需要将日期过滤器应用于组。 响应来自后端:

[
    {
    "date": "date1"
    }, 
    {
    "date": "date1"
    },
    {
    "date": "date1"
    },
    {
    "date": "date2"
    },
    {
    "date": "date2"
    },
    {
    "date": "date3"
    }
]

必填:

[
    [
        "date": "2017-05-30T12:40:39.000Z",
        "message": [
            {
                "date_time": 2017-05-30T12: 40: 39.000Z
            }
        ]
    ],
    [
        "date": "2017-05-31T05:43:17.000Z",
        "message": [
            {
                "date_time": 2017-05-31T05: 43: 17.000Z
            },
            {
                "date_time": 2017-05-31T05: 44: 15.000Z
            },
            {
                "date_time": 2017-05-31T05: 44: 38.000Z
            }
        ]
    ]
]

我已经检查了多个答案,但无法找到一个好的解决方案。

5 个答案:

答案 0 :(得分:4)

您可以像这样使用flatMapfilter将数组分组到字典中。

let datesArray = yourArray.flatMap { $0["date"] as? String } // return array of date
var dic = [String:[[String:Any]]]() // Your required result
datesArray.forEach {
    let dateKey = $0
    let filterArray = yourArray.filter { $0["date"] as? String == dateKey }
    dic[$0] = filterArray
}
print(dic)

注意:确保字典没有任何订单,因此date的打印顺序可能会发生变化。

答案 1 :(得分:3)

这是我按日期(仅日,月和年)分组的解决方案:

let groupDic = Dictionary(grouping: arr) { (pendingCamera) -> DateComponents in

    let date = Calendar.current.dateComponents([.day, .year, .month], from: (pendingCamera.date)!)

    return date
}

答案 2 :(得分:2)

谢谢ElegyD。这对我有用。

extension Sequence {
    func groupSort(ascending: Bool = true, byDate dateKey: (Iterator.Element) -> Date) -> [[Iterator.Element]] {
        var categories: [[Iterator.Element]] = []
        for element in self {
            let key = dateKey(element)
            guard let dayIndex = categories.index(where: { $0.contains(where: { Calendar.current.isDate(dateKey($0), inSameDayAs: key) }) }) else {
                guard let nextIndex = categories.index(where: { $0.contains(where: { dateKey($0).compare(key) == (ascending ? .orderedDescending : .orderedAscending) }) }) else {
                    categories.append([element])
                    continue
                }
                categories.insert([element], at: nextIndex)
                continue
            }

            guard let nextIndex = categories[dayIndex].index(where: { dateKey($0).compare(key) == (ascending ? .orderedDescending : .orderedAscending) }) else {
                categories[dayIndex].append(element)
                continue
            }
            categories[dayIndex].insert(element, at: nextIndex)
        }
        return categories
    }
}

用法:

class Model {
    let date: Date!
    let anotherProperty: String!

    init(date: Date, _ anotherProperty: String) {
        self.date = date
        self.anotherProperty = anotherProperty
    }
}

let modelArray = [
    Model(date: Date(), anotherProperty: "Original Date"),
    Model(date: Date().addingTimeInterval(86400), anotherProperty: "+1 day"),
    Model(date: Date().addingTimeInterval(172800), anotherProperty: "+2 days"),
    Model(date: Date().addingTimeInterval(86401), anotherProperty: "+1 day & +1 second"),
    Model(date: Date().addingTimeInterval(172801), anotherProperty: "+2 days & +1 second"),
    Model(date: Date().addingTimeInterval(86400), anotherProperty: "+1 day"),
    Model(date: Date().addingTimeInterval(172800), anotherProperty: "+2 days")
]

let groupSorted = modelArray.groupSort(byDate: { $0.date })
print(groupSorted) // [["Original Date"], ["+1 day", "+1 day", "+1 day & +1 second"], ["+2 days", "+2 days", "+2 days & +1 second"]]

let groupSortedDesc = modelArray.groupSort(ascending: false, byDate: { $0.date })
print(groupSortedDesc) // [["+2 days & +1 second", "+2 days", "+2 days"], ["+1 day & +1 second", "+1 day", "+1 day"], ["Original Date"]]

答案 3 :(得分:1)

您可以使用数组扩展名。

extension Array {
  func sliced(by dateComponents: Set<Calendar.Component>, for key: KeyPath<Element, Date>) -> [Date: [Element]] {
    let initial: [Date: [Element]] = [:]
    let groupedByDateComponents = reduce(into: initial) { acc, cur in
      let components = Calendar.current.dateComponents(dateComponents, from: cur[keyPath: key])
      let date = Calendar.current.date(from: components)!
      let existing = acc[date] ?? []
      acc[date] = existing + [cur]
    }

    return groupedByDateComponents
  }
}

您可以使用Swift 5 KeyPath在任何模型属性上使用它

let grouped = models.sliced(by: [.year, .month, .day], for: \.createdAt)

答案 4 :(得分:0)

以下是swift 3的答案,其中dateList()是带有项目列表的函数 按日期排序 - descending  至于guard var lastDate = sortedDates.first,你必须约会  从你的第一个项目。

func datesListGroupedByDay(sortedDates: [Date]) -> [[Date]] {
    guard !sortedDates.isEmpty, 
           var lastDate = sortedDates.first? else { return [] }

    let calendar = NSCalendar.current
    var lastGroup: [Date] = []
    var groups: [[Date]] = []

    sortedDates.forEach { date in
        guard let currentDate = date,
                    let currentDateDayBegin = currentDate.dateAtBeginningOfDay(),
                    let lastDateDayBegin = lastDate.dateAtBeginningOfDay() else { return }

        let difference = calendar.dateComponents([.year, .month, .day],
                                                 from: currentDateDayBegin,
                                                 to: lastDateDayBegin)
        guard let differenceYear = difference.year,
                let differenceMonth = difference.month,
                let differenceDay = difference.day else { return }

        if differenceYear > 0 || differenceMonth > 0 || differenceDay > 0 {
            lastDate = currentDate
            groups.append(lastGroup)
            lastGroup.removeAll()
        }

        lastGroup.append(date)
    }
    groups.append(lastGroup)

    return groups
}

extension Date {
    func dateAtBeginningOfDay() -> Date? {
        var calendar = Calendar.current
        // Or whatever you need 
        // if server returns date in UTC better to use UTC too
        let timeZone = NSTimeZone.system
        calendar.timeZone = timeZone

        // Selectively convert the date components (year, month, day) of the input date
        var dateComps = calendar.dateComponents([.year, .month, .day], from: self)
        // Set the time components manually
        dateComps.hour = 0
        dateComps.minute = 0
        dateComps.second = 0

        // Convert back
        let beginningOfDay = calendar.date(from: dateComps)
        return beginningOfDay
    }
}