根据对象参数 UPDATED Swift 从 fetch 中删除重复的对象 [英] Removing duplicate objects from fetch based on object parameter UPDATED Swift

查看:17
本文介绍了根据对象参数 UPDATED Swift 从 fetch 中删除重复的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个由 FetchResultsController 填充的 TableVIew.获取的项目在它们自己的部分中正确显示,但我想要实现的是只显示一次相同类型的对象,但存储已获取的对象数量.示例:实体名称:Item,实体属性:itemId:Stringcategory:String.category 用于对 fetch 进行排序并创建 Tableview 部分.因此,如果我为同一个 itemId 获取的对象有三个单元格,我只想显示一个单元格并计算它们应该显示的数量,并将其显示在唯一显示的单元格的标签中.我正在尝试使用 itemFetchRequest.propertiesToFetch = ["itemId"]itemFetchRequest.returnsDistinctResults = true 这应该消除基于 itemId 的所有重复项Item 实体的属性,但我仍然得到多个具有相同项目的单元格.你能发现为什么 itemFetchController 返回同一个项目的倍数吗?

I have a TableVIewthat gets populated by a FetchResultsController. Fetched items are properly displayed in their own section but what I want to achieve is to show only once the same type of object but store the number of objects that has been fetched. Example: Entity name: Item, entity attribute: itemId: String, category: String. category is used to sort the fetch and create Tableviewsections. So if I had three cell for the same itemId fetched object I just want to display one cell and keep count of how many they were supposed to be displayed, and display it in a label in the only displayed cell. I'm trying to use itemFetchRequest.propertiesToFetch = ["itemId"]and itemFetchRequest.returnsDistinctResults = truethat should eliminate all duplicates based on itemId attribute of Item entity but I still get more than one cell with the same items. Can you spot why itemFetchController is returning multiples of the same item?

这是我目前想出的代码cellForRowAt:

This is the code I came up with so far cellForRowAt:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "statisticsCell", for: indexPath) as! StatisticsTableViewCell
    let productPrice: String!
    cell.idInfoLabel.text = itemFetchedResultController?.object(at: indexPath).itemId!
    cell.nameInfoLabel.text = itemFetchedResultController?.object(at: indexPath).itemName!
    // get items details(image, price, minimum stock quantity) from Product Entity
    let item = itemFetchedResultController?.object(at: indexPath).itemName!
        let productRequest: NSFetchRequest<Product> = Product.fetchRequest()
        productRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
    productRequest.predicate = NSPredicate(format: "name == %@", item!)
        productRequest.fetchLimit = 1
        do {
            let fetch = try context.fetch(productRequest)
            cell.productImageView.image = UIImage(data: (fetch[0].productImage! as Data))
            cell.minimumStockInfoLabel.text = fetch[0].minimumStock
            productPrice = fetch[0].price

            //fetch itemes for amount of single object
            let itemId = itemFetchedResultController?.object(at: indexPath).itemId!

            print(itemId!)
            let itemRequest = NSFetchRequest<Item>(entityName: "Item")
            itemRequest.sortDescriptors = [NSSortDescriptor(key: "itemName", ascending: true)]
            itemRequest.predicate = NSPredicate(format: "date BEGINSWITH %@", dateToFetchMin)
            itemRequest.predicate = NSPredicate(format: "itemId == %@", itemId!)
            do {
                let itemFetch = try context.fetch(itemRequest)
                print(productPrice)
                print(itemFetch, itemFetch.count)
                cell.soldQuantityInfoLabel.text = String(describing: itemFetch.count)
                let amount = Double(productPrice!)! * Double(itemFetch.count)
                cell.salesAmountInfoLabel.text = String(describing: amount)
            } catch  {
                print("Error in fetching sold items for cell: (error)")
            }
        } catch  {
            print("Error in fetching product for cell: (error)")
        }
    return cell
}

FetchResultController:

var itemFetchedResultController: NSFetchedResultsController<Item>?

和获取函数:

func configureItemFetchedResultsController() {
        print("configureItemFetchedResultsController(): started")

        // first sortDescriptor filters the date range:  possibly change date from String to dates in both function and  CoreData and use "(date >= %@) AND (date <= %@)" instead of "BEGINSWITH" in predicate
        let itemFetchRequest = NSFetchRequest<Item>(entityName: "Item")
        itemFetchRequest.sortDescriptors = [NSSortDescriptor(key: "category", ascending: true),NSSortDescriptor(key: "itemId", ascending: true)]
        itemFetchRequest.predicate = NSPredicate(format: "order.user.name == %@", UserDetails.fullName ?? "")
        itemFetchRequest.predicate = NSPredicate(format: "date BEGINSWITH %@", dateToFetchMin)
        itemFetchRequest.propertiesToFetch = ["itemId"]
        itemFetchRequest.returnsDistinctResults = true
        //        itemFetchRequest.propertiesToGroupBy = ["category","itemId","itemName"]
        //        itemFetchRequest.resultType = .dictionaryResultType
        itemFetchedResultController = NSFetchedResultsController(fetchRequest: itemFetchRequest, managedObjectContext: context, sectionNameKeyPath: "category", cacheName: nil)
        do {
            try itemFetchedResultController?.performFetch()
            self.statisticsTableView.reloadData()
            print("configureItemFetchedResultsController(): sold items fetched")
        } catch  {
            //            fatalError("failed to fetch entities: (error)")
            print("configureItemFetchedResultsController(): failed to fetch Item entities: (error)")
        }
        self.statisticsTableView.reloadData()
    }

实际TableView结果:

更新:

在尝试使用 Dictionary 路由并使用 itemFetchRequest.propertiesToFetch = ["category","itemId","itemName"]itemFetchRequest.propertiesToGroupBy 之后= ["category","itemId","itemName"] 我终于得到了 fetch 结果我希望每个 itemId 只有一个对象,代价是没有将它们正确划分为以参数 category 命名的部分.然后我决定返回使用 itemFetchResultsController 来执行获取,我得到了相同的获取对象,所以使用 itemFetchRequest.propertiesToFetch = ["category","itemId","itemName"]itemFetchRequest.propertiesToGroupBy = ["category","itemId","itemName"] 现在使 .distinctResults 工作.我现在的问题是 cellForRowAt.在版本 1 中,我在 let item = itemFetchedResultController!.fetchedObjects![indexPath.row] 行上得到 Thread 1: Fatal error: NSArray element failed to match the Swift Array Element type.将其转换为 NSArray 并没有解决它.对此有什么想法吗?

After trying to go the Dictionaryroute and using itemFetchRequest.propertiesToFetch = ["category","itemId","itemName"] and itemFetchRequest.propertiesToGroupBy = ["category","itemId","itemName"] I finally got the fetch result I wanted being only one object per itemId, at cost of not having them properly divided into sections named after the parameter category. I decided then to go back using itemFetchResultsControllerto perform the fetch and I get the same fetched objects so using itemFetchRequest.propertiesToFetch = ["category","itemId","itemName"] and itemFetchRequest.propertiesToGroupBy = ["category","itemId","itemName"] makes now .distinctResults work. My problem now is in cellForRowAt. In version1 I get Thread 1: Fatal error: NSArray element failed to match the Swift Array Element typeon the line let item = itemFetchedResultController!.fetchedObjects![indexPath.row]. Casting it as NSArraydidnt solve it. Any ideas on this?

在版本 2 中,我得到 *** 由于未捕获的异常NSInvalidArgumentException"而终止应用程序,原因:-[NSKnownKeysDictionary1 itemName]:无法识别的选择器发送到实例 0x60000078a2e0'.

In version2 instead I get *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSKnownKeysDictionary1 itemName]: unrecognized selector sent to instance 0x60000078a2e0'.

所以新的代码是:

FetchResultController:

func configureItemFetchedResultsController() {
        print("configureItemFetchedResultsController(): started")
        let itemFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Item")
        itemFetchRequest.sortDescriptors = [NSSortDescriptor(key: "category", ascending: true),NSSortDescriptor(key: "itemId", ascending: true)]
        let user = NSPredicate(format: "order.user.name == %@", UserDetails.fullName ?? "")
        let dateFrom = Conversions.dateConvert(dateString: dateToFetchMin)!
        let dateTo = Conversions.dateConvert(dateString: dateToFetchMax)!
        print(dateFrom)
        let from = NSPredicate(format: "date >= %@", dateFrom as CVarArg)
        let to = NSPredicate(format: "date <= %@", dateTo as CVarArg)
        itemFetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [user,from,to])
        itemFetchRequest.propertiesToFetch = ["category","itemId","itemName"]]
        itemFetchRequest.returnsDistinctResults = true
        itemFetchRequest.propertiesToGroupBy = ["category","itemId","itemName"]
        itemFetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType 

        itemFetchedResultController = NSFetchedResultsController(fetchRequest: itemFetchRequest, managedObjectContext: context, sectionNameKeyPath: "category", cacheName: nil) as? NSFetchedResultsController<Item>
        do {
            try itemFetchedResultController?.performFetch()
            let resultsDict = itemFetchedResultController!.fetchedObjects!
            print(resultsDict as NSArray)
            print("configureItemFetchedResultsController(): sold items fetched")
        } catch  {
            //            fatalError("failed to fetch entities: (error)")
            print("configureItemFetchedResultsController(): failed to fetch Item entities: (error)")
        }
        self.statisticsTableView.reloadData()
    }

cellForRowAt 版本 1:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let item = itemFetchedResultController!.fetchedObjects![indexPath.row] //as NSArray
        let name = item.itemName!//["itemName"]!
        let itemId = item.itemId!
//        let productPrice: String!
        let cell = tableView.dequeueReusableCell(withIdentifier: "statisticsCell", for: indexPath) as! StatisticsTableViewCell
        let productRequest: NSFetchRequest<Product> = Product.fetchRequest()
        productRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
        productRequest.predicate = NSPredicate(format: "name == %@", name)
        productRequest.fetchLimit = 1
        do {
            let fetch = try context.fetch(productRequest)
            cell.idInfoLabel.text = fetch[0].productId
            cell.nameInfoLabel.text = fetch[0].name
            cell.productImageView.image = UIImage(data: (fetch[0].productImage! as Data))
            cell.minimumStockInfoLabel.text = fetch[0].minimumStock
            let productPrice = fetch[0].price
            //fetch itemes for amount of single object
            let itemRequest = NSFetchRequest<Item>(entityName: "Item")
            itemRequest.sortDescriptors = [NSSortDescriptor(key: "itemName", ascending: true)]
            itemRequest.predicate = NSPredicate(format: "date BEGINSWITH %@", dateToFetchMin)
            itemRequest.predicate = NSPredicate(format: "itemId == %@", itemId)
            do {
                let itemFetch = try context.fetch(itemRequest)
                print(productPrice!)
                print(itemFetch, itemFetch.count)
                cell.soldQuantityInfoLabel.text = String(describing: itemFetch.count)
                let amount = Double(productPrice!)! * Double(itemFetch.count)
                cell.salesAmountInfoLabel.text = String(describing: amount)
            } catch  {
                print("Error in fetching sold items for cell: (error)")
            }
        } catch  {
            print("Error in fetching product for cell: (error)")
        }
        return cell
    }

cellForRowAtversion2 :

cellForRowAtversion2 :

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "statisticsCell", for: indexPath) as! StatisticsTableViewCell
        let item = itemFetchedResultController?.object(at: indexPath).itemName!
        //        let item = itemResultsArray[indexPath.row]
        let productRequest: NSFetchRequest<Product> = Product.fetchRequest()
        productRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
        productRequest.predicate = NSPredicate(format: "name == %@", item!)
        productRequest.fetchLimit = 1
        do {
            let fetch = try context.fetch(productRequest)
            cell.idInfoLabel.text = fetch[0].productId
            cell.nameInfoLabel.text = fetch[0].name
            cell.productImageView.image = UIImage(data: (fetch[0].productImage! as Data))
            cell.minimumStockInfoLabel.text = fetch[0].minimumStock
            let productPrice = fetch[0].price
            //fetch item for amount of single object
            let itemId = itemFetchedResultController?.object(at: indexPath).itemId!
            let itemRequest = NSFetchRequest<Item>(entityName: "Item")
            itemRequest.sortDescriptors = [NSSortDescriptor(key: "itemName", ascending: true)]
            itemRequest.predicate = NSPredicate(format: "date BEGINSWITH %@", dateToFetchMin)
            itemRequest.predicate = NSPredicate(format: "itemId == %@", itemId!)
            do {
                let itemFetch = try context.fetch(itemRequest)
                print(productPrice!)
                print(itemFetch, itemFetch.count)
                cell.soldQuantityInfoLabel.text = String(describing: itemFetch.count)
                let amount = Double(productPrice!)! * Double(itemFetch.count)
                cell.salesAmountInfoLabel.text = String(describing: amount)
            } catch  {
                print("Error in fetching sold items for cell: (error)")
            }
        } catch  {
            print("Error in fetching product for cell: (error)")
        }
        return cell
    }

推荐答案

经过几天测试不同选项并遇到各种错误后,我终于更正了代码,使其能够正常工作.在这个过程中,我发现了一些基本点(从我试图找到解决我问题的方法的帖子数量来看),很少有人受到影响.这个答案是为了帮助其他人,并澄清在获得不同结果时所涉及的强制性属性和类型定义.

After a couple of days of testing different options and getting all sort of errors, I finally corrected the code to work as I wanted. During the process I found out basic points that ( judging by the quantity of posts I had a look at trying to find a solution to my problem ), very a few people got under their belts. This answer is to help others and clarify the mandatory properties and type definitions that are involved in getting distinct results to work.

分步指南:

1st: itemFetchRequest.returnsDistinctResults = true 这将结果设置为不同

1st: itemFetchRequest.returnsDistinctResults = true this set the result to be distinct

第二:itemFetchRequest.propertiesToFetch = ["category","itemId","itemName"]这是您想要在结果中显示的属性,它们也需要在 .propertiesToGroupBy 中.

2nd: itemFetchRequest.propertiesToFetch = ["category","itemId","itemName"] this is what properties you want to show in your results and they need also to be in .propertiesToGroupBy .

第三:itemFetchRequest.propertiesToGroupBy = ["category","itemId","itemName"]这是您想要不同结果的属性.

3rd: itemFetchRequest.propertiesToGroupBy = ["category","itemId","itemName"] this is the properties you want distinct results of.

4th: itemFetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType 这是唯一允许获得不同结果的类型.

4th: itemFetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType this is the only allowed type to get distinct results.

5th: NSFetchedResultsController这是控制器的强制类型,否则你将无法访问获取的对象的参数值.

5th: NSFetchedResultsController<NSDictionary>this is the mandatory type for the controller as otherwise you won't have access to fetched object's parameter's values.

6th: let item = itemFetchedResultController?.object(at: indexPath) 这是获取获取对象的强制性方法.使用 fetchedObjects![indexPath.row] 得到错误的项目.我在两个显示的类别中都得到了相同的两个项目.

6th: let item = itemFetchedResultController?.object(at: indexPath) this is the mandatory way to get fetched objects. Using fetchedObjects![indexPath.row] gets wrong item. I was getting same two items in both displayed categories.

7th: let itemName = item!["itemName"]! let itemId = item!["itemId"]! 这是获取参数值的方法获取的对象是字典类型.

7th: let itemName = item!["itemName"]! let itemId = item!["itemId"]! this is the way to get parameter's values as the fetched objects are dictionary type.

所以这一切的最终代码是:

So the final code for all this is:

获取:

// dictionary fetch result controller
    func configureItemFetchedResultsController() {
        print("configureItemFetchedResultsController(): started")
        let itemFetchRequest = NSFetchRequest<Item>(entityName: "Item")
        itemFetchRequest.sortDescriptors = [NSSortDescriptor(key: "category", ascending: true),NSSortDescriptor(key: "itemId", ascending: true)]
        // predicates to filter for user and date range:
        let user = NSPredicate(format: "order.user.name == %@", UserDetails.fullName ?? "")
        let dateFrom = Conversions.dateConvert(dateString: dateToFetchMin)!
        let dateTo = Conversions.dateConvert(dateString: dateToFetchMax)!
        print(dateFrom)
        let from = NSPredicate(format: "date >= %@", dateFrom as CVarArg)
        let to = NSPredicate(format: "date <= %@", dateTo as CVarArg)
        itemFetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [user,from,to])
        itemFetchRequest.returnsDistinctResults = true
        itemFetchRequest.propertiesToFetch = ["category","itemId","itemName"]//["category","itemId","itemName"]
        itemFetchRequest.propertiesToGroupBy = ["category","itemId","itemName"]
        itemFetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType //.managedObjectResultType// .dictionaryResultType

        itemFetchedResultController = NSFetchedResultsController(fetchRequest: itemFetchRequest, managedObjectContext: context, sectionNameKeyPath: "category", cacheName: nil) as? NSFetchedResultsController<NSDictionary>// as! NSFetchedResultsController<Item>
        do {
            try itemFetchedResultController?.performFetch()
            print("configureItemFetchedResultsController(): sold items fetched")
        } catch  {
            //            fatalError("failed to fetch entities: (error)")
            print("configureItemFetchedResultsController(): failed to fetch Item entities: (error)")
        }
        self.statisticsTableView.reloadData()
    }

显示获取的对象:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "statisticsCell", for: indexPath) as! StatisticsTableViewCell
        let item = itemFetchedResultController?.object(at: indexPath)  //fetchedObjects![indexPath.row] gets the wrong item
        print("fetched is: (String(describing: item))")
        let itemName = item!["itemName"]!
        let itemId = item!["itemId"]!
        let productRequest: NSFetchRequest<Product> = Product.fetchRequest()
        productRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
        productRequest.predicate = NSPredicate(format: "name == %@", itemName as! CVarArg)
        productRequest.fetchLimit = 1
        do {
            let fetch = try context.fetch(productRequest)
            cell.idInfoLabel.text = fetch[0].productId
            cell.nameInfoLabel.text = fetch[0].name
            cell.productImageView.image = UIImage(data: (fetch[0].productImage! as Data))
            cell.minimumStockInfoLabel.text = fetch[0].minimumStock
            let productPrice = fetch[0].price
            //fetch itemes for amount of single object
            let itemRequest = NSFetchRequest<Item>(entityName: "Item")
            itemRequest.sortDescriptors = [NSSortDescriptor(key: "itemName", ascending: true)]
            itemRequest.predicate = NSPredicate(format: "date BEGINSWITH %@", dateToFetchMin)
            itemRequest.predicate = NSPredicate(format: "itemId == %@", itemId as! CVarArg)
            do {
                let itemFetch = try context.fetch(itemRequest)
                print(productPrice!)
                print(itemFetch, itemFetch.count)
                cell.soldQuantityInfoLabel.text = String(describing: itemFetch.count)
                let amount = Double(productPrice!)! * Double(itemFetch.count)
                cell.salesAmountInfoLabel.text = String(describing: amount)
            } catch  {
                print("Error in fetching sold items for cell: (error)")
            }
        } catch  {
            print("Error in fetching product for cell: (error)")
        }
        return cell
    }

也非常感谢你帮助我解决这个问题,这个日志和详细的答案是为了帮助其他人更好地理解所涉及的整个过程.如果我的回答有问题,请发表评论,我会对其进行编辑,以免误导他人.

Many thanks for helping me out on this one as well, and this log and detailed answer is to help others understand better the whole process involved. Please comment if there is something wrong in my answer and I'll edit it, so not to mislead others with it.

这篇关于根据对象参数 UPDATED Swift 从 fetch 中删除重复的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆