【问题标题】:How to group array of objects by date in swift?如何快速按日期对对象数组进行分组?
【发布时间】:2017-10-29 17:47:51
【问题描述】:

需要按日期分组。响应采用排序格式。需要在日期上应用过滤器来分组。 来自后端的响应:

[
    {
    "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
            }
        ]
    ]
]

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

【问题讨论】:

  • 您如何对对象进行分组?依据什么逻辑?
  • 发布您的解决方案。

标签: ios arrays swift date grouping


【解决方案1】:

修改 ElegyD 处理可选日期对象的解决方案

    fileprivate func compare(date:Date, to:Date, toGranularity:Calendar.Component)->ComparisonResult{
    return Calendar.current.compare(date, to: to, toGranularity: toGranularity)
}

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

用法 结构数据项{ 变量日期:日期? }

让 items = [DataItem(), DataItem(date:Date())] let grouped = items.groupSort(toGranularity: .month, byDate: {$0.date })

【讨论】:

    【解决方案2】:

    您可以使用数组扩展。

    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)
    

    【讨论】:

      【解决方案3】:

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

      let groupDic = Dictionary(grouping: arr) { (pendingCamera) -> DateComponents in
      
          let date = Calendar.current.dateComponents([.day, .year, .month], from: (pendingCamera.date)!)
      
          return date
      }
      

      【讨论】:

        【解决方案4】:

        谢谢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"]]
        

        【讨论】:

        • 你好,你能告诉我组后对象的确切类型吗,例如:[[Date : [Model]],我试图将对象传递给不工作
        • @famfamfam,我们将有类型 [[Model]]。
        • 谢谢,但是很难用section+row和这种数据映射到tableview中,我卡住了:(
        • 我认为更好地更改为 [[Date :[Model]] 以便于处理谢谢
        【解决方案5】:

        您可以像这样使用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 的打印顺序可能会改变。

        【讨论】:

        • [ [ "date": "2017-05-30T12:40:39.000Z", "message": [ [ "date_time": 2017-05-30T12: 40: 39.000Z ] ] ] ,[“日期”:“2017-05-31T05:43:17.000Z”,“消息”:[[“日期时间”:2017-05-31T05:43:17.000Z],[“日期时间”:2017-05- 31T05: 44: 15.000Z ] ] ] 我能够实现这样的目标。没有使用您的代码。
        • @SahilJaidka 你能解释一下你的意思吗?另外编辑你的问题,而不是在评论中添加JSON
        猜你喜欢
        • 1970-01-01
        • 2019-02-20
        • 2019-03-05
        • 1970-01-01
        • 2013-10-26
        • 1970-01-01
        • 2015-06-26
        • 1970-01-01
        • 2015-11-19
        相关资源
        最近更新 更多