# Working with Iterables

Course: [Dart](https://www.devtutorials.ai/learn/dart) · Section: Collections
Source: https://www.devtutorials.ai/learn/dart/collections/working-with-iterables

> How to transform and question a list with map, where, fold, reduce, any, every, firstWhere, take and skip, and how to chain them.

## What Is an Iterable?

**Iterable** = a collection of items you can go through one by one.

- Every **List** is an Iterable.
- So are the results of methods like `map()` and `where()`.
- You can use `for-in` on any Iterable.

This lesson is about **asking questions of a whole list**, and **changing every item**, in one short line.

**Simple lesson:** an Iterable is anything you can walk through, item by item.

### A Real-World Example

The class teacher has everyone's marks: `[45, 82, 91, 30, 67]`.

She asks:

- "Give everyone **5 grace marks**." That changes every item.
- "Who **passed**, with 50 or more?" That keeps some items.
- "What is the **class total**?" That squeezes all items into one number.
- "Did **anyone** fail?" That asks one yes-or-no question.

Each question is one Iterable method. Switch the steps on and off:

Every **Run** box on this page runs on the course's Dart runner in your browser.

Watch for `( )` and `[ ]` in the output. They tell you whether you got an Iterable or a List.

## What You Will Learn

In this lesson, you will learn:

1. What an Iterable is, and why its results are "lazy".
2. How to change every item with `map()` and `expand()`.
3. How to keep some items with `where()`, `take()` and `skip()`.
4. How to combine items with `fold()` and `reduce()`.
5. How to ask questions with `any()`, `every()` and `firstWhere()`, and chain them all.

## Iterable vs List

### The Result Prints with ( )

```dart runnable title="map gives an Iterable"
void main() {
  var marks = [45, 82, 91];
  var withGrace = marks.map((m) => m + 5);
  print(withGrace);
  print(withGrace.toList());
}
```

- `( )` = an Iterable.
- `[ ]` = a List.
- **`toList()`** = turns an Iterable into a List.

### Lazy Results

**Lazy** = the work is not done until you actually use the result.

- `map()` and `where()` only **describe** the work.
- The work happens when you `print`, loop over, or call `toList()`.
- This saves time when you only need part of the result.

Call `toList()` when you need a real list: to use an index like `[0]`, to `add` items, or to keep the result for later.

**Simple lesson:** most Iterable methods give a lazy Iterable, and `toList()` turns it into a List.

## Transformation Methods

### map()

**`map(f)`** = run function `f` on **every** item, and give back the new items.

- Same number of items out as in.
- The function decides what each new item looks like.

```dart runnable title="Change every item with map"
void main() {
  var names = ['asha', 'ravi', 'priya'];
  print(names.map((n) => n.toUpperCase()).toList());

  var marks = [45, 82, 91];
  print(marks.map((m) => m >= 50 ? 'Pass' : 'Fail').toList());
}
```

### expand()

**`expand(f)`** = each item becomes **zero or more** items, and they are all flattened into one Iterable.

```dart runnable title="Flatten groups into one list"
void main() {
  var groups = [
    ['Asha', 'Ravi'],
    ['Priya'],
    ['Kiran', 'Meena', 'Imran'],
  ];
  var everyone = groups.expand((g) => g).toList();
  print(everyone);
  print(everyone.length);
}
```

**Simple lesson:** `map` changes each item into one new item, and `expand` can turn one item into many.

## Filtering Methods

### where()

**`where(test)`** = keep only the items for which `test` is `true`.

```dart runnable title="Keep only the items that pass a test"
void main() {
  var marks = [45, 82, 91, 30, 67];
  var passed = marks.where((m) => m >= 50).toList();
  print(passed);
  print('${passed.length} students scored 50 or more.');

  var names = ['Asha', 'Ravi', 'Arjun', 'Priya'];
  print(names.where((n) => n.startsWith('A')).toList());
}
```

### take() and skip()

- **`take(n)`** = keep the **first** `n` items.
- **`skip(n)`** = leave out the first `n` items, keep the rest.

```dart runnable title="The top three, and the rest"
void main() {
  var rankList = ['Asha', 'Ravi', 'Priya', 'Kiran', 'Meena'];
  print(rankList.take(3).toList());
  print(rankList.skip(3).toList());
}
```

**Simple lesson:** `where` keeps the items that pass a test, and `take` or `skip` keep a part by position.

## Combining Methods

### fold()

**`fold(start, f)`** = start with a value, then combine it with each item, one by one, into **one** result.

- `sum` = the result so far. Its first value is the `0` you gave.
- `m` = the current item.

### reduce()

**`reduce(f)`** = like `fold`, but it starts with the **first item** instead of a value you give.

```dart runnable title="Total and highest mark"
void main() {
  var marks = [45, 82, 91, 30, 67];
  var total = marks.fold(0, (sum, m) => sum + m);
  var highest = marks.reduce((a, b) => a > b ? a : b);
  print('Total: $total');
  print('Highest: $highest');
  print('Average: ${(total / marks.length).toStringAsFixed(1)}');
}
```

**Simple lesson:** `fold` and `reduce` squeeze a whole list into one value, and `fold` is safe on an empty list.

## Question Methods

### any() and every()

- **`any(test)`** = `true` if **at least one** item passes.
- **`every(test)`** = `true` only if **all** items pass.

```dart runnable title="Did anyone fail? Did everyone attend?"
void main() {
  var marks = [45, 82, 91, 30, 67];
  print(marks.any((m) => m < 35));
  print(marks.every((m) => m >= 35));

  var attendance = [true, true, true];
  print(attendance.every((present) => present));
}
```

### firstWhere(), lastWhere() and singleWhere()

- **`firstWhere(test)`** = the **first** item that passes.
- **`lastWhere(test)`** = the **last** item that passes.
- **`singleWhere(test)`** = the **only** item that passes. It crashes if there are zero or several.
- Add **`orElse: () => value`** to give a value when nothing passes, instead of crashing.

```dart runnable title="Find items with a test"
void main() {
  var marks = [45, 82, 91, 30, 67];
  print(marks.firstWhere((m) => m > 80));
  print(marks.lastWhere((m) => m > 80));
  print(marks.singleWhere((m) => m < 40));
  print(marks.firstWhere((m) => m == 100, orElse: () => -1));
}
```

Without `orElse`, a search that finds nothing crashes:

```dart runnable expect-error title="Nothing matched, and there is no orElse"
void main() {
  var marks = [45, 82, 91];
  print(marks.firstWhere((m) => m == 100));
}
```

The error is `Bad state: No element`.

**Simple lesson:** `any` and `every` answer yes or no, and `firstWhere` finds an item, with `orElse` as a safe fallback.

## Chaining Methods

**Chaining** = calling one method on the result of another, in one expression.

Read a chain from top to bottom, like a recipe:

```dart runnable title="A chain of steps"
void main() {
  var marks = [45, 82, 91, 30, 67];
  var report = marks
      .where((m) => m >= 50)
      .map((m) => 'Scored $m')
      .toList();
  print(report);

  var bonusTotal = marks
      .map((m) => m + 5)
      .where((m) => m >= 50)
      .fold(0, (sum, m) => sum + m);
  print(bonusTotal);
}
```

1. **`where`** keeps the marks that are 50 or more.
2. **`map`** turns each one into a sentence.
3. **`toList`** makes the result a real list.

**Easy way to remember**

- **map** = change **every** item.
- **where** = **keep** some items.
- **fold** = squeeze everything into **one** value.
- **any / every** = ask **yes or no**.
- **firstWhere** = **find** one item.

**Simple lesson:** chain short steps, each doing one job, and read them from top to bottom.

## TL;DR

1. **Iterable** = anything you can walk through. Results print with `( )`.
   - **Lazy** = work happens only when used. `toList()` makes a List.
2. **Change:** `map` (one in, one out), `expand` (one in, many out).
3. **Keep:** `where` (by test), `take` and `skip` (by position).
4. **Combine:** `fold(start, f)` and `reduce(f)` give one value.
   - `reduce` crashes on an empty list.
5. **Ask:** `any`, `every`, `firstWhere`, `lastWhere`, `singleWhere` (with `orElse`).
   - **Chain** them, one step per line.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Using `[0]` on a `map` result | `map` gives a List | It gives an Iterable. Call `toList()` first | An error: no `[]` on Iterable. |
| Forgetting `toList()` before printing | The output will show `[ ]` | An Iterable prints with `( )` | Output like `(50, 87)`. |
| `reduce` on an empty list | It gives 0 | It crashes | `Bad state: No element`. Use `fold`. |
| `firstWhere` without `orElse` | It gives null when nothing matches | It crashes | `Bad state: No element`. |
| `where` to change items | `where` transforms | `where` only keeps or drops | Items stay the same. Use `map`. |
| `map` to filter | `map` can drop items | `map` keeps the same count | You get `false` or `null` items. Use `where`. |
| One huge chain on one line | Shorter is better | Put each step on its own line | Code nobody can read. |

## Check Yourself

## Exercises

## What to Remember

- Every List is an Iterable. Iterable results print with `( )`, and `toList()` makes a List.
- `map` changes every item, and `where` keeps only the items that pass a test.
- `fold` and `reduce` squeeze a list into one value. `fold` is safe on an empty list.
- `any` and `every` ask yes-or-no questions, and `firstWhere` finds one item.
- Chain short steps, one per line, and use `orElse` so a search never crashes.

Next: **Sets**. You will learn a collection that never holds the same value twice.
