根据对象参数 UPDATED Swift 从 fetch 中删除重复的对象 [英] Removing duplicate objects from fetch based on object parameter UPDATED Swift
问题描述
我有一个由 FetchResultsController
填充的 TableVIew
.获取的项目在它们自己的部分中正确显示,但我想要实现的是只显示一次相同类型的对象,但存储已获取的对象数量.示例:实体名称:Item
,实体属性:itemId:String
,category:String
.category
用于对 fetch 进行排序并创建 Tableview
部分.因此,如果我为同一个 itemId
获取的对象有三个单元格,我只想显示一个单元格并计算它们应该显示的数量,并将其显示在唯一显示的单元格的标签中.我正在尝试使用 itemFetchRequest.propertiesToFetch = ["itemId"]
和 itemFetchRequest.returnsDistinctResults = true
这应该消除基于 itemId
的所有重复项Item
实体的属性,但我仍然得到多个具有相同项目的单元格.你能发现为什么 itemFetchController
返回同一个项目的倍数吗?
I have a TableVIew
that 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 Tableview
sections. 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 = true
that 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 Dictionary
route 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 itemFetchResultsController
to 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 type
on the line let item = itemFetchedResultController!.fetchedObjects![indexPath.row]
.
Casting it as NSArray
didnt 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
}
cellForRowAt
version2 :
cellForRowAt
version2 :
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屋!