How to count occurences at the end of the list - python

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

Related

How to compare two lists according to a specific requirement?

I have a reference list my_ref = [1,2,3]. I am trying to create a function foo that takes a list of length 3 (let's call it list_n). foo will check if adding just value 1 to only one arbitrary element of list_n, makes each element of list_n be greater than each element of my_ref pairwise. Since this explanation may be confusing, I present several examples as follows:
Example 1:
my_ref = [1,2,3]
list1 = [0,2,4]
If number 1 is added to one of the zeroes, then we have [0,2,4], so now all elements of list1 are greater than equal to all greatest elements of my_list, i.e. 1 = 1, 2=2 and 4 > 3. So the function should return True with that element to be added by 1, which was 0 in this example.
Example 2:
my_ref = [1,2,3]
list_2 = [2,2,1]
If number 1 is added to one of the two's, then we have [3,2,1], so all elements of list1 are greater than equal to all elements of my_list, i.e. 3 = 3, 2=2 and 1 = 1. Therefore, the function should return True with that element to be added by 1, which was 2 in this example.
Example 3:
my_ref = [1,2,3]
list_3 = [5,3,1]
By adding number 1 to any one's of list_3, we will have: 5>3, 3>2, 2>1. Hence, the function returns True with the number 1.
BUT
Example 4:
my_ref = [1,2,3]
list_3 = [0,2,1]
There is no way to add number 1 to any elements of list_3 so that the explained requirement is met. Thus, the function should return False with None.
I have coded as follows:
def fun(l):
my_ref = [1, 2, 3]
l.sort()
for i in range(2, 0, -1):
if l[i] < ref[i] and sum(l) >= 5:
return True, l[i]
return False, None
It works fine with most examples but not all. For instance, print(fun([4, 1, 0])) returns (True, 1) which is wrong, instead it should return (False, None). I have tried a lot. Each time, the function with some examples work fine but with some examples doesn't. I hope my the problem is now clear.
How do I fix it?
Thank you.
def func(l):
superior = 0
ref = [0, 1, 2, 3]
l1 = l[:]
l2 = l[:]
l3 = l[:]
l4 = l[:]
l1[0]+=1
l2[1]+=1
l3[2]+=1
l4[3]+=1
targets = [l1, l2, l3, l4]
for i in range(len(targets)):
if get_superior_count(targets[i]) >= 3:
return True, i
return False, None
def get_superior_count(l):
ref = [0, 1, 2, 3]
target = l[:]
count = 0
l.sort()
m = 3
n = 3
while m > 0 and n > 0:
if target[m] >= ref[n]:
count+=1
target.pop(m)
ref.pop(n)
m-=1
n-=1
return count
Not sure it is what you want (and it's a bit ugly) but seems working with your examples.
def checker(my_ref, l):
if sum(l) +1 < sum(my_ref):
return False
return True
def align_lists(my_ref, l):
return list(zip(sorted(my_ref), sorted(l)))
def compare_paired_elements(l, my_ref):
requires_bump = 0
if not checker(my_ref, l):
return False
for pair in align_lists(my_ref, l):
ref_num = pair[0]
num = pair[1]
if num < ref_num:
if num + 1 < ref_num:
continue
if num + 1 == ref_num:
requires_bump += 1
print(num, "needs a bump to match", ref_num)
if requires_bump > 1:
return False
else:
return True
Examples:
> my_ref = [1,2,3]
> l = [0,2,4]
> compare_paired_elements(l, my_ref)
0 needs a bump to match 1
True
> l = [4,1,0]
> compare_paired_elements(l, my_ref)
0 needs a bump to match 1
1 needs a bump to match 2
False

Python, How to concatenate successive and similar elements of a list?

For example:
the input is :
['1','2','3','3','7','7','4']
I want this output:
['1','2','33','77','4']
I made this function but it works only if len of the list is power of 2
def function(L):
M=[]
for i in range(0,len(L),2):
if L[i]==L[i+1]:
M.append(L[i]+L[i+1])
else:
M.append(L[i])
M.append(L[i+1])
return M
import itertools
def function(L):
return [ ''.join(g) for k, g in itertools.groupby(L) ]
print(function(['1','2','3','3','7','7','4']))
A counter would probably serve you well, but if you want a solution similar to yours and import-less know that while loops are much more appropiate for codes like yours:
def function(L):
M = []
i = 0
while i < len(L):
if i+1 < len(L):
if L[i]==L[i+1]:
M.append(L[i]+L[i+1])
i += 1
else:
M.append(L[i])
i += 1
else:
M.append(L[i])
break
return M
This is not the most efficient way to face the problem, but comes straight from the code you programmed, just to show how in this case you could have found a solution (again, probably not quite the best one) using a while loop instead of a for loop, since it allows you to modify i's value inside the loop.
This solution concatenates all similar elements in the list. I used Collections module to identify the similar elements and concatenated as per your need
>>> from collections import Counter
>>>
>>> a = ['1','2','3','3','7','7','4']
>>> Counter(a)
Counter({'3': 2, '7': 2, '1': 1, '2': 1, '4': 1})
>>> b = Counter(a)
>>> for ele in Counter(a):
... print (ele, b[ele])
...
1 1
2 1
3 2
7 2
4 1
>>>
>>> c = [str(ele)*int(b[ele]) for ele in b]
>>> c
['1', '2', '33', '77', '4']
>>>
I would suggest splitting the problem in two subproblems. One is to concatenate a series of similar elements (auxiliary function), the other is to advance through the list to the next dissimilar element and to create the result list:
def function(L) :
if L == [] :
return []
else :
(j,val) = concatvalues(L)
M = [val]
M.extend(function(L[j:]))
return M
def concatvalues(L) :
j = 1
s = L[0]
while j < len(L) and L[j] == L[0] :
s += L[j]
j += 1
return (j,s)
print(function(['1','2','3','3','7','7','4']))
This function works
def function(L):
M = []
K = L
for el in K:
repet = K.count(el)
K.remove(el)
M.append(el*repet)
return M
*Updated

delete odd numbers in a range

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)

How do I get smallest postitive integer not present in the array

I am trying to find out smallest positive number not present in the list a
def smallitem(a):
a = sorted(set(a))
lst = []
for item in a:
item + = 1
if item not in a:
lst.append(item)
continue
mst = []
for item in lst:
if item < 1:
item += 1
if item not in a:
mst.append(item)
continue
n = 0
if mst:
n = min(mst)
return n or min(lst)
I think I have got the solution but it doesnt look correct to me the way I have done it.
for example:
smallitem([-1, -3]) # 1
smallitem([1,3,6,4,1,2, 87]) # 5
You can convert the list to a set and then keep incrementing a positive integer from 1 until it is not found in the set:
def smallitem(a):
set_a = set(a)
i = 1
while i in set_a:
i += 1
return i
Perhaps there's a lighter way do this.
The time complexity is always O(n).
def small_item(a):
s = set(a)
for i in range(1, max(s)):
if i not in s:
return i
return max(max(s) + 1, 1)
print small_item([1,3,6,4,1,2, 87])
print small_item([-1, -3])
Here's anther way to do this:
def smallitem(l):
l = list(set(sorted(l)))
x = l[0]
for i in l:
if i != x and x >= 1:return x
x += 1
return 1
Testing:
>>> smallitem([-3, -1])
1
>>> smallitem([1, 3, 6, 4, 1, 2, 87])
5
>>>

How to treat the last element in list differently in Python?

I need to do some special operation for the last element in a list.
Is there any better way than this?
array = [1,2,3,4,5]
for i, val in enumerate(array):
if (i+1) == len(array):
// Process for the last element
else:
// Process for the other element
for item in list[:-1]:
print "Not last: ", item
print "Last: ", list[-1]
If you don't want to make a copy of list, you can make a simple generator:
# itr is short for "iterable" and can be any sequence, iterator, or generator
def notlast(itr):
itr = iter(itr) # ensure we have an iterator
prev = itr.next()
for item in itr:
yield prev
prev = item
# lst is short for "list" and does not shadow the built-in list()
# 'L' is also commonly used for a random list name
lst = range(4)
for x in notlast(lst):
print "Not last: ", x
print "Last: ", lst[-1]
Another definition for notlast:
import itertools
notlast = lambda lst:itertools.islice(lst, 0, len(lst)-1)
If your sequence isn't terribly long then you can just slice it:
for val in array[:-1]:
do_something(val)
else:
do_something_else(array[-1])
using itertools
>>> from itertools import repeat, chain,izip
>>> for val,special in izip(array, chain(repeat(False,len(array)-1),[True])):
... print val, special
...
1 False
2 False
3 False
4 False
5 True
Version of liori's answer to work on any iterable (doesn't require len() or slicing)
def last_flagged(seq):
seq = iter(seq)
a = next(seq)
for b in seq:
yield a, False
a = b
yield a, True
mylist = [1,2,3,4,5]
for item,is_last in last_flagged(mylist):
if is_last:
print "Last: ", item
else:
print "Not last: ", item
If your logic is never breaking out of the loop then a for...else construct might work:
In [1]: count = 0
...: for i in [1, 2, 3]:
...: count +=1
...: print("any item:", i)
...: else:
...: if count:
...: print("last item: ", i)
...:
any item: 1
any item: 2
any item: 3
last item: 3
You need the count variable just in case the iterable is empty, otherwise the variable i won't be defined.
Use more_itertools:
import more_itertools
array = [1,2,3,4,5]
peekable = more_itertools.peekable(array)
last = object()
for val in peekable:
if peekable.peek(last) is last:
print('last', val)
else:
print(val)
Gives:
1
2
3
4
last 5
Simple way with an if condition:
for item in list:
print "Not last: ", item
if list.index(item) == len(list)-1:
print "Last: ", item
for i in items:
if i == items[-1]:
print 'The last item is: '+i

Categories

Resources