根据对象参数UPDATED Swift从提取中删除重复的对象

时间:2019-05-16 12:03:46

标签: ios swift uitableview

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

这是我到目前为止提出的代码 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结果:

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中,在行Thread 1: Fatal error: NSArray element failed to match the Swift Array Element type上有let item = itemFetchedResultController!.fetchedObjects![indexPath.row]。 将其强制转换为NSArray didnt即可解决。有什么想法吗?

在version2中,我得到*** 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
    }

cellForRowAt版本2:

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
    }

1 个答案:

答案 0 :(得分:1)

经过几天的测试,测试了各种选项并得到了各种各样的错误,我终于更正了代码,使其可以按需工作。在此过程中,我发现了一些基本要点(从我尝试查找问题的解决方案的职位数量来看),很多人都处于控制之下。这个答案是为了帮助其他人并阐明使不同结果起作用所必需的强制性属性和类型定义。

分步指南:

第一:itemFetchRequest.returnsDistinctResults = true将结果设置为不同

2nd:itemFetchRequest.propertiesToFetch = ["category","itemId","itemName"] 这就是您要在结果中显示的属性,它们也必须位于.propertiesToGroupBy中。

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

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

第五个:NSFetchedResultsController<NSDictionary>这是控制器的强制类型,否则您将无法访问获取的对象的参数值。

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

第7个:let itemName = item!["itemName"]! let itemId = item!["itemId"]!这是获取参数值的方法,因为获取的对象是字典类型。

所有这些的最终代码是:

正在获取:

// 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
    }

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