0

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'])

wim
  • 338,267
  • 99
  • 616
  • 750
Link
  • 61
  • 1
  • 7

4 Answers4

1

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) 
Dani Mesejo
  • 61,499
  • 6
  • 49
  • 76
1

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)}
wim
  • 338,267
  • 99
  • 616
  • 750
0

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]}
Hadrian
  • 917
  • 5
  • 10
0

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]]
Alexander Riedel
  • 1,329
  • 1
  • 7
  • 14
  • however how would you do it if it HAD to be different values, so not just div/nodiv but div3/nodiv3 and so on? – Link Nov 11 '20 at 16:56
  • you could use some mor else/if statements for that purpose i guess, but at some point its getting ugly.. https://stackoverflow.com/a/9987533/11951277 – Alexander Riedel Nov 11 '20 at 17:05
  • My approach is about 400% more efficient than this answer, according to `timeit`. – wim Nov 11 '20 at 17:06