How do I code a specific array element in section (GroupBy)?
in editingStyle, lists.remove(at: indexPath.row) moved the wrong element.
Help..
How do I code a specific array element in section (GroupBy)?
in editingStyle, lists.remove(at: indexPath.row) moved the wrong element.
Help..
what is the difference between following two ones code?
and
You have to understand what are your data structures.
In
sections[indexPath.section].trades.remove(at: indexPath.row)
self.trades untouched.In
self.trades.removeAll(where : { $0.id == sections[indexPath.section].trades[indexPath.row].id }) // .id at the end
$0.id is the id of a Trade item in self.trades ; sections[indexPath.section].trades[indexPath.row].id is the id of an item in the section from which you remove item.
Note: things could become more clear if you changed the name of property:
struct sectionBySymbol {
var symbol: String
var sectionTrades: [Trade] // Renamed
}
Then
self.trades.removeAll(where : { $0.id == sections[indexPath.section].trades[indexPath.row].id })
becomes
self.trades.removeAll(where : { $0.id == sections[indexPath.section].sectionTrades[indexPath.row].id })
Hope that helps.
Thank you very much. I will spent few hours trying to understand it...
Thank you Claude31! Highly Apprecieated. How do I close the thread?
Q: on moveRowAt, I can move element within section:
let movedTrade = sections[fromIndexPath.section].sectionTrades.remove(at: fromIndexPath.row)
sections[to.section].sectionTrades.insert(movedTrade, at: to.row)
How do I move in self.trades?
Have you several sections in the table ?
If so, your data source should manage the 2 dimensions: section and row in section.
So, lists cannot be the dataSOurce. It should be more a 2D array:
var lists : [[someType]] // To be populated to feed rows in each section
And then, you should call:
lists[indexPath.section].remove(at: indexPath.row)
THANKS! : how do I delete row after removed from array: tableView.deleteRows(at: [indexPath], with: .fade)?
...
Claude31: I am still confused. Since my code is short, I will post is below for you to help pointing out the mistakes.. Code crashes when remove element from array and delete from table.. Thank You!
//data model
import Foundation
//Trade data model
struct Trade {
var id: Int
var date: Date
var symbol: String
var qty: Double
var price: Double
}
//Group Trade by symbol for tableView Section
struct sectionBySymbol {
var symbol: String
var trades: [Trade]
}
//code
import UIKit
class SectionTableViewController: UITableViewController {
//for Sections
var sections = [sectionBySymbol]()
var trades: [Trade] = [
Trade(id: 1, date: stringToDate("04/18/2022 10:05"), symbol: "TSLA", qty: 10, price: 989.20),
Trade(id: 2, date: stringToDate("04/19/2022 11:30"), symbol: "TSLA", qty: 20, price: 970),
Trade(id: 3, date: stringToDate("04/20/2022 08:13"), symbol: "NVDA", qty: 50, price: 220.25),
Trade(id: 4, date: stringToDate("04/20/2022 09:23"), symbol: "AFRM", qty: 25, price: 40.25),
Trade(id: 5, date: stringToDate("04/19/2022 07:20"), symbol: "AFRM", qty: 30, price: 32.95),
Trade(id: 6, date: stringToDate("04/17/2022 11:21"), symbol: "IBM", qty: 25, price: 110.25),
Trade(id: 7, date: stringToDate("04/20/2022 09:23"), symbol: "IBM", qty: 25, price: 112.00),
Trade(id: 8, date: stringToDate("04/19/2022 07:20"), symbol: "NFLX", qty: 40, price: 222.15),
Trade(id: 9, date: stringToDate("04/17/2022 11:21"), symbol: "NVDA", qty: 100, price: 199.25),
Trade(id: 10, date: stringToDate("04/20/2022 09:23"), symbol: "TSLA", qty: 25, price: 1110.25),
Trade(id: 11, date: stringToDate("04/19/2022 07:20"), symbol: "AFRM", qty: 30, price: 34.00),
Trade(id: 12, date: stringToDate("04/17/2022 11:21"), symbol: "IBM", qty: 25, price: 99.80)
]
override func viewDidLoad() {
super.viewDidLoad()
//set title
navigationItem.title = "Cost Average"
navigationItem.leftBarButtonItem = editButtonItem
let groupBySymbol = Dictionary(grouping: trades, by: \.symbol)
self.sections = groupBySymbol.map {(Key, Value) in
return sectionBySymbol(symbol: Key, trades: Value)
}
}
// MARK: - Table view data source
//numberOfSections
override func numberOfSections(in tableView: UITableView) -> Int {
// return number of sections
return self.sections.count //number of section
}
//numberOfRowsInSection
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].trades.count
}
//cellForRowAt
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//decimal number with comma
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
var content = cell.defaultContentConfiguration()
let section = self.sections[indexPath.section]
let trade = section.trades[indexPath.row]
content.text = "\(trade.symbol) \(String(format: "%.0f", trade.qty)) x $\(String(format: "%.2f", trade.price)) = \(formatter.string(from: NSNumber(value: trade.qty * trade.price)) ?? "0.00")"
content.secondaryText = "\(dateToString(trade.date))"
cell.contentConfiguration = content
return cell
}
// viewForHeaderInSection to create Section Header
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let titleLabel = UILabel()
titleLabel.text = "Test"
titleLabel.backgroundColor = UIColor.gray
return (titleLabel)
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
trades.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
let movedTrade = trades.remove(at: fromIndexPath.row)
trades.insert(movedTrade, at: to.row)
}
}
Here is the error:
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
trades.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
You remove item for the full trades array. But not from the trades in
struct sectionBySymbol {
var symbol: String
var trades: [Trade]
}
Which causes the number of rows in section not to be correct anymore.
Just change as follows:
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
sections[indexPath.section].trades.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
If you wanted to remove from self.trades, you should: find what is the position in trades of the section/row you remove (it is NOT indexPath.row). Then remove it from self.trades. After that, recompute self.sections dictionary with self.sections = groupBySymbol.map {(Key, Value) in return sectionBySymbol(symbol: Key, trades: Value) }. So it is much simpler to update sections[indexPath.section].trades directly.
Thank you! All working perfect now! Appreciate it.
Thanks for the feedback. You can close the thread now on the correct answer.
You'd better close this thread about removing, and create a new one (referencing this one if needed).
However, answer is about the same.
You need to add the element in the dataSource.
But here, you need to find in which section to add, eventually creating a new section.
It may thus be simpler to:
let groupBySymbol = Dictionary(grouping: trades, by: \.symbol)
self.sections = groupBySymbol.map {(Key, Value) in
return sectionBySymbol(symbol: Key, trades: Value)
}
Got it. All working. Appreciated it!
I'm running into an interesting problem: After Add working by calling grouping.., but tableView will bring all deleted element back (as well as newly added), I can delete again, but every time I add a new item, all deleted are back, and moved items are back to original position as well. I can't figure it our....
Thanks for feedback. It is time now to close the thread.
I'm running into an interesting problem: After Add working by calling grouping.., but tableView will bring all deleted element back
That's logical with the code:
if editingStyle == .delete {
sections[indexPath.section].trades.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
But you do not update self.trades. So when you add an item, you add it to the original self.trades (no item removed) ; when rebuild sections, your rebuild with all initial items plus the new ones…
So, you should remove from self.trades as well:
if editingStyle == .delete {
self.trades.removeAll(where : { $0.id == sections[indexPath.section].trades[indexPath.row] }) // do it before next line
sections[indexPath.section].trades.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
You could also unify your code by updating self.trades and rebuild sections, but the previous one avoids rebuilding everything:
if editingStyle == .delete {
self.trades.removeAll(where : { $0.id == sections[indexPath.section].trades[indexPath.row] }) // do it before next line
let groupBySymbol = Dictionary(grouping: trades, by: \.symbol)
self.sections = groupBySymbol.map {(Key, Value) in
return sectionBySymbol(symbol: Key, trades: Value)
}
tableView.deleteRows(at: [indexPath], with: .automatic)
}
Note: Dictionary is not ordered. Order of cells may change when sections are rebuilt. Otherwise, you will have to manage to get sections in right order (with a sort) and have items in each section in the correct order.
what is the difference between following two ones code? -sections[indexPath.section].trades.remove(at: indexPath.row) and -self.trades.removeAll(where : { $0.id == sections[indexPath.section].trades[indexPath.row] })?
To get a constant sections order, you could create rebuildSections func to sort in ascending order of symbol:
func rebuildSections() {
let groupBySymbol = Dictionary(grouping: trades, by: \.symbol)
sections = groupBySymbol.map {(Key, Value) in
return sectionBySymbol(symbol: Key, trades: Value)
}
sections = sections.sorted(by: { $0.symbol < $1.symbol })
}
what is the difference between following two ones code?
and
You have to understand what are your data structures.
In
sections[indexPath.section].trades.remove(at: indexPath.row)
self.trades untouched.In
self.trades.removeAll(where : { $0.id == sections[indexPath.section].trades[indexPath.row].id }) // .id at the end
$0.id is the id of a Trade item in self.trades ; sections[indexPath.section].trades[indexPath.row].id is the id of an item in the section from which you remove item.
Note: things could become more clear if you changed the name of property:
struct sectionBySymbol {
var symbol: String
var sectionTrades: [Trade] // Renamed
}
Then
self.trades.removeAll(where : { $0.id == sections[indexPath.section].trades[indexPath.row].id })
becomes
self.trades.removeAll(where : { $0.id == sections[indexPath.section].sectionTrades[indexPath.row].id })
Hope that helps.
Thank you very much. I will spent few hours trying to understand it...
Thank you Claude31! Highly Apprecieated. How do I close the thread?
Q: on moveRowAt, I can move element within section:
let movedTrade = sections[fromIndexPath.section].sectionTrades.remove(at: fromIndexPath.row)
sections[to.section].sectionTrades.insert(movedTrade, at: to.row)
How do I move in self.trades?
To close the thread, just mark the correct answer by tapping on the checkmark in the lower circle.
It will turn green:
on moveRowAt, I can move element within section:
let movedTrade = sections[fromIndexPath.section].sectionTrades.remove(at: fromIndexPath.row) sections[to.section].sectionTrades.insert(movedTrade, at: to.row)
How do I move in self.trades?
So you move in the same section (changing section would not make sense).
For instance, you had.
Trade(id: 4, date: stringToDate("04/20/2022 09:23"), symbol: "AFRM", qty: 25, price: 40.25),
Trade(id: 5, date: stringToDate("04/19/2022 07:20"), symbol: "AFRM", qty: 30, price: 32.95),
Trade(id: 11, date: stringToDate("04/19/2022 07:20"), symbol: "AFRM", qty: 30, price: 34.00),
Imagine you want to move third in first position.
Then, ids: [4, 5, 11], should now be in other order [11, 4, 5]. You need to keep those id, to avoid smashing ids in other sections.
Which means,
Trade(id: 11, date: stringToDate("04/19/2022 07:20"), symbol: "AFRM", qty: 30, price: 34.00),
becomes
Trade(id: 4, date: stringToDate("04/19/2022 07:20"), symbol: "AFRM", qty: 30, price: 34.00),
and
Trade(id: 4, date: stringToDate("04/20/2022 09:23"), symbol: "AFRM", qty: 25, price: 40.25),
becomes
Trade(id: 5, date: stringToDate("04/20/2022 09:23"), symbol: "AFRM", qty: 25, price: 40.25),
and
Trade(id: 5, date: stringToDate("04/19/2022 07:20"), symbol: "AFRM", qty: 30, price: 32.95),
becomes
Trade(id: 11, date: stringToDate("04/19/2022 07:20"), symbol: "AFRM", qty: 30, price: 32.95),
To code this: var oldIdInPositions : [Int] = [] var newIdInPositions : [Int] = [] // Get the old values if fromIndexPath.section == to.section { return } // Should do nothing, cannot occur
oldIdInPositions = sections[to.section].sectionTrades.map { $0.id } newIdInPositions = oldIdInPositions // at start
newPos.move(fromOffsets: [fromIndexPath.row], toOffset: to.row) // We just move in newPos
Now we can change in self.trades. Do it using index in trades, not a copy of trade item. Needed to update trades
for index in 0..<self.trades.count where self.trades[index].symbol == sections[to.section].sectionTrades[0].symbol { // all sectionTrades[x] have the same symbol in a section, just choose the first
// What is the position of self.trades[index] in section ? We search its id in oldIdInPositions
if let pos = oldIdInPositions.firstIndex(of: self.trades[index].id) { // We found it at pos
// Then, update the id, with the new value
self.trades[index].id == newIdInPositions[pos]
}
}
Now, rebuild sections
rebuildSections()
I did not test, hope that works.
mistaken post, please ignore.
But you can do it !
-
—
Claude31
Add a CommentCould you show the code you have so far and explain exactly what does not work as expected ?