4

I simply wanted to use forEach to set all values in a List to false, but I don't understand why it doesn't work. I created a filled list with fixed length like this:

List<bool> myList = List<bool>.filled(6, false);

Then I set one value to true:

setState(() => myList[3] = true);

Then I try to reset all values to false again, but as you can see from the print output it does not work:

setState(() {
  myList.forEach((val) => val = false);
  print(myList);
});

I/flutter (29049): [false, false, false, true, false, false]

Hasen
  • 11,710
  • 23
  • 77
  • 135

3 Answers3

5

You can check the answer why you can't update the values inside forEach here: List.forEach unable to modify element?

Dart does not have variable references, all elements are passed as a reference to an object, not to a variable. (In other words, Dart is purely "call-by-sharing" like both Java and JavaScript).

That means that the e parameter to the forEach callback is just a normal local variable, and assigning to it has no effect outside the callback. The same goes for iterators: they return the value in the iterable, but it has no reference back to the iterable after that.

You can do what you want using filled method like you used to create the list.

setState(() {
  myList = List<bool>.filled(myList.length, false);
  print(myList);
});

diegoveloper
  • 93,875
  • 20
  • 236
  • 194
  • So the best way to set all values to false is with another filled list? – Hasen Nov 11 '19 at 05:02
  • or you can use `for` loop :) – diegoveloper Nov 11 '19 at 05:03
  • 1
    Yeah but I thought `forEach` was a neater way of doing a for loop. – Hasen Nov 11 '19 at 05:04
  • `forEach` is somewhat odd then really. The documentation says "Applies the function f to each element of this collection in iteration order." But if you can't modify the elements you can't actually do what it says. It still has some uses of course though. – Hasen Nov 11 '19 at 05:11
  • 1
    @Hasen it's a method to produce side effects. – zerkms Nov 11 '19 at 06:12
  • "was a neater way of doing a for loop" --- it is, but neither lets you mutate the value by reference. – zerkms Nov 11 '19 at 06:13
  • @zerkms So in fact it's not "a neater way of doing a for loop" since a for loop _does_ allow you to mutate values. – Hasen Nov 13 '19 at 06:12
  • @Hasen I'm not sure what "neater" means for you then. It keeps the `for` statement semantics but provides a nicer syntax. Both `for` statement and list's `forEach` allow you mutate values. Just look carefully **how** you mutate them in a `for` statement and do the same in `forEach`. – zerkms Nov 13 '19 at 06:15
  • @Hasen zerkms is correct in that it might seem like a neater way of writing a for-loop when you want to go through every variable, but to explain further the iterated item of a foreach loop is NOT going to update the actual item in the iterated collection. A for loop simple gives you an index variable you can use to manually update the original item in a list. If you kept a counter variable in a for-each you could do the same. – aspirant_sensei Jan 19 '22 at 11:30
3

forEach element can not modify the actual element in the list. Assume this code:

var list = [false, true, false, false];

list.forEach((item) {
    item = false;
});

print("List: $list");

The output is still:

List: [false, true, false, false]

So what you can do is using an indexed for:

for (int i=0; i < list.length; i++) {
    list[i] = false;
}

Or map it and reassign it:

var list = [true, true, false];
list = list.map((item) {
    return false;
}).toList();

You'll get:

List: [false, false, false, false]

Mahdi-Malv
  • 16,677
  • 10
  • 70
  • 117
  • 1
    Using `map` does not update the elements. What you do here is creating a new `Iterable` and try to assign it to a `List` variable, which will fail at run-time. You could just do `list = List.filled(list.length, false);` if you want to assign a new list, but that's not the same as changing the content of the original list. – lrn Nov 11 '19 at 06:59
  • I've made the answer a little bit more complete (and fix the runtime issue). I think using `filled` is not fully usefull for all cases. You simply assign one value and apply it to a range in the list. To get better result you can simply use a `for` (Dart does not have `forEachIndexed`) or map the list and reassign it back (which ofcourse is not element modification, but a list modification). – Mahdi-Malv Nov 11 '19 at 07:19
1

As pointed out above, the forEach function does not give you write access to the list, it just provides you with the value. Changing the parameter of the forEach callback function has no effect outside of that function call, it's just changing a local variable.

To change the list, you must store to its element slots directly. Either:

for (int i = 0; i < list.length; i++) list[i] = false;

or

list.fillRange(0, list.length, false);

should do the job.

lrn
  • 64,680
  • 7
  • 105
  • 121