Welcome back. With nested data structures,
we'll often want to traverse all the contents and print or extract some of the items.
We do that by nested iteration,
with one for loop indented inside another.
Here we have a list called nested1.
We've seen it before,
and on line two, we iterate.
We go through all of the items in nested1.
We're going to call this the outer loop because it goes through the outer list.
The loop variable or iterator variable x,
will be bound to one of the items each time.
So, the first time that we execute lines three through five,
x will be bound to the list a, b, c. So,
we'll have x bound to a, b,
c, and then we execute line three through five,
and we'll change x and make it be bound to d,
e, and we'll execute lines three through five again.
As part of the loop,
I just print level1 on line three because we're going to have a lot of
stuff printing as a way of making things look nice in the output.
Then we're going to do an inner loop on lines four and five.
So, on line four we started another iteration.
We have another iterator variable or loop variable.
It's called y, and y will get bound first to a,
and then to b, and then to c. So,
the first time we execute line five,
y will be bound to a,
and it'll print out a few spaces,
level2: and then it'll print out a.
We'll go on. Y will get a new value.
It'll be b, and we'll print out level2, b.
We get all the way through a, b, c,
and then we're done with the for loop for the first value x bound to a,
b, c. Then we go on to let x have a new value.
X is going to be bound to d and e,
and we do the inner loop again.
When we do the inner loop again,
y will get bound to d and then y will get down to e. So,
let's see how the whole thing unfolds when we run it.
So, you'll notice that line three; print level1.
That gets executed three times.
The reason is that we have three items in the outer list.
So, we execute the outer loop three times.
Each time we print level1.
Then the first time we go through a, b,
and c, so line five executes three times,
once with y bound to a,
once with y bound to b,
and once with y bound to c. One thing that's important to keep track of
is which variable names that we're
using for the outer loop and which are we are using for the inner loop.
You'll notice that the variable x is
used as the iterator variable or loop variable in the outer loop,
and then we see it again on line four when we're creating the inner iteration.
That's a very common pattern because when we do this nested iteration,
we're letting x be bound to each of the inner lists,
and then in the inner loop,
we're going to want to have an iteration through each of those lists.
So, if you're ever tempted to say for x in nested1,
and then for x in y.
That's not going to work.
The reason it's not going to work is that for x is defining the iterator variable.
It's saying here's a new variable name that's going to get bound to something.
What you really wanted in this inner spot is for y in x.
You want the x to refer to a variable that's bound as part of running the outer loop.
Here we have a traversal where we're supposed to accumulate
something from the inner lists rather than just printing them.
So, we're supposed to write code to create a new list that contains
every person's last name and save that list as last names.
So, each item in the outer list is itself
a list and those lists contain last names like Turner,
and Damon, and Wiig.
This requires an accumulation pattern.
What makes me think that it's going to be an accumulation?
Well, we had created a new list,
and it contains every person's last name.
Those are kind of triggers that suggests to me that we're going to have an accumulation.
So, to solve this, let's set up our usual template for an accumulation pattern.
Since I'm accumulating a list,
I start with an empty list for something in something.
I'm going to refer to accum here,
and at the end, I will have my list.
Now instead of calling it accum,
I'm going to call it last_names because they told us we're supposed to
accumulate it into a variable called last_names.
Now what am I going to iterate over?
Well, I'm going to iterate over the whole giant list.
So, that's called info and I can name my loop variable anything I want to.
But since these are all entertainers,
I'm going to call my variable entertainer.
Now, what am I going to do with each of these entertainers?
Well, I'm going to extract just the last name.
This is a very well-structured piece of data because
every inner list has the last name in position one.
You'll notice it's the same always.
It's always position one where we have the last name.
That means we could always just do something with entertainer position one.
What are we going to do? We're going to append
that entertainer to the end of the existing list of last names.
I see that I misspelled last_names.
Just to help me see what's happening,
I'm going to print last_names each time.
So, when we execute this,
I will see as the list accumulates.
It looks like I accidentally deleted
my print statement as I was getting rid of all the markings on the screen.
Let me see if I can undo it and make it come back.
Well, I can't make it come back.
So, I'm going to type it again.
Now we can see on each iteration when we get to line six I'm
printing and the first time I only have Turner.
The next time I have Turner with Damon appended, and so on.
So, that's how I would go about answering an exercise like this.
I'd first figure out how I'm going to need an accumulation pattern.
I would make the little template for that.
Then I would start filling in parts of the template,
and I would have to come up with the insight that
the last_name is always in the same position in each of the inner lists.
Therefore, I can always refer to position square bracket one knowing that
the entertainer variable was going to be bound to
a different one of these inner lists each time I went through here.
The first time I get to line five.
It's bound to the first inner list and I get Turner.
The second time entertainers bounded that second inner lists Matt Damon 1970,
and we get Damon's last name appended to our list of last names.
Now note that we don't have an inner loop in this problem.
We have for entertainer in info but we don't have another for loop listed here.
But we do have an outer loop and we have square brackets
to extract an item from an inner list.
So, we have an outer loop here on line four,
and then we have another square brackets on line five
to extract a name from one of the inner lists.
If we're extracting things from two levels into
a nested data structure like Turner and Damon are getting extracted from info,
we will always need a combination of two iterations and square bracket index operations.
We could have info square bracket one, square bracket zero.
So, that would be using two square brackets, and if I do that,
I'm going to get rid
of the print statement on line six so that we don't get confused by it in the output.
I will just get Matt directly.
So, if I want to extract Matt,
or I can extract Damon by doing
[1] [1] or I
can have a for loop as I do on line four with a square bracket on line five.
I could have two for loops.
Then I'll print a whole lot of stuff,
but it's all the inner values, Tina,
Turner, 1939 and so on.
So, that's two four loops,
or two square brackets.
We've already seen a four loop with an inner square bracket.
We can also have a weird thing where we could say,
for val in infosquare bracket one print val.
I'm just going to
write pass here so that it does;
we don't want to do anything in that four loop.
So here, on lines 12 and 13,
I am only looking at val square bracket one,
that's the inner list, Matt, Damon,
1970, actor, and I'm printing that.
I'm printing each of those values.
So, in any case,
if I'm trying to get values like Tina, and Turner, and Matt,
and Damon out of this nested data structure,
I have to do some combination.
I either need two square brackets,
or I need two four loops,
or I need one four loop and a square bracket.
One four loop and a square bracket.
Two levels of nesting,
some combination of two four loops and square brackets.
Here's another problem where we accumulate.
In this case, we do need nested iteration because we're
not always going to extract the same thing from each inner list.
Our task is to use nested iteration to save
every string containing b into a new list named b strings.
So, this is another accumulation pattern.
We're going to traverse this nested data structure finding each of
the vegetables and fruits and accumulating all of the ones that contain the letter b.
So, this is an accumulation pattern,
b_strings is eventually going to be a list of words that all contain the letter b,
like bananas and blueberries but not lemons.
Initially, it's empty, and I'm going to iterate through the outer list.
So, for each of these lists and the outer list,
I'm going to check whether the items in it are b words.
So, I'm going to say for word in
list, b_strings.append of word.
When I finish, I'll print out b_strings.
Now, this isn't quite right because I'm going to actually
append all of the words even when they don't contain the letter b.
So, I've got apples when I shouldn't have apples.
But I do now have just a simple list,
not a nested list.
I just have all of the items that appeared anywhere at the second level in the list,
and now they're in this flat list.
So, what do I have to do instead of automatically appending every word?
I have to append it only if it has some property, if something something.
If the word has some nice property,
I will append it, and otherwise, I won't bother.
Now, what do I want to put into this IF expression?
At this point, when people start learning about nested iteration,
they sometimes forget about the nice built-in things that Python has given us.
I'm just first going to remind you and then I'll show you the hard way,
but first, I'll show you the easy way.
If we want to check on whether a word has the letter b,
we have the InOperator.
So, if b is in the word,
then we append it, and otherwise, we don't append it.
Now, we managed to pass the test,
we just got bananas,
and blueberries, and green beans, and so on.
Now there's a lot of ways that you can go wrong when you start writing this kind of code,
you might be tempted to say things like,
append list instead of append word, then what do you get?
You get a bunch of lists rather than getting single words.
You might be tempted to append the letter b instead,
then you just get a list of the letter b a whole bunch of times,
but what we really want is to append the word, if we do this.
By the way, it gets easier if you use mnemonic variable names like Word.
If I had called this y and this one x,
it'd be a lot harder to remember what exactly I ought to be printing here.
I'll be more tempted to do something like appending x for b,
and say x instead of for b and y. I should be saying b and y;
this gets a lot more complicated.
I can get the right answer,
but it's a lot harder.
It was a lot easier when I used mnemonic names for lists and L for word in list,
if b is in the word,
then accumulate that word into our final value.
Now, what's the complicated way that I sometimes see students
do things if they don't remember that we have this nice InOperator?
Well, they start to iterate a third time for character in word.
If character equals b,
then we append the word.
That almost works if you don't remember about the InOperator- almost works,
but it fails when a word has more than one b in it.
You'll see bananas came out fine,
but blueberries got in here twice because it got in there once for the first b,
and a second time when we got another b in the word blueberries.
So, there's a way to get around this if you remember
the fancy break command that ends the inner iteration, the inner four loop.
This will break us out of the innermost four loop that we're in.
So, this actually should work I think.
Yes, it does, but it's a complicated way.
The simple way is to just check
here if b is in word.
So, that's nested iteration to accumulate some of the values from the inner lists.
We'll see you next time.