简体   繁体   中英

List Comprehension nested in Dict Comprehension

I want to create a dict with lists as values, where the content on the lists depends on whether or not the key (numbers 1 to 100) is dividable by 3,5 and/or 7

The output would be like this:

{
    1: ['nodiv3', 'nodiv5', 'nodiv7'],
    3: ['div3', 'nodiv5', 'nodiv7'],
    15: ['div3', 'div5', 'nodiv7'],
}

Similar questions where about filtering the list/values, not creating them.

dict_divider = {}
for x in range(0,101):
    div_list= []
    if x % 3 == 0:
        div_list.append('div3')
    else:
        div_list.append('nodiv3')
    if x % 5 == 0:
        div_list.append('div5')
    else:
        div_list.append('nodiv5')
    if x % 7 == 0:
        div_list.append('div7')
    else:
        div_list.append('nodiv7')
    dict_divider[x] = div_list

This works just fine, but is there a way to do this with a pythonic one-/twoliner?

Something along like this: d = dict((val, range(int(val), int(val) + 2)) for val in ['1', '2', '3'])

Pythonic is not about one or two liners. In my opinion is (mainly) about readability, perhaps this could be considered more pythonic :

def label(n, divisor):
    return f"{'' if n % divisor == 0 else 'no'}div{divisor}"


def find_divisors(n, divisors=[3, 5, 7]):
    return [label(n, divisor) for divisor in divisors]


dict_divider = {x: find_divisors(x) for x in range(1, 101)}

print(dict_divider) 

You don't actually need to do all these brute-force divisions. Every third number is divisible by three, every seventh number is divisible by seven, etc:

0 1 2 3 4 5 6 7 8 9 ...  <-- range(10)
0 1 2 0 1 2 0 1 2 0 ...  <-- mod 3

0 1 2 3 4 5 6 7 8 9 ...  <-- range(10)
0 1 2 3 4 5 6 0 1 2 ...  <-- mod 7

So the best approach should take advantage of that fact, using the repeating patterns of modulo. Then, we can just zip the range with however many iterators you want to use.

import itertools

def divs(n):
    L = [f"div{n}"] + [f"nodiv{n}"] * (n - 1)
    return itertools.cycle(L)

repeaters = [divs(n) for n in (3, 5, 7)]
d = {x: s for x, *s in zip(range(101), *repeaters)}

you could write a second loop so that you only have to write if...else only once

dict_divider = {}
div_check_lst = [3, 5, 7]
for x in range(0,101):
    div_list= []
    for div_check in div_check_lst:
        if x % div_check == 0:
            div_list.append(f'div{str(div_check)}')
        else:
            div_list.append(f'nodiv{str(div_check)}')
    dict_divider[x] = div_list

or

dict_divider = {x:[f'{'no' * x % div_check != 0}div{str(div_check)}' for x in range(0,101) for div_check in div_check_lst]}

There is actually a one liner that isnt even that complicated:)

my_dict = {}
for i in range(100):
    my_dict[i] = ['div' + str(n) if i % n == 0 else 'nodiv' + str(n) for n in [3,5,7]]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM