i have an problem with delete item from array i try to solve it long time but i couldn't
my problem is :
i have dictionary[Int:String] which contain items, the user select these items from the table view cell for delete it after,
this dictionary have an item from type string and with key key is indexPath.row
the following my dictionary code:
var dictionaryOfItems = [Int:String]()
dictionaryOfItems.updateValue("Banana", forKey: 0)
dictionaryOfItems.updateValue("Cucumber", forKey: 3)
and i have an array which contains array of item type string
the following code is my array :
var array :[String] = ["Banana","Apple","Orange","Cucumber","lettuce","Milk","Tea"]
i need to delete items from array which dictionarOfItems contain it's
when i try to delete the items from array i get the error message index out off range how can i fix it please
the following is my full code:
var array :[String] = ["Banana","Apple","Orange","Cucumber","lettuce","Milk","Tea"]
var dictionaryOfItems = [Int:String]()
dictionaryOfItems.updateValue("Banana", forKey: 0)
dictionaryOfItems.updateValue("Cucumber", forKey: 3)
for (index,_) in dictionaryOfItems {
if array[index] == dictionaryOfItems[index] {
array.remove(at: index)
}
}
Instead of explicitly removing item from your array, you can simply filter using a predicate:
Filtering our elements whose associated array index (say idx) corresponds to a key in you dictionary dictionaryOfItems whose value equals that of the array element.
E.g.:
array = array.enumerated()
.filter { (idx, _) in dictionaryOfItems[idx] != array[idx] }
.map { $0.1 }
print(array) // ["Apple", "Orange", "lettuce", "Milk", "Tea"]
It seems like quite a roundabout way, however; to use a dictionary to track the every-changing element indices in your mutatable array (this, in particular, is the reason of your runtime error). Are you sure you want a dictionary to track indices and elements in an array, where the latter is, inherently, a collection of elements and associated indices?
On another note, if your predicate is in fact simpler (just removes items which are present as values in a dictionary; without need of matching tracked indices), you could use a much simpler filter approach:
array = array.filter { !dictionaryOfItems.values.contains($0) }
print(array) // ["Apple", "Orange", "lettuce", "Milk", "Tea"]
Try this:
var array :[String] = ["Banana","Apple","Orange","Cucumber","lettuce","Milk","Tea"]
var dictionaryOfItems = [Int:String]()
dictionaryOfItems.updateValue("Banana", forKey: 0)
dictionaryOfItems.updateValue("Cucumber", forKey: 3)
var indexes = dictionaryOfItems.keys.sorted().reversed()
for index in indexes {
if array[index] == dictionaryOfItems[index] {
array.remove(at: index)
}
}
Related
I trying to delete row from SwiftUI list which load data from Realm DB, it's implemented like here:
struct MyView: View {
private let realm = try! Realm()
var body: some View {
List {
ForEach(realm.objects(MyModelDB.self)) { model in
MyRow(model: model)
}
}.onDelete { indexSet in
try? realm.write {
indexSet.forEach { index in
realm.delete(words[index])
}
}
}
}
}
}
}
but when I perform deleting, I receive an exception:
*** Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.'
Whats wrong?
A couple of options are: delete the objects in reverse order OR delete all of the objects at once.
Here's why. Suppose you have three objects
a index 0
b index 1
c index 2
so the total count is 3 and your indexSet contains indexes 0 1 and 2.
If you were to iterate over that, there would be three loops, however, realm is live updating so if you delete the object at index 0, then only two indexes remain, so when it tries to perform the third loop, it will crash.
Deleting in reverse alleviates that issue as index 2 is deleted first, then index 1 and then index 0.
I would suggest gathering up the objects you want deleted into an array or list and then deleting them all at once without iterating
realm.delete(objects: Sequence>)
where Sequence would be an Array or List.
I have recently started messing around with SwiftUI and so far it is great. I have however ecnountered a problem that I can't seem to find an answer to.
I am trying to create this design:
As you can see here is a tableview (a list) which rows have a smaller tableview (a list) inside them. The task was pretty simple while using IB, but with swiftUI I can't seem to find a way to limit the number of rows to only the amount I need or to manually set the lists height based on the number of rows (keep in mind those rows can also be variable in height).
My code so far only consist of creating the List and adding 2 test rows:
List {
OrderItemRow()
OrderItemRow()
}
But as you can see SwiftUI automatically stretches the List to fill all the available space. The effect:
If you know the height of your rows, this can be achieved like this:
List(self.arrayOfAllItemsInOrder(currentOrder)) { item in
OrderItemRow(currentOrder: order, item:item)
}
.environment(\.defaultMinListRowHeight, rowHeight)
.frame(height: self.arrayOfAllItemsInOrder.reduce(0) { i, _ in i + rowHeight })
This will calculate the full size of the list based on rowHeight and limit the frame's height.
Note that if the size of your row goes beyond rowHeight, the entire calculation will be messed up and you will probably be seeing half (or missing entire) cells.
I would suggest a List using a ForEach to show your orders and a nested ForEach to show items in an order. The key is to manage the Data being supplied to each ForEach to the number of items desired and also to construct appropriate Row Views for OrderHeaderRow, OrderItemRow, etc.
struct OrdersView: View {
var body: some View {
List {
Section(header: Text("Current Order")) {
OrderHeaderRow(order: currentOrder)
ForEach(self.arrayOfAllItemsInOrder(currentOrder)) { item in
OrderItemRow(currentOrder: order, item:item)
}
OrderTotalRow(order: order)
ActionButtonsRow(order:order)
}
Section(header: Text("Previous Orders")) {
ForEach(self.arrayOfAllPreviousOrders) { order in
OrderHeaderRow(order: order)
ForEach(self.arrayOfAllItemsInOrder(order)) { item in
OrderItemRow(order: order, item:item)
}
OrderTotalRow(order: order)
ActionButtonsRow(order:order)
}
}
}
}
}
My code (fragment below) causes the Simulator to Hang.
What am I doing wrong?
To reproduce the problem, cut and paste into the Simulator.
class INK
{
var test = 1
}
var array = [INK!](repeating: nil, count: 1)
for idx in 0..<array.count
{
array[idx] = INK()
}
var idx2 = 0
for ink_item in array
{
idx2 += 1
print("idx2=\(idx2)")
}
This is a known bug, see SR-1635. Since an IUO is no longer a distinct type, it shouldn't really be possible to have an array of them in the first place.
This is confirmed by the fact that the following code fails to compile:
// compiler error: Implicitly unwrapped optionals are only allowed at top level.
// and as function results
var array: [Ink!] = []
(note I renamed your class name to Ink to conform to Swift naming conventions)
Depending on your situation, you may want to consider using a lazy property instead:
lazy var array : [Ink] = {
var array = [Ink]()
// populate array
return array
}()
Or making the array itself an implicitly unwrapped optional (and defer both the allocation and initialisation of the array):
var array : [Ink]!
Although note that IUOs should always be a last resort due to their inherent unsafety.
I have a custom struct data:
struct mydata
{
double distance;
string label;
}
I will generate lots of mydata in a loop. And I want to get the top minium disatnce items meanwhile theirs label must be unique.
Now I am using the max heap to solve this problem. My algorithm like this:
// get topK items with unique label
for i = 1:N
{
mydata item = generate_a_data();
if (max_heap.size() < K)
{
insert_to_max_heap(item);
}
else // max_heap is full
{
if (item.distance < max_heap(top).distance)
{
insert_to_max_heap(item);
}
}
}
The problem happened in the insert_to_max_heap(), because the constraint of unique label, I cannot just replace the top node in the max heap with new item, so I have to iterate all elements in the heap to find whether the same label exists. If it exists a node has the same label, I just update the distance of old node. pseudocode :
insert_to_max_heap(item)
{
for_each node in max_heap
{
if (node.label == item.label)
{
if (node.distance > item.distance)
{
// update min distance
node.distance = item.distance;
}
return;
}
}
// no identical label, replace the top node
max_heap.top = item;
sort_max_heap();
}
Is there more efficient way to improve my algorithm or new idea to solve th problem? Algorithm should be as fast as possible, and I don't have enough space to save all items in the loop.
I think you need to maintain a hash map which the key is label and the value is the position(or pointer) of the struct in your max heap.
When a new mydata is generated,check if a struct with the same label exists in the hash map firstly.If 'yes', determine whether to substitute it(after substituting,shift it down in the heap if necessary) or not according to the distance,otherwise determine whether to insert the new mydata to your heap or not,and don't forget to update your hash map at the same time.
I am sure this is quite simple but I cannot seem to get this right.
I have a built up a ICollection of Users. This collection could have 1 or many.
I now want to loop through this collection and assign a variable to each user that I can then use to fill in a spread sheet.
At present I have this code :
string username = null;
foreach (var user in dailyReport)
{
username = user.UserName;
}
Cell cell= worksheet.Cells["B5"];
cell.PutValue(username);
Now obviously this just puts the last user of the collection into cell B5!
How do I collect all user.UserNames so I can place them in B5, B6, B7 and so on????
To get a list of the user names:
List<string> userNames = dailyReport.Select( x=> x.UserName).ToList();
Alternatively you can just do the assignment in the loop directly:
int index = 5;
foreach (var user in dailyReport)
{
Cell cell= worksheet.Cells["B"+ index.ToString()];
cell.PutValue(user.UserName);
index++;
}
You need to put the value inside the foreach loop. The specific thing you are asking for--getting a different variable for every value in the collection--is not possible, and isn't what you want anyway.
string column = "B"; // or whatever your column is
int row = 1; // or whatever your starting value is
foreach (var user in dailyReport)
{
string username = user.UserName;
string cellAddress = String.Format("{0}{1}", column, row);
Cell cell = worksheet.Cells[cellAddress];
cell.PutValue(username);
row++; // make sure to increment the row, or every username goes in the same cell
}
int i = 5;
foreach (var user in dailyReport)
{
worksheet.Cells["B" + (i++).ToString()].PutValue(user.UserName);
}