FAQ: Code Challenge: Loops - Same Values

Under the circumstances the lists would have to be the same length else we run the risk of raising an IndexError. Were we given two different length lists then we would have to use the length of the shorter list and so would not examine the latter values of the longer list.

Have you learned about zip(), yet? That’s one way to get two lists the same length because it will discard surplus values on uneven lists.

>>> a = [2, 5, 8, 11, 14, 17]
>>> b = [3, 7, 11, 15, 19]
>>> c = [*zip(a, b)]
>>> c
[(2, 3), (5, 7), (8, 11), (11, 15), (14, 19)]
>>> 

Notice how there is no 17 in the pairs since there was nothing to pair it up with.

For the purpose of this lesson, let’s continue with the assumption that the lists are the same length.

We can use the list of tuples to compare values by iterating and unpacking…

>>> print ([a for a, b in c if a == b])
[]
>>> 

If you haven’t got to the unit on iterators yet, then ignore the above. For the heck of it, let’s use the technique to solve the lesson.

>>> def same_values(m, n):
    return [a for a, b in zip(m, n) if a == b]

>>> print (same_values([5, 1, -10, 3, 3], [5, 10, -10, 3, 5]))
[5, -10, 3]
>>> 

For the time being, stick with what you know, and be sure to review it for what you might or might have learned from it.

Greetings fellow travellers.

I’ve seen the solutions offered here which are really helpful (thanks everyone), but it’s driving me mad trying to debug the stab I’d made at this one:

#Write your function here
def same_values(lst1, lst2):
  new_list = []
  if len(lst1) != len(lst2):
    return None
  else:
    for x in lst1:
      for y in lst2:
        if x == y:
          new_list.append(lst1.index(x))
        elif x != y:
          continue
    return new_list

print(same_values([5, 1, -10, 3, 3], [5, 10, -10, 3, 5]))

This prints:

[0, 0, 2, 3, 3]

I can’t make sense of this. It’s picking the right indices … but doubling them at the start/end of the loop?

I think it’s appending the last ‘True’ index for every loop iteration, but I can’t tell why exactly and therefore how to stop it (or rather avoid the same mistake again in future).

I coded a ‘break’ in toward the end as follows. But this is obviously very contingent and a tidy-up rather than a real solution.

if x is lst1[1]:
            break

I wondered if anyone could please help point out where I’ve gone wrong to get repetitions like this?

the loops are nested, so the first element of lst1 is compared to every element of lst2. Then the second element of lst1 is compared to every element of lst2 and so forth

in some cases, this leads to duplicate entries. For example the first element of first list (value 5) is two times in second list, resulting in a duplicate

So why don’t we have to iterate through lst2 as well? Why can’t I create a nested loop, iterating through the len of lst2 then compare lst1[i] == lst2[i]?

if you used nested loops, you compare each element of lst1 with every element of lst2. Which isn’t desirable.

Okay so I must’ve been confused the difference of an element and an index. An index is the location of the list and an element is what the index entails?

 ---       ---
|   element   |  0  <=  index
 ---       ---
|   element   |  1
 ---       ---
|   element   |  2
 ---       ---
|   element   |  3
 ---       ---
|   element   |  4
 ---       ---
|   element   |  5
 ---       ---

We count elements from 0 to length minus 1. Those are the indices. The content of the element is a value. Values can be any object, including lists and dictionaries, even function references.

1 Like

Gotcha, thank you both

1 Like

Thanks for the pointer on zip() I didn’t even think of being able to achieve this using that. That’s a much more elegant solution than I came up with and would handle adding additional lists easier, I believe.

I just checked for which list was shorted and used that for my range. This requires extra lines of code and would get worse as additional lists are added.

def same_values(lst1,lst2):
  if len(lst1) <= len(lst2):
    len_lst = len(lst1)
  else:
    len_lst = len(lst2)
  return [i for i in range(len_lst) if lst1[i] == lst2[i]]
1 Like

Why can’t I use “for index in lst1” instead of “for index in range(len(lst1))”?

def same_values(lst1, lst2):
—new_lst = [ ]
—for index in range(len(lst1)):
-----if lst1[index] == lst2[index]:
-----new_lst.append(index)
—return new_lst

print(same_values([5, 1, -10, 3, 3], [5, 10, -10, 3, 5]))

Because that is a lookup loop and will not give us indices. index would be an incorrect variable name since its a value, not an index.

We are asked to return the indices where values match.

1 Like

Thank you! So if its a function that will get reused you use range?

1 Like

We use range when we need an integer sequence that will correspond with the indices of our list.

1 Like

Thank you for your time! I understand better!!!

1 Like

You’re welcome! Be sure to review and practice with this concept before leaping forward. Once it is cemented in your mind, it will become second nature.

for x in iterable

x is always the value of each successive element in the iteration process.

We choose how to use x in the code block. It could be an index of another iterable, or it could be the value. We only need to track the index if we plan to modify the element at the current index. If all we want to do is read, or poll the value, then don’t involve the index.


The difference is subtle until it becomes obvious, like learning how simple a magic trick is (kind of wrecks the trick, though) but this is no trick. It’s mechanics–direct access versus indexed access.

colors = 'roygbiv'

With indexed access we will iterate one pseudo-list (range) which will not deviate while we mutate the elements of a real list at the corresponding index. We need this level of abstraction to ensure we complete an uninterrupted iteration cycle. We’re not modifying the list we’re iterating over, only using its values as the index of another iterable.

>>> colors = 'roygbiv'
>>> n = len(colors)
>>> r = range(n)
>>> for x in r:
    print(colors[x], end='')

    
roygbiv
>>> 

Two iterables versus one…

>>> for x in colors:
    print(x, end='')

    
roygbiv
>>> 

They look the same until we try to mutate. We know that strings are immutable yet there are ways to simulate mutability (a trick?); but, let’s see this in terms of a list, which is mutable.

>>> color_list = list(colors)
>>> for x in color_list:
    print(x.upper(), end='')
    x = x.upper()

    
ROYGBIV
>>> color_list
['r', 'o', 'y', 'g', 'b', 'i', 'v']
>>> n = len(color_list)
>>> r = range(n)
>>> for x in r:
    print(color_list[x].upper(), end='')
    color_list[x] = color_list[x].upper()

    
ROYGBIV
>>> color_list
['R', 'O', 'Y', 'G', 'B', 'I', 'V']
>>> 

After a few of these loop challenges I’m completely gutted. I thought I had a grasp on things and now feel just stupid and lost. I’m having a difficult time interpreting what the exercise even wants from me, let alone, figuring out which type of loop to use and then the proper syntax.

Suggest stop here, go back to the beginning, reset and do everything again. Rinse and repeat and don’t move forward until you overcome those issues of understanding and implementing loops.

I have successfully solved the challenge, but my solution seems rather long:

This is what I have:

def same_values(lst1, lst2):
  zipindex = []
  index = 0
  for i1, i2 in zip(lst1, lst2):
    if i1 == i2:
      zipindex.append(index)
      index += 1
    else:
      index += 1
  return zipindex

Is there a more efficient way to do this in fewer lines?

index is not necessary, so that saves a couple lines.

if you really want to write in very few lines, you could attempt list comprehension.

1 Like

Not sure that list comprehensions have been covered yet since they fall under iterators, of a sort. Can’t remember if they come up in the lists and dictionary unit. Do you know where they are first introduced?

1 Like