delete odd numbers in a range - python

I'm trying to create a code which deletes the odd numbers in a user-defined range (for example, between 4 and 10). So far I have this:
def even(x,y):
if x > y:
return None
else:
s = list(range(x, y+1))
for i in s:
if s[i]%2!=0:
del s[i]
return s
even(4,10)
When I run the code, it returns [4, 5, 6, 7, 8, 10] instead of [4, 6, 8, 10]. Any idea why?

It makes little sense to create a larger collection and then remove the items you don't want.
I suspect it would be better if you just create the list with what you want up front:
def even(lo, hi):
if lo > hi: return None # although [] may make more sense
return [item for item in range(lo, hi + 1) if item % 2 == 0]
The reason why I state that it may be better to return [] for the case of lo > hi is because that's what gets returned for other edge cases, such as even(3,3).
This gives what you desire, as per the following transcript:
>>> def even(lo, hi):
... if lo > hi: return None
... return [item for item in range(lo, hi + 1) if item % 2 == 0]
...
>>> even(4, 10)
[4, 6, 8, 10]

There are 3 things wrong with your code.
Using s[i] accesses the ith item of the list, but i is already holding the list item because you did for i in s::
>>> s = list(range(4, 11))
>>> s
[4, 5, 6, 7, 8, 9, 10]
>>> for i in s:
... print(i)
...
4
5
6
7
8
9
10
What your loop is actually checking with s[i] is this:
>>> for i in s:
... print(s[i])
...
8 # i=4, s[4]
9 # i=5, s[5]
10 # i=6, s[6]
When you do find an odd number (s[5]=9, 9%2 != 0), you immediately break out of the loop because of the return s. So, your loop will only remove the first odd number it finds, then immediately break out of the loop.
Maybe it's just wrongly indented, but the return s should be at the end of the function, not inside the loop.
You are removing items from the list while you are iterating over it. That is never a good idea, because that will mess up the loop.
>>> s = list(range(4, 11))
>>> for idx, i in enumerate(s):
... print(i)
... if i%2 != 0:
... print("removing i")
... del s[idx]
...
4
5
removing i
7 # notice that 6 was now skipped after removing 5
removing i
9 # notice that 8 was now skipped after removing 7
removing i
With that said, the correct way is to iterate over the input list but the result/output should be on a different list. That way, the loop does not get messed up. The simplest (and most "pythonic") way is by using list comprehension:
def even(x,y):
if x > y:
return None
else:
s = list(range(x, y+1))
return [d for d in s if d % 2 == 0]
Or, you can manually loop using while and then track the correct list index:
def even(x,y):
if x > y:
return None
else:
s = list(range(x, y+1))
idx = 0
while idx < len(s):
if s[idx]%2!=0:
del s[idx]
# after this, s[idx] now points to the next item
else:
idx += 1
# move to the next item
return s

This is the right code. you can remove from list by items and del by index in python:
def even(x,y):
if x > y:
return None
else:
s = list(range(x, y+1))
for i in s:
if i%2 != 0:
s.remove(i)
return s
even(4,10)

Related

How to compare each element in the list and check if it's bigger then the element on the right

hello I am struggling with this problem for school and can't get my code to do what it needs to solve this. The question is: Define an element of a list of items to be a dominator if every element to its right (not just the one
element that is immediately to its right) is strictly smaller than that element. It wants me to count how many denominators are in the list.
def extract_increasing(digits):
countDem = 0
#check and see if there is anything in the list
if not digits:
return 0
#compare the first element to the one on the right of it
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
countDem += 1
return countDem
The code below should check if a number in the list is a dominator.
def is_dominator(lst, idx):
for i in range(idx + 1, len(lst)):
if lst[i] >= lst[idx]:
return False
return True
digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in digits:
print(is_dominator(digits, i))
The error in your code is that you're adding one for the counter every time the next value meets the condition.
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
countDem += 1
Every time digits[x] > digits[y] is met you add one to your counter. You should only add one once you checked that all values to the right meet the condition.
isDem = False
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
isDem = True
else:
isDem = False
#Once you went through all the values to the right you can add one to the counter
if isDem ==True:
countDem += 1
Hope that helps!
You start in the last element, and save always the max_element in every iteration, then you know always if exist some number grater than the current number. This is a little more efficient because it runs through the array only once.
def dominator(li: list):
sol = 0
max_number = -math.inf
for i in range(len(li)-1, -1,-1):
if li[i] > max_number:
sol+=1
max_number = li[i]
return sol
Try list comprehension
lst = [0, 10, 2, 6, 7]
new_lst = [v for k,v in enumerate(lst) if all(v > x for x in lst[k+1:])]
# [10, 7]
Update
def extract_increasing(digits: list) -> int:
countDem = 0
for x, y in enumerate(digits):
if all(y > a for a in digits[x+1:]):
countDem += 1
return countDem
lst = [0, 10, 2, 6, 7]
extract_increasing(lst) # -> 2

Google Foobar - Please Pass The Coded Message Test Case Failure

Question
You have L, a list containing some digits (0 to 9). Write a function solution(L) which finds the largest number that can be made from some or all of these digits and is divisible by 3.
If it is not possible to make such a number, return 0 as the solution. L will contain anywhere from 1 to 9 digits. The same digit may appear multiple times in the list, but each element in the list may only be used once.
Test Cases
Input:
solution.solution([3, 1, 4, 1])
Output:
4311
Input:
solution.solution([3, 1, 4, 1, 5, 9])
Output:
94311
My code
def sum(L):
totalSum = 0
for x in range(len(L)):
totalSum = totalSum + L[x]
return totalSum
def listToInteger(L):
strings = [str(integer) for integer in L ]
concatString = "".join(strings)
finalInt = int(concatString)
return finalInt
def solution(L):
num = sum(L)
if not num % 3:
L.sort(reverse=True) # sort list in descending order to create largest number
return listToInteger(L)
else:
n = num % 3
flag = False
while not flag: # locate digit causing indivisiblity
if n in L:
L.remove(n)
L.sort(reverse=True)
return listToInteger(L)
elif(n > num):
return 0
else:
n += 3
I get the two test cases correct, but there is one hidden case that keeps failing. I'm not sure if the input is not strict enough, or if there is a fault in my logic
the only fault in logic I can think of is if the input was [8,5,3] , the sum would be 16 and 16 % 3 = 1
so it would check the list for 1, 4, 7, 10, 13 , 16 but it wouldnt be in the list so it wouldn't remove 8 or 5. it would return 0 when it should actually return [3].
I added a function for this but even then it was still failing the hidden test case. . .
Any suggestions would be appreciated
Your code seems to assume that there can only be one digit that is wrong. What would you do with input like 1,1,3? sum is 5, n is 2 and you'll try to remove 2, 5, and then fail and return 0.
You need to change your assumptions and check other digits too, and make it possible to remove more than 1 digit when working toward a solution.
This code worked fine for [8,5,3]
example: [8,5,3,6]
sum will be 22
sum%3 will be 1
so numbers need to check in list to delet are 1,4 7,10,13,16,19,22 and it will never delet any elements,since there are no these elements in the list
so still there are 6 and 3 which can be multilple of 3.
so take 3 and 6 in a list and sort them and answer will be 63
def sum(L):
totalSum = 0
for x in range(len(L)):
totalSum = totalSum + L[x]
return totalSum
def listToInteger(L):
strings = [str(integer) for integer in L ]
concatString = "".join(strings)
finalInt = int(concatString)
return finalInt
def solution(L):
num = sum(L)
if not num % 3:
L.sort(reverse=True) # sort list in descending order to create largest number
return listToInteger(L)
else:
n = num % 3
flag = False
while not flag: # locate digit causing indivisiblity
if n in L:
L.remove(n)
L.sort(reverse=True)
return listToInteger(L)
elif(n > num):
k=[]
for i in L:
if i%3==0:
k.append(i)
if len(k)!=0:
k.sort(reverse=True)
return listToInteger(k)
else:
return 0
else:
n += 3
l=[8,5,3]
print(solution(l))

A problem with a generator that merges sequences

I'm studying the theme of generators in python and can't deal with one task.
The point is this: you need to implement a generator that takes 2 non-decline sequences, combines it into 1 non-decline and returns it.
I understood how to write it with a function, but I don't know how to implement it through "yield".
Here's the code for my function:
def merge_lists(lst1, lst2):
res = []
i1, i2 = 0, 0
while i1 < len(lst1) and i2 < len(lst2):
el1, el2 = lst1[i1], lst2[i2]
res.append(el1)
i1 += 1
res.append(el2)
i2 += 1
res.extend(lst1[i1:])
res.extend(lst2[i2:])
return res
I will be glad to get help in writing code and explaining the solution.
Code / Algorithm:
def merge_lists(lst1, lst2):
i = j = 0
while i < len(lst1) and j < len(lst2):
if lst1[i] <= lst2[j]:
yield lst1[i]
i += 1
else:
yield lst2[j]
j += 1
while i < len(lst1):
yield lst1[i]
i += 1
while j < len(lst2):
yield lst2[j]
j += 1
Explanation:
We first initialize variables i, j to zero which represent the indices within the lists lst1 and lst2 respectively.
In the first while loop we consequently checks the smaller element amongst lst1 and lst2 eg. if the smaller one is lst1[i] then we yield the element lst1[i] and increment the index i otherwise we yield the element lst2[j] and increment the index j and rest of the loop is self explanatory.
In the other while loops we check if there are any remaining elements inside lst1 and lst2, and if there are then we yield those elements also.
Example:
lst1 = list(range(0, 10)) # lst1 is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
lst2 = list(range(10, 21)) # lst2 is [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
for num in merge_lists(lst1, lst2): # -- loop through the generator
print(num, end=" ") #--> use the results on demand
Result:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # --> combined non declining sequence
import itertools
itertools.chain.from_iterable([range(10), range(20, 30)])
# Should get your work done.
# Or, in your case
itertools.chain.from_iterable([lst1, lst2])
Please visit https://docs.python.org/3/library/itertools.html for more useful generators.
a = [9,7,5,3]
b = [8,6,4,2]
def merger(l1,l2):
i1=0
i2=0
while i1<len(l1) and i2<len(l2):
if l1[i1]>l2[i2]:
yield l1[i1]
i1+=1
else:
yield l2[i2]
i2+=1
while i1<len(l1):
yield l1[i1]
i1+=1
while i2<len(l2):
yield l2[i2]
i2+=1
for x in merger(a,b):
print(x)
Explanation: merger() is a generator function. Hence every value of x obtained on looping over merger() will return a value. It must be understood that in a generator, the function starts from the same point from which it was left in the last call, hence i1=0,i2=0 would be executed just once.

Using sum() to print the sum of even numbers in a list

I am having trouble finding information on using sum to take from a list. I know how to use sum with range, for example:
sum = 0
for i in range(50):
sum=sum + i
print (sum)
But I can't get my code to work when I am using a list such as [1, 2, 6, 7, 8, 10] and taking the even numbers using sum. Can anyone point me in the right direction?
You can filter out odd-values:
def is_even(x):
# if the remainder (modulo) is 0 then it's evenly divisible by 2 => even
return x % 2 == 0
def sum_of_evens(it):
return sum(filter(is_even, it))
>>> sum_of_evens([1,2,3,4,5])
6
Or if you prefer a conditional generator expression:
>>> lst = [1,2,3,4,5]
>>> sum(item for item in lst if item % 2 == 0)
6
Or the explicit (long) approach:
lst = [1,2,3,4,5]
sum_ = 0
for item in lst:
if item % 2 == 0:
sum_ += item
print(sum_) # 6

How to count occurences at the end of the list

I would like to count the same occurences at the end of the list in python. It's very trivial to do, but I'm interested in some of your interesting solutions as well. List can contain only '1' or '2' item. Result must be in [3,4,5]. If less than 2 quit, if more than 5 return 5.
Examples:
Let's have
L = [1,1,2]
Result: None (quit)
L = [1,2,1,1]
Result: None (quit)
L = [1,2,1,1,1]
Result: 3
L = [1,1,2,2,2,2]
Result: 4
L = [1,2,1,1,1,1,1,1]
Result: 5
Comedy one line answer:
def countOccurencesAtTheEndOfTheList(L):
return (lambda num: None if num <= 2 else min(5, num))(len(L) if all(map(lambda x: x == L[-1], L)) else len(L) - 1 - [idx for idx, x in enumerate(L) if x != L[-1]][-1])
print countOccurencesAtTheEndOfTheList([1,1,2])
print countOccurencesAtTheEndOfTheList([1,2,1,1])
print countOccurencesAtTheEndOfTheList([1,2,1,1,1])
print countOccurencesAtTheEndOfTheList([1,1,2,2,2,2])
print countOccurencesAtTheEndOfTheList([1,2,1,1,1,1,1,1])
output:
None
None
3
4
5
Explanation:
[idx for idx, x in enumerate(L) if x != L[-1]] Gets the indices of each element of L that do not match the last element.
[idx for idx, x in enumerate(L) if x != L[-1]][-1] Gets the index of the rightmost element that does not match the last element. This is only valid if all of the elements in the list are not identical.
len(L) - 1 - [the above line] gets the number of elements at the end of the list that match the last element, if all of the elements in the list are not identical.
all(map(lambda x: x== L[-1], L) returns True only if all of the elements in the list are identical.
len(L) if [the above line] else [the line above the above line] gets the number of elements at the end of the list that match the last element, regardless of whether all the elements in the list are identical or not.
lambda num: None if num <= 2 else min(5, num) returns None if the value is too low, and clamps the maximum possible value to 5.
Warning: for entertainment purposes only. Please do not write code like this.
I fulfil the boring job of giving a readable answer. ;) It works with all kinds of elements, not just 1s and 2s.
In [1]: def list_end_counter(lst):
....: counter = 0
....: for elem in reversed(lst):
....: if elem == lst[-1]:
....: counter += 1
....: else:
....: break
....: if counter < 3:
....: return None
....: elif counter > 5:
....: return 5
....: return counter
A slight modification to save some lines:
In [1]: def list_end_counter(lst):
....: def stop():
....: raise StopIteration()
....: counter = sum(1 if elem == lst[-1] else stop() for elem in reversed(lst))
....: return None if counter < 3 else 5 if counter > 5 else counter
Both give the correct results:
In [2]: print list_end_counter([1,1,2])
None
In [3]: print list_end_counter([1,2,1,1])
None
In [4]: print list_end_counter([1,2,1,1,1])
3
In [5]: print list_end_counter([1,1,2,2,2,2])
4
In [6]: print list_end_counter([1,2,1,1,1,1,1,1])
5
You can try using itertools.groupby by taking advantage of the fact that it will group keys separately if they are unsorted (this returns False for < 2 just for the sake of showing the output - you can change to whatever you want). With groupby, you get an iterable that takes the form (key, values), where values is another iterable that contains all values relating to the key. In this case, we don't care about the key (hence the _), and we convert the values to a list and then take the length of it (this results in a list of lengths that would look like [1, 1, 2] in the case of [1, 2, 1, 1]). We then take the last item from that list which will represent the number of times the last element is repeated. From there, we apply the logic of which value to return:
In [1]: from itertools import groupby
In [2]: def my_func(l):
...: val = [len(list(g)) for _, g in groupby(l)][-1]
...: if val < 3:
...: return False
...: return min(val, 5)
...:
In [3]:
In [4]: L = [1,1,2]
In [5]: my_func(L)
Out[5]: False
In [6]: L = [1,2,1,1]
In [7]: my_func(L)
Out[7]: False
In [8]: L = [1,2,1,1,1]
In [9]: my_func(L)
Out[9]: 3
In [10]: L = [1,1,2,2,2,2]
In [11]: my_func(L)
Out[11]: 4
In [12]: L = [1,2,1,1,1,1,1,1]
In [13]: my_func(L)
Out[13]: 5
Here's another idea:
def count(l):
n = l[::-1].index([2,1][l[-1] - 1])
return min(n, 5) if n > 2 else None
print count([1,1,2])
print count([1,2,1,1])
print count([1,2,1,1,1])
print count([1,1,2,2,2,2])
print count([1,2,1,1,1,1,1,1])
None
None
3
4
5
'index' method of lists can be used to do the search.
I'm assuming that if the list is all 1, you want the same result as when a single 2 is prepended to that list; and if all 2, the same result as if 1 is prepended...
def mejmo_count( lst ):
if len(lst) >= 3: # otherwise answer is None
tail = lst[-2:-6:-1] # extract last part, reversed (omit final)
x = 3-lst[-1] # search for this..
n = (tail + [x]).index(x) # find the first x (sentinel found if not present)
if n >= 2: # n can be 0..4 here
return n+1
return None

Categories

Resources