【问题标题】:Sort an array of objects by date and section them accordingly in TableView按日期对对象数组进行排序,并在 TableView 中对它们进行相应的分段
【发布时间】:2020-09-25 12:44:01
【问题描述】:

我有一个应用程序,其中包含允许用户将餐点上传到 Firebase 以供以后查看,我想显示这些餐点,这些餐点按记录日期排序,并在 TableView 中以降序(按日期)相应地划分。

Meal 对象定义如下

class Meal: NSObject, Codable {

var id: String?
var foodname: String?
var quantity: Float!
var brandName: String?
var quantityType: String?
var calories: Float!
var date: Date?
}

而我的 TableView 目前看起来是这样的

class MealsTableViewController: UITableViewController {

var databaseController: DatabaseProtocol?
var sections = Dictionary<String, Array<Meal>>()
var sortedSections = [String]()

var listOfMeals = [Meal]() {
    didSet {
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
}

override func viewDidLoad() {
    super.viewDidLoad()


    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    databaseController = appDelegate.databaseController
    let accountsRef = databaseController?.getAccountsRef()

    accountsRef?.document(SingletonAccount.shared.userEmail).collection("meals")
        .getDocuments { (querySnapshot, error) in

            if error != nil {
                print("Error retrieving all meals!")
            }else{
                do{
                    for document in querySnapshot!.documents
                    {
                        let currentMeal = Meal()
                        let data = try! document.data()
                        currentMeal.id = data["id"] as? String ?? ""
                        currentMeal.brandName = data["brandName"] as? String ?? ""
                        currentMeal.calories = data["calories"] as? Float ?? 0.0
                        let parsingDate = data["date"] as! Timestamp
                        currentMeal.date = parsingDate.dateValue()
                        currentMeal.foodname = data["foodname"] as? String
                        currentMeal.quantity = data["quantity"] as? Float
                        currentMeal.quantityType = data["quantityType"] as? String
                        print(currentMeal.date)
                        self.listOfMeals.append(currentMeal)
                     }

                }catch{
                    print(error)
                }
            }
        }




}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return listOfMeals.count
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "foodCell", for: indexPath)
    cell.textLabel?.text = listOfMeals[indexPath.row].foodname
    cell.detailTextLabel?.text = String(listOfMeals[indexPath.row].calories) + "kJ"
    cell.detailTextLabel?.textColor = UIColor.secondaryLabel
    cell.accessoryType = .disclosureIndicator
    return cell
}

我也尝试使用下面的代码尝试按日期对对象进行排序,但它不起作用。

                self.listOfMeals.sort { (meal1, meal2) -> Bool in
                return (meal1.date!.compare(meal2.date!))
            }

我尝试了这里发布的其他方法,但它们似乎都不起作用

【问题讨论】:

  • 小心使用 getDocuments,因为它会读取整个数据库中的每一餐。随着时间的推移,这可能是数以万计的饭菜,并且试图一次将它们全部加载然后排序可能会使设备不堪重负(而且真的很慢)。添加限制以读取较小的数据部分要好得多。有关更多信息,请参阅我的答案。

标签: swift firebase uitableview google-cloud-firestore tableview


【解决方案1】:

我相信compare 方法不是您所需要的,因为它会在两个日期之间返回一个ComparisonResult 实例。此外,如果日期是可选的,请不要强制转换它们,否则当其中一个为 nil 时,您的应用可能会崩溃。

self.listOfMeals.sort { meal1, meal2 -> Bool in
    guard let meal1Date = meal1.date, let meal2Date = meal2.date else { return false }
    return meal1Date < meal2Date
}

【讨论】:

    【解决方案2】:

    如果您想一次性对对象进行排序和分组,可以将Dictionary(grouping:by:) 与排序一起使用

    let calendar = Calendar.current
    let nilDate = Date.distantPast
    
    let grouped = Dictionary(grouping: listOfMeals.sorted(by: { ($0.date ?? nilDate) < ($1.date ?? nilDate) }),
                             by: { calendar.startOfDay(for: $0.date ?? nilDate) })
    

    下面是一个简单的测试用例

    struct Meal: CustomStringConvertible {
        var id: String?
        var date: Date?
    
        var description: String {
            get {
                let dateString = date == nil ? "nil" : String(describing: date!)
                return "\(id!) \(dateString)"
            }
        }
    }
    
    let day = 24.0 * 60.0 * 60.0
    var listOfMeals = [
        Meal(id: "a", date: Date(timeIntervalSinceNow: 5000)),
        Meal(id: "b", date: Date(timeIntervalSinceNow: -5000)),
        Meal(id: "c", date: Date(timeIntervalSinceNow: (day + 5000.0))),
        Meal(id: "d", date: Date(timeIntervalSinceNow: day)),
        Meal(id: "e", date: Date(timeIntervalSinceNow: -day)),
        Meal(id: "f", date: Date(timeIntervalSinceNow: -(day - 5000.0))),
        Meal(id: "g", date: Date(timeIntervalSinceNow: 0)),
        Meal(id: "h", date: Date(timeIntervalSinceNow: -2 * day)),
    ]
    

    【讨论】:

    • 如何指定表格中的节数并从分组中访问日期元素?对不起,我还不熟悉字典
    • 字典有一个属性keys,它是一个数组,所以你可以访问count属性grouped.keys.count
    【解决方案3】:

    我可能会误解,但似乎问题是问如何阅读按日期降序排列的餐食。

    您不会希望阅读其他答案中显示的每顿饭,然后在内存中对它们进行排序,因为这可能是海量的数据。请注意,您可以将limits 添加到我的代码中,也可以仅显示 6 月份的餐食,或者一次仅显示 10 餐。

    让 Firestore 为您完成这项工作。这是在您的膳食对象中读取的代码,按降序排序。

    func readMealsSortedDesc() {
        let meals = self.db.collection("meals") //self.db points to my Firestore
        meals.order(by: "date", descending: true).getDocuments(completion: { snapshot, error in
            if let err = error {
                print(err.localizedDescription)
                return
            }
    
            guard let documents = snapshot?.documents else { return }
    
            for doc in documents {
                let docId = doc.documentID
                let name = doc.get("foodname") as? String ?? "No Food Name"
                let date = doc.get("date") as? Timestamp ?? Timestamp()
                print(docId, name, date.dateValue())
            }
        })
    }
    

    如果 6 月有三顿饭(加上仅 6 月的限制),1 日一份,15 日一份,30 日一份,这是输出

    meal_2 Hot Dog 2020-06-30 04:00:00 +0000
    meal_1 Burger 2020-06-15 04:00:00 +0000
    meal_0 Pizza 2020-06-01 04:00:00 +0000
    

    我不知道具体要如何组织数据,但您应该有一个 dataSource 数组,其中包含表示节的对象并保存节子数据。像这样的

    var myDailyMealsArray = [DailyMealStruct]()
    
    struct DailyMealStruct {
        var sectionTitle = "this would be a string of the date"
        var mealNameArray = [String]()
    }
    

    因此,当您从 Firebase 读取数据时,将其添加到相应的 DailyMealStruct 对象(该对象中 2020 年 6 月 1 日的所有餐点,然后该对象中 2020 年 6 月 2 日的所有餐点等)

    从那里,您的部分是 myDailyMealsArray 中的对象数,子数据来自每个对象内的 mealNameArray。

    func numberOfSections(in tableView: UITableView) -> Int {
        return self.myDailyMealsArray.count
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        let title = self.myDailyMealsArray[section].sectionTitle
        return title
    }
    

    【讨论】:

      猜你喜欢
      • 2015-09-28
      • 2011-10-30
      • 1970-01-01
      • 2015-06-20
      • 1970-01-01
      • 2016-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多