1

The problem is for all the numbers (1 - 20) the highest single digit (1 - 9) of any of the numbers is divisible by.

I have a for loop as given below:

values = [[] for value in range(1, 11)]

for num in range(1, 21):
    highest = 0
    for div in range(1, 10):
        if(num % div == 0 and div > highest):
            highest = div
    
    values[highest].append(num)

The following for loop output:

[[], [1, 11, 13, 17, 19], [2], [3], [4], [5, 10, 15, 20], [6, 12], [7, 14], [8, 16], [9, 18]]

The empty list [] that in the output can be ignored. For example:

[[1, 11, 13, 17, 19], [2], [3], [4], [5, 10, 15, 20], [6, 12], [7, 14], [8, 16], [9, 18]]

I want to convert the following for loop to list comprehension, can anyone please help me.

15
  • I don't believe it's that simple - you do appending on index and it's the index that needs calculating via additional loop... What do you think about dict comprehension (dict pairing number to its highest) and then "flipping" it? Commented Nov 4, 2021 at 14:22
  • 1
    You can do it if you'll change logic a bit: nums = list(range(1, 21)) ; values = [[nums.pop(i) for i in range(len(nums) - 1, -1, -1) if not nums[i] % d] for d in range(9, 0, -1)]. But it will generate list in reversed order. Commented Nov 4, 2021 at 14:25
  • @OlvinRoght Wrap it into reversed (and do range(9, -1, -1) to not skip index 0) and it will be what OP needs. Post it as an answer! | Now excuse me, I need to take my time to understand how you did that because it's amazing! :D Commented Nov 4, 2021 at 14:30
  • @h4z3, I actually know how to do this, decided to leave this for OP. Also, changing to range(9, -1, -1) is very bad idea, you can say why. Commented Nov 4, 2021 at 14:31
  • The question is: Why do you want it as a list comprehension? It will loose readability and not gain anything. Commented Nov 4, 2021 at 14:34

3 Answers 3

4

The problem is for all the numbers (1 - 20) the highest single digit (1 - 9) of any of the numbers is divisible by.

I'd implement it in another way using list.pop():

nums = list(range(1, 21))
values = []
for d in range(9, 0, -1):
    temp = []
    for i in range(len(nums) - 1, -1, -1):  # iterating in reverse order
        if not nums[i] % d:  # nums[i] % d == 0
            temp.insert(0, nums.pop(i))
    values.insert(0, temp)
print(values)

Basically, we initialize list of numbers from 1 to 20 and pop value which divisible by digit. I used list.insert() here to make it produce result in same order as your solution.

In comment I posted code which will produce list in reversed order, here is it:

nums = list(range(1, 21))
values = [[nums.pop(i) for i in range(len(nums) - 1, -1, -1) if not nums[i] % d] for d in range(9, 0, -1)]

You can add two calls of reversed() (as i suggested in another comment) or reverse it using slicing to make it return list in proper order:

nums = list(range(1, 21))
values = [[nums.pop(i) for i in range(len(nums) - 1, -1, -1) if not nums[i] % d][::-1] for d in range(9, 0, -1)][::-1]

Upd. I've decided to add some tests result which should help to see why list comprehension doesn't make it any better. If you want to reproduce tests, code is here.

Tests results (lower is better):

Temple Version: 0.49361748499999997
Tranbi: 1.794325605
JonSG: 5.4978652320000005
JonSG(+ Olvin): 4.834248347000001
Olvin Roght (v1): 0.34827960000000147
Olvin Roght (v2): 0.4133600079999997
Kelly Bundy: 0.19429717999999951
Temple Version(+ Kelly): 0.20479166999999876
Sign up to request clarification or add additional context in comments.

9 Comments

@KellyBundy, those old greeks were smart! Added your method to comparison. Consider posting it as answer, that's a good option.
Btw, replacing the OP's (num % div == 0 and div > highest) with not num % div seems to make it about as fast as yours.
@KellyBundy, could be, did not investigate. Reverting loop logic with break could also make it faster. List relocation is not cheap, so mine solution is not fastest obviously, at least in Python.
Right, further optimized OP's almost catches mine.
|
1

Here is a slightly different take on the comprehension.

values = [
    [
        num for num
        in range(1, 21)
        if index == max([
            i for i
            in range(1, 10)
            if not num%i
        ])
    ] for index, _
    in enumerate(range(1, 11))
]
print(values)

This will give you:

[[], [1, 11, 13, 17, 19], [2], [3], [4], [5, 10, 15, 20], [6, 12], [7, 14], [8, 16], [9, 18]]

It can be simplified a bit but I was attempting to match what you had done as closely as I could.

Here is a straightforward simplification and improvement as highlighted by @olvin-roght:

values = [
    [
        num for num in range(1, 21)
        if index == next(i for i in range(9, 0, -1) if not num%i)
    ] for index in range(10)
]

Comments

0

Why do you need the first empty list? The solution proposed by @Olvin makes clever use of pop. You'll have to reverse it to get what you want though.

Here a solution that might be easier to understand and gives you the result you expect:

print([ [num for num in range(1,21) if num % div == 0 and all(num % x != 0 for x in range(div + 1, 10)) ] for div in range(1,10)])

Output:

[[1, 11, 13, 17, 19], [2], [3], [4], [5, 10, 15, 20], [6, 12], [7, 14], [8, 16], [9, 18]]

The use of all will generate more computing time (so will reverse in the other solution).
It's fun to create comprehension if you want to train but loops are fine as well. And here much easier to read!

EDIT

Since so many people commented on the question and my answer I started to look closer to the problem and timed all solutions. It turns out @Olvin's solution is the fastest and matches the speed of OP's loop. Followed by my solution (approx. 3x slower on my machine) and @JonSG's solution (9x slower) turns out reversing the lists is much better optimized than I thought...

3 Comments

"might be easier to understand" - doubt that, mine is complex too. List comprehension is just not a way to go here.
well I also wrote "might" ;-) Loved your solution though! I said it might be easier to understand cause it's closer to the initial loop structure actually
@Tranbi Thank you for your solution. This will work. List comprehension is not for this problem, it's making complex.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.