List Comprehension nested in Dict Comprehension - python

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)}

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

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]}

Related

How do I find the amount of positive integers from 500-5943 that are divisible by 3 or 7 in Python?

I'm new to Python and I'm having some trouble. I know if I'm trying to find the number of numbers from 0-1000 that are divisible by 3 or 7 for example I would use the equation np.floor(1000/3)+np.floor(1000/7)-np.floor(1000/21). However, this time the range doesn't start from 0, so what should I replace 1000 with? Thanks! np is numpy btw
O(n) approach:
result = 0
for x in range(1001):
if (x % 3 == 0 and x % 7 == 0):
result += 1
print(result)
More concise:
result = sum(x % 3 == 0 and x % 7 == 0 for x in range(1001))

Find the sum of all the multiples of 3 or 5 below 1000. (idk what is wrong with my code)

I'm a beginner and I tried this code to list the sum of all the multiples of 3 or 5 below 100, but it gives a wrong answer and I don't know why.
result = 0
result2 = 0
som = 0
sum2 = 0
below_1000 = True
while below_1000:
result = result+3
if result < 1000:
som += result
else:
below_1000 = False
below_1000 = True
while below_1000:
result2 = result2+5
if result2 < 1000:
sum2 += result2
else:
below_1000 = False
final_result = som+sum2
print(final_result)
Since you first loop over multiples of 3, then again over multiples of 5, you are double-counting a lot of values, specifically values that are multiples of both 3 and 5 (for example 15 or 60).
To write this manually, you can use a for loop over range
total = 0
for i in range(1000):
if i % 3 == 0 or i % 5 == 0:
total += i
>>> total
233168
A more concise way to do this same thing is using a generator expression within the sum function
>>> sum(i for i in range(1000) if i % 3 == 0 or i % 5 == 0)
233168

Splitting a string of integers with spaces without converting to a list in python

Basically, I need a code that takes an integer and then prints out the string of numbers with the certain range.
For example:
n = 11
1 2 2 3 3 3 4 4 4 4
n = 7
1 2 2 3 3 3 4
a = []
n = int(input())
if n == 0:
print(n)
for i in range(int(n) + 1):
a += [i] * i
a = ' '.join(map(str, a))
print(a[:n])
This does the job but it counts spaces as characters, so I tried to convert it to an int
n = int(n)
print(' '.join(a[:n]))
But when the n >= 47, it starts to print out 10 as 1 0 which is incorrect
I also tried this code
n = int(input())
for i in range(n):
b = (str(i) * i)
print(b, end = ' ')
But I don't understand how to separate the b with spaces without converting the string to a list and printing it in one line either.
I am not sure if it is even possible.
Maybe something like this?
# initialize variables:
number = 11
counter = 0
for i in range(number):
for j in range(i):
# print number:
print('%d ' %i, end='')
# increment counter (tracks how many numbres to print):
counter += 1
# exit inner loop if counter reaches number:
if counter >= number-1: break
# exit outer loop if counter reaches number:
if counter >= number-1: break
Output:
1 2 2 3 3 3 4 4 4 4
Here's a solution using itertools. Generators, chain, repeat and islice are all lazily evaluated, so this uses O(1) space.
>>> n = 7
>>> from itertools import chain, repeat, islice
>>> gen = chain.from_iterable(repeat(i, i) for i in range(1, n+1))
>>> for i in islice(gen, n):
... print(i, end=' ')
...
1 2 2 3 3 3 4
This seems simple. But this does solve the problem?
>>> for i in t:
... if i!=" ": print i
...
1
2
2
3
3
3
4
4
4
4
or even string replace like,
print t.replace(" ","\n")
1
2
2
3
3
3
4
4
4
4

Compare and count same digits between two numbers

I'm looking to write a function that receives two four-digit numbers (m, n) that counts how many digits are the same between m and n, including duplicates and zeroes on the left. The thing is, my professor only taught us how to use loops, and don't want us to use lists and intersections, and I'm no able to do it.
For example, if m = 331 and n = 3, it should return 2 as the amount of equal digits, bit if n = 33, it should return 3 same digits.
>>> compare_digits(331, 3)
2
>>> compare_digits(332, 33)
3
Edit: This is the code I created before, and it counts same digits more than it should, but the central idea is the usage of % and // to read each digit, but it's not working...
def compare_digits(m, n):
read_ndigits = 0
same_digits = 0
while read_ndigits < 4: #number of digits
current_n = n % 10
read_mdigits = 0
while read_mdigits < 4:
current_m = m % 10
if current_n == current_m:
same_digits += 1
m //= 10
read_mdigits += 1
n //= 10
read_ndigits += 1
return same_digits
The output is very messy and I can't even recognize any pattern.
You can use collections.Counter() with set intersection:
from collections import Counter
def compare_digits(m, n):
m_counts = Counter(str(m).zfill(4))
n_counts = Counter(str(n).zfill(4))
return sum(min(m_counts[k], n_counts[k]) for k in m_counts.keys() & n_counts.keys())
print(compare_digits(331, 3)) # 2
print(compare_digits(332, 33)) # 3
print(compare_digits(3, 331)) # 2
Well, I decided to limit the number of digits to 4 and not be generic about it, so I wrote this and it worked perfectly:
def compare_digits(m, n):
a = m % 10
m //= 10
b = m % 10
m //= 10
c = m % 10
m //= 10
d = m % 10
read_ndigits = 0
same_digits = 0
while read_ndigits < 4:
current = n % 10
if current == a:
same_digits += 1
a = None
elif current == b:
same_digits += 1
b = None
elif current == c:
same_digits += 1
c = None
elif current == d:
same_digits += 1
d = None
n //= 10
read_ndigits += 1
return same_digits
Thank you all for your help :)

Creating a "snake" counter

I'm just trying to get the logic straight and using Python to help me do it. Ultimately, I need to solve this problem using ImageJ macro language.
I have no idea if I'm using the right term, but I'd like to create a "snake" counter.
x = 1
number = 12
maxVal = 3
minVal = 1
for i in xrange(number):
%do something
x = incrementSnakeCounter(x, maxVal, minVal)
print("i = ", i)
print("x = ", x)
The "snake" part is making the counter go up only to the maxVal, repeating that number on the next iteration, counting down to the minVal, repeating that value on the next iteration, and repeating the process.
For instance, in the above
I'd like the following to happen :
i = 0
x = 1
i = 1
x = 2
i = 2
x = 3
i = 3
x = 3
i = 4
x = 2
i = 5
x = 1
i = 6
x = 1
i = 7
x = 2
i = 8
x = 3
i = 9
x = 3
i = 10
x = 2
i = 11
x = 1
You will find some useful utils in itertools:
from itertools import chain, cycle
def snake(lower, upper):
return cycle(chain(range(lower, upper+1), range(upper, lower-1, -1)))
> s = snake(1,3)
> [next(s) for _ in range(10)]
[1, 2, 3, 3, 2, 1, 1, 2, 3, 3]
Here's a silly mathematical solution:
def snake(low, high, x):
k = (high-low+1)
return k - int(abs(x % (2*k) + low - k - 0.5))
[snake.snake(1,3,x) for x in range(8)]
[1, 2, 3, 3, 2, 1, 1, 2]
Add a conditional to determine if x should be increasing or decreasing at any given point within the loop.
x = 1
number = 12
maxVal = 3
minVal = 1
for i in xrange(number):
%do something
if(xIsIncreasing)
x = incrementSnakeCounter(x, maxVal, minVal)
else
x = decrementSnakeCounter(x, maxVal, minVal)
print("i = ", i)
print("x = ", x)
Then inside your incrementSnakeCounter() change the value of xIsIncreasing to false when x == maxVal and inside your decrementSnakeCounter() to true when x == minVal (you'll have to do some work to make sure that you're staying at the same value twice in a row, I don't have time right now to solve that part for you).
You can write a little custom generator.
The key is to create a list of the pattern you want to repeat [1, 2, 3, 3, 2, 1] and then index that with the modulo of the length to get the repeating behavior:
def snake(x, max_v=3, min_v=1):
cnt=0
sn=list(range(min_v, max_v+1,1))+list(range(max_v, min_v-1,-1))
while cnt<x:
yield cnt, sn[cnt%len(sn)]
cnt+=1
Then:
for i,x in snake(12):
print("i=",i)
print("x=",x)
print()
Prints:
i= 0
x= 1
i= 1
x= 2
i= 2
x= 3
i= 3
x= 3
i= 4
x= 2
i= 5
x= 1
i= 6
x= 1
i= 7
x= 2
i= 8
x= 3
i= 9
x= 3
i= 10
x= 2
i= 11
x= 1

Categories