Flutter Remove Multiple List Items In For Loop (Multiple Returns In A For Loop) - flutter

I have a list of food items. That has a category, subCategory1, subCategory2 ect.
When the user unselects an category item. They are also unselecting the subCategory1, subCatategory2 items etc as they are children of the original categogory item like this:
So I have a for loop that is running through looking for children category list elements and removing them like this:
// Remove a category item and all of its children
if (categoryType == "category") {
List<String> subCategory1Children = List<String>.from(
snapshot.data.documents[gridIndex]['subCategory1Children']);
// Remove the subcategory items
for (int i = 0; i < foodDrinkMenuElements['subCategory1'].length; i++) {
String subID = foodDrinkMenuElements['subCategory1'][i];
int removalIndex = _indexOfListGridElement('subCategory1', subID);
if (subCategory1Children.contains(subID)) {
_removeCategoryGridItem(removalIndex, subID, 'subCategory1');
}
}
//Remove the actual item being pressed
_removeCategoryGridItem(listIndex + 1, id, categoryType);
}
Which calls _removeCategoryGridItem() like this:
void _removeCategoryGridItem(int removalIndex, String id, String categoryType) {
_FoodandDrinkKey.currentState.removeItem(removalIndex,
(BuildContext context, Animation<double> animation) {
return _buildListItem(removalIndex, animation);
});
foodDrinkMenuElements[categoryType].remove(id);
}
The For loop is always finishing after 1 list item is removed. I assume this is because of the return statement in the function _removeCategoryGridItem.
I have seen other answers that say to put it in an list and run through the list but I don't see how that applies here.
Thanks for your help

The For loop is always finishing after 1 list item is removed. I assume this is because of the return statement in the function _removeCategoryGridItem.
No, generally the only way that a function could break out of a loop in a calling function is by throwing an exception.
I don't know what type foodDrinkMenuElements[categoryType] returns, but presuming that it's a List, Map, or Set, you cannot remove items from the collection while you iterating over the collection.
From the List documentation:
It is generally not allowed to modify the list's length (adding or removing elements) while an operation on the list is being performed.... Changing the list's length while it is being iterated ... will break the iteration.
There is similar language for Map and Set.
I have seen other answers that say to put it in an list and run through the list but I don't see how that applies here.
That is exactly what you should do: you should queue which items to remove and then process the queue to avoid mutating the same collection you're iterating over:
final pendingRemoves = List<void Function()>[];
// Remove the subcategory items
for (int i = 0; i < foodDrinkMenuElements['subCategory1'].length; i++) {
String subID = foodDrinkMenuElements['subCategory1'][i];
int removalIndex = _indexOfListGridElement('subCategory1', subID);
if (subCategory1Children.contains(subID)) {
pendingRemoves.add(() =>
_removeCategoryGridItem(removalIndex, subID, 'subCategory1'));
}
}
// Since an index is involved, you also need to remove in reverse order so that
// the queued indices still refer to the same elements.
for (pendingRemove in pendingRemoves.reversed) {
pendingRemove();
}
//Remove the actual item being pressed
_removeCategoryGridItem(listIndex + 1, id, categoryType);

Related

Flutter/Dart Compare multiple Lists and generate list with elements containd in all given lists

So here is my problem:
let's say we have two or more lists
List<SomeModel> one = [SomeModel, SomeModel, SomeModel],
List<SomeModel> two = [SomeModel, SomeModel, SomeModel, SomeModel, SomeModel, SomeModel]
what I want to do is compare these two/or more lists and make a new Listcontaining only those elements that exist in every list.
List<Fruits> one = [Banana, Apple, Cherry];
List<Fruits> two = [Cherry, Blueberry, Apple, Mango, Pineapple, Pear];
List<Fruits> three = [Cherry, Apple, Mango, Pineapple, Pear, Banana];
List<Fruits> getMatchingList(){
do something...
List<Fruits> matchingFruits = [Cherry, Apple];
return matchingFruits
}
I literally have no idea how to do this, especially with larger lists or with lists of unknown content.
If the number of lists is fixed you can just use the where function to filter items:
List<SomeModel> filteredList = one.where(item => two.contains(item))
.where(item => three.contains(item)).toList();
So your method will be:
List<Fruits> getMatchingList(List<Fruits> one, List<Fruits> two, List<Fruits> three){
return one.where(item => two.contains(item))
.where(item => three.contains(item))
.toList();
}
If you need an increasing number of lists to be processed, on the other side, just use:
List<Fruits> getMatchingList(List<List<Fruits>> listsToAnalyze){
Map<Fruits,integer> fruitCounter = new Map();
//expand is a method that flattern a list of lists in a single list
listsToAnalyze.expand(element => element).toList().forEach(element => {
//if the element exists, its counter is increased, otherwise is set to 1
fruitCounter.update(element, (count) => count+1, ()=> 1);
}
//remove results with counter < size of the list of lists
//supposing that an item can appear only once in each list, this means
//that the value appeared in all lists
fruitCounter.removeWhere((key,value)=> value<listsToAnalyze.lenght);
return fruitCounter.keys.toList();
}
In case an item can appear multiple times in a single list, do not expand the list and keep a List<Fruit> to control if an item was already inserted during each cycle on a list. Remember to empty the map at each new list.
List<Fruits> getMatchingList(List<List<Fruits>> listsToAnalyze){
Map<Fruits,integer> fruitCounter = new Map();
List<Fruits> fruitsInsertedThisRound= new List();
//cycle on each list alone
listsToAnalize.forEach(list => {
fruitsInsertedThisRound.clear(); //ensure that the list is empty at each cycle
list.forEach(el => {
//if the element exists, its counter is increased, otherwise is set to 1
if(!fruitsInsertedThisRound.contains(el){
//fruit still not inserted this round
fruitCounter.update(el, (count) => count+1, ()=> 1);
fruitsInsertedThisRound.add(el); //add the element to the list of inserted fruits
}
}
}
//remove results with counter < size of the list of lists
//this means that the value appeared in all lists
fruitCounter.removeWhere((key,value)=> value<listsToAnalyze.lenght);
return fruitCounter.keys.toList();
}
Merge both the list by removing duplicate
As Single Line
a.addAll(b.takeWhile((item)=> !a.contains(item)));
by looping
for(var item in a){
if(!b.contains(item)){
b.add(item);
}
}

Flutter - different between the real value and the displayed value

I am facing very strange problem.
I want to implement the option to remove rows from DataTable, and therefore I implemented the following method:
onRemoveRow() {
setState(
() {
lastRowIndex -= selectedGeneLists.length;
geneLists.removeWhere((element) => selectedGeneLists.contains(element));
for (int i = 0; i < geneLists.length; i++) {
GenesListObjIndexed genesListObjIndexed = geneLists[i];
genesListObjIndexed.index = i;
}
selectedGeneLists = [];
},
);
}
This function should modify the list that store the table's data, and the expectation is that when I delete the items from the list the items will be deleted from the table.
But you can see here the following problem (the selected line isn't been removed):
The strange this is when I debug and check the value of the list it's look great and as expected, so what can be the problem?
Use the key property to uniquely identify each row and delete the row with the key value.

How to get value inside list without index?

I am creating an app using flutter and dart.
I have a list of objects with the name parameter, and I want to check if the user input is equal to any of the names of objects inside list.
Simply
I want to take input from a user and switch if one of the objects inside a list has this value to add it in a list
I have searched a lot but with nothing.
void main() {
Data data = Data();
String name = 'Messi';
//I want to switch if name equals any name inside players list without index
}
class Data {
List<Player> players = [
Player(
name: 'Messi',
),
Player(
name: 'Mohamed',
),
];
}
If you want to get a filtered list from the list you have, you can do this:
players.where((player) => player.name == userInput).toList();
If you just want the first occurrence, you can do this:
players.firstWhere((player) => player.name == userInput);
You can use the method forEach from the lists.
It basically works like this:
players.forEach ((player) {
if(player.name == name){
your code...
}
});
Hope this helps!

How to wait for item to be removed from list

I am trying to delete and Item from the list, it takes long time in application to delete this element, so I want to wait until this element is deleted and then verify that this item is not present on the page.
this.GroupList = element.all(by.repeater("Group in GroupList | orderBy: order"));
Now I delete an element from the above list based on its Name getAttribute('aria-label'), How can I use ExpectedConditions.stalenessOf OR ExpectedConditions.invisibilityOf to wait till one element from the list is removed from DOM.
If you have a reference to this specific repeater item, you can use stalenessOf Expected Condition, for instance:
// page object
this.GroupList = element.all(by.repeater("Group in GroupList"));
// test
var itemToBeDeleted = pageObject.GroupList.get(5);
// delete an item here
browser.wait(EC.stalenessOf(itemToBeDeleted), 5000);
Note that you don't need the "orderBy" part in the repeater locator.
Another idea would be to wait for the count to decrease by one with a custom expected condition:
function waitForCount(elementArrayFinder, count) {
return function () {
return elementArrayFinder.count(function (actualCount) {
return actualCount === count;
});
};
}
Usage:
pageObject.GroupList.count().then(function (countBefore) {
// delete item here
browser.wait(waitForCount(pageObject.GroupList, countBefore - 1));
});

Filter getElementsByTagName list by option values

I'm using getElementsByTagName to return all the select lists on a page - is it possible to then filter these based upon an option value, ie of the first or second item in the list?
The reason is that for reasons I won't go into here there are a block of select lists with number values (1,2,3,4,5 etc) and others which have text values (Blue and Black, Red and Black etc) and I only want the scripting I have to run on the ones with numerical values. I can't add a class to them which would more easily let me do this however I can be certain that the first option value in the list will be "1".
Therefore is there a way to filter the returned list of selects on the page by only those whose first option value is "1"?
I am pretty sure that there is a better solution, but for the moment you can try something like:
var allSelect = document.getElementsByTagName("select");
var result = filterBy(allSelect, 0/*0 == The first option*/, "1"/* 1 == the value of the first option*/);
function filterBy(allSelect, index, theValue) {
var result = [];
for (var i = 0; i < allSelect.length; i++) {
if(allSelect[i].options[index].value == theValue ) {
result.push(allSelect[i]);
}
}
return result;
}
I managed to get this working by wrapping a simple IF statement around the action to be performed (in this case, disabling options) as follows:
inputs = document.getElementsByTagName('select');
for (i = 0; i < inputs.length; i++) {
if (inputs[i].options[1].text == 1) {
// perform action required
}
}
No doubt there is a slicker or more economic way to do this but the main thing is it works for me.