Carly's Clippers Project Question

I understood each task in this project until I got to task #12. Here is a screenshot of the task and the hint associated with this task:

And here is the code I wrote for this task (which is correct, I believe - I watched the Project Walkthrough to see the answer but still didn’t completely understand the explanation):

cuts_under_30 = [hairstyles[i] for i in range(len(new_prices)) if new_prices[i] < 30]

I think what I don’t really understand is the range part of this code. I’m a little confused about the i variable and the indexes as well. If someone could explain this task to me I would really appreciate it!

1 Like

The range is the range of the length of the list new_prices. For example:

new_prices = [1, 2, 3] #The range of this is 0-2 (using python indexing). The length is still 3.

Therefore, the for…of loops and the if block are both iterating through the individual elements of the list, until the index of the list reaches the end of the list. For example:

list1 = [1, 2, 3]
for i in range(len(list1)):
print i #this will print i, which is each individual element in the list 'list1', until the loop gets to the last index of the list.

Then in the if block, the code checks to see if the element at the index i of the new_prices list is less than thirty.
If my explanation does not make sense, please ask, and I will try to clarify it as best as I can!

2 Likes

Basically, it checks to see if the element of the list new_prices, at the index ‘i’ is less than thirty, and then in a way, adds 1 to i, and repeats that process. It does this until (the for…of loop) the value at the index of i is at the end of the range of the list.
Please ask me to clarify if this does not make much sense!

1 Like

I get the impression that what you’re actually struggling to understand is the list comprehension, so let’s review that. :slight_smile:

The list comprehension is:
[hairstyles[i] for i in range(len(new_prices)) if new_prices[i] < 30]

If we consider a simpler list comprehension, such as:
[x for x in range(10)]
what we get is:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

The list comprehension adds an item, x, if it meets the condition for x in range(10). Hence, we get all the integers from 0 to 9 inclusive.

So, our original comprehension of [hairstyles[i] for i in range(len(new_prices)) if new_prices[i] < 30] works in a similar way.

It will add every item, hairstyles[i], to the list which matches the condition. In this case, our condition is:

for i in range(len(new_prices)) if new_prices[i] < 30

Let’s assume there are 10 haircuts, and 10 prices in the new_prices list. This condition will check each of them in turn (the for i in range(len(new_prices)) bit) to see whether they’re under 30 bucks (the if new_prices[i] < 30 bit).

If this condition is met, then hairstyles[i] goes into the list. If not, it doesn’t.

Hopefully that’s made it a bit clearer, but do let us know if you’re still stumped. :slight_smile:

2 Likes

@tera5288723178 this does make sense, thank you very much! It was just a little tough for me to wrap my head around at first.

@thepitycoder thank you so much for your thorough response! This was extremely helpful. Thank you for breaking down each part of the code and explaining it to me with examples. I definitely understand it a lot better now that I’ve seen it broken down. Thank you again!

list comprehensions do a little more than just one loop/condition:

[ RESULT
  # any number of for/if:
  for this in these
  if sky is purple
  for pickle in jar
  for color in [black]
  if whatever
  if i.feel.like.it
]

You can think of it much like completely normal code where you have the ability to take values out of iterables, and also the ability to skip combinations.

It commonly gets used to implement map/filter and those are single loops, that’s what you’ll usually see, but that’s not a limitation of list comprehension.

One limitation though, the first thing has to be for, so this is not allowed:

[ RESULT if something ]
even though that actually makes perfectly good sense, that would produce a list of either zero or one results depending on the condition.

So if you for example have a list of lists representing a grid (a chessboard, perhaps), then you could use list comprehension to iterate through it. First you’d have to iterate through rows, and a row is several cells so that needs iterating through as well:

grid = ['abcd',
        'efgh',
        'ijkl']

[ (x, y, value)
  for (y, row) in enumerate(grid)
  for (x, value) in enumerate(row)
]

# (0, 0, 'a'), (1, 0, 'b'), ...
2 Likes

Why are other hairstyles with prices less than 30 not showing when printed

cuts_under_30 = [hairstyles[i] for i in range(len(new_prices)) if new_prices[i] < 30]

I can’t find an error in the comprehension you have provided. Is there an error code when you attempt to print this list? This may need a little more information to solve.

1 Like

Also, @kcde, are you actually printing the list somewhere else in the code?

(It’s hard to debug an entire program from just one line of it. :slight_smile:)

I get ‘TypeError: object of type ‘generator’ has no len()’ error every time i try to initiate the code
cuts_under_30 = [hairstyles[i] for i in range(len(new_prices)) if new_prices[i] < 30]

Even though it’s similar to the one from the walkthrough. I don’t quite get it(

Found my own mistake - i didn’t covert new_prices into list and there was no len() in this generator.
Once i did new_prices2 = list(new_prices) and changed my initial code to cuts_under_30 = [hairstyles[i] for i in range(len(list(new_prices2))) if new_prices2[i] < 30] it worked perfectly fine!

Edit: You caught your error before I replied :ok_hand:. You can skip creating the generator in the first place if you make sure to use square parantheses.

It’s really hard to tell for certain without more of your code but I believe you have used curly instead of square parantheses when creating new_prices. Does your code use something like the following line-

new_prices = (price-5 for price in prices)

Instead of something like this?

new_prices = [price-5 for price in prices]

Using curly braces will provide you with a generator object which is not something you need to worry about at this point in the lessons. No doubt they’ll crop up again as they are very useful but stick with list comprehensions for now.

1 Like

:smile:

1 Like

Hi, thanks for that. :slight_smile:

If I run your code, I get the following:

>>> print(cuts_under_30)
['bouffant', 'pixie', 'crew', 'bowl']

This is the correct output, as you only have 4 values in your new_prices list which are below 30. We can show this by doing the following:

>>> for a,b in zip(hairstyles,new_prices):
	print("%s costs $%d" % (a,b))

	
bouffant costs $25
pixie costs $20
dreadlocks costs $35
crew costs $15
bowl costs $15
bob costs $30
mohawk costs $45
flattop costs $30

As you can see, the hairstyles bob and flattop cost exactly 30. However, your list comprehension is:

cuts_under_30 = [hairstyles[i] for i in range(len(new_prices)-1) if new_prices[i] < 30]

The cost of the haircut must be less than 30, which is only true for 4 of your 8 haircuts. :slight_smile:

If the condition were less than or equal (<=), then your output would be different:

>>> cuts_under_30 = [hairstyles[i] for i in range(len(new_prices)-1) if new_prices[i] <= 30]
>>> print(cuts_under_30)

['bouffant', 'pixie', 'crew', 'bowl', 'bob']

Notice that bob has appeared in the list, but flattop hasn’t even though their prices are the same. Can you figure out why? :slight_smile:

2 Likes

:upside_down_face: Ahh. cause it’s

thanks.
I actually thought more prices were less than 30. But looking at it now prices < 30 aren’t much compared to prices <= 30.
Thanks once again

1 Like

Hello. I have a quick question regarding this exercise. The output of list comprehension does not match a hairstyle to its cost. Is it possible to use a dictionary to display hairstyles as keys and costs as values?

1 Like

Hi there.

Yes, the exercise matches the two up by relying on the list index only - so the item at position 1 in the hairstyles list relates to the price at position 1 of the cost list…

It should be quite simple to use these two lists to create the dictionary that you’re thinking about. :slight_smile:

You should attempt it yourself first, as a learning exercise... but here's how I'd do it.

You did attempt this yourself first, right…

If you didn't... you REALLY should...

Hmm… fine, if you insist… here’s the code - but I really do hope you tried it yourself first! You don’t learn by just knocking off other people’s code!

Here's how I did it.

My initial thought was to use a comprehension, like so:

services = {hairstyles[i]: prices[i] for i in range(len(hairstyles))}

Here we create a dictionary where each key is an item from hairstyles, and each value is an item from prices. We use the comprehension’s iteration capability to have it generate the appropriate index, i, from 0 to the appropriate stopping point (len(hairstyles)).

You could equally do it with a for loop:

services = {}

for i in range(len(hairstyles)):
  services[hairstyles[i]] = prices[i]

The for loop does the same job as the comprehension, really. I think the comprehension is a more elegant solution, since we only need a single line to achieve the goal.

Both of these create the same output, if we print services to the console:

{'bouffant': 30, 'pixie': 25, 'dreadlocks': 40, 'crew': 20, 'bowl': 20, 'bob': 35, 'mohawk': 50, 'flattop': 35}

Edit: Occurred to me that the dictionary comprehension could be done differently, which didn’t involve relying on indices…

services = {k:v for k,v in zip(hairstyles,prices)}

That variant uses zip to join the hairstyles and prices into nicely wrapped tuples - e.g. ('bouffant', 30) - which can then be manipulated directly into the key-value pairing… Less typing than my original method. :slight_smile:

2 Likes

Thanks a million. That really helped. I did try it out myself several times before seeking the assistance of the community.image

1 Like

I have another quick question regarding indentation.

Here is the question:

You will be given two extremely similar lists, but exactly one of the items in a list will be valued slightly higher than its counterpart (which means that evaluating the value > the other value will return True ).

Create a function that returns whether the first list is slightly superior to the second list.

And here is my code:

def is_first_superior(lst1, lst2):
	for num1 in lst1:
		for num2 in lst2:
			if num1 > num2:
				return True
  return False

I keep running into a ‘IndentationError: unindent does not match any outer indentation level.’
I hope you don’t mind assisting me a second time.