Удаление дубликатов объектов из выборки на основе параметра объекта UPDATED Swift - PullRequest
0 голосов
/ 16 мая 2019

У меня есть TableVIew, который заполняется FetchResultsController.Выбранные элементы правильно отображаются в своем собственном разделе, но я хочу показать только один раз тот же тип объекта, но сохранить количество выбранных объектов.Пример: имя объекта: Item, атрибут объекта: itemId: String, category: String.category используется для сортировки выборки и создания 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 не решило проблему.Есть идеи по этому поводу?

В версии 2 вместо этого я получаю *** 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 Ответ

1 голос
/ 19 мая 2019

После нескольких дней тестирования различных опций и получения всевозможных ошибок я, наконец, исправил код так, чтобы он работал так, как я хотел. В ходе процесса я выяснил основные моменты, которые (судя по количеству постов, на которые я смотрел, пытаясь найти решение своей проблемы), очень немногие люди оказались за их поясами. Этот ответ состоит в том, чтобы помочь другим и уточнить обязательные свойства и определения типов, которые участвуют в получении определенных результатов для работы.

Пошаговое руководство:

1-й: itemFetchRequest.returnsDistinctResults = true этот результат будет отличаться

2-й: itemFetchRequest.propertiesToFetch = ["category","itemId","itemName"] это то, что свойства вы хотите показать в ваших результатах, и они также должны быть в .propertiesToGroupBy.

3-й: itemFetchRequest.propertiesToGroupBy = ["category","itemId","itemName"] это свойства, которые вы хотите отличные результаты.

4th: itemFetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType это единственный допустимый тип для получения отличных результатов.

5th: NSFetchedResultsController<NSDictionary> это обязательный тип для контроллера, так как иначе у вас не будет доступа к значениям параметров выбранного объекта.

6th: let item = itemFetchedResultController?.object(at: indexPath) это обязательный способ получения выбранных объектов. Использование fetchedObjects![indexPath.row] возвращает неправильный элемент. Я получал одинаковые два предмета в обеих отображаемых категориях.

7th: let itemName = item!["itemName"]! let itemId = item!["itemId"]! это способ получения значений параметров, так как извлекаемые объекты имеют тип словаря.

Итак, окончательный код всего этого:

Feching:

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

Большое спасибо за помощь в этом, и этот журнал и подробный ответ помогут другим лучше понять весь вовлеченный процесс. Пожалуйста, прокомментируйте, если что-то не так в моем ответе, и я его отредактирую, чтобы не вводить в заблуждение других.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...