I have the following list of numbers:
List = [1, 2, 3, 4, 5, 6, 15]
I want the indexes of those numbers which are multiple of n, so I do:
def indexes(List, n):
# to enumerate the numbers
E = enumerate(List)
# filtering tuples
F = list(filter(lambda x: x[1] % n == 0, E))
return [ i[0] for i in F]
indexes(List, 2)
[1, 3, 5]
That's ok, but what happens when I add the variable m?
def Index( L, n, m):
# enumeration
E = enumerate(L)
# filtering tuples
F_n = list(filter(lambda x: x[1]%n == 0, E))
F_m = list(filter(lambda x: x[1]%m == 0, E))
L_n = [ l[0] for l in F_n]
L_m = [ J[0] for J in F_m]
return L_n + L_m
>>>Index(List, 2, 5):
[1, 3, 5]
Why that code doesn't returns [1, 3, 5, 4, 6]?
What is the mistake?
And how to create the function that returns that list?
You can use a list comprehension in combination with enumerate method.
Also, you can apply extended iterable unpacking operator in order to pass parameters as many you need.
List = [1, 2, 3, 4, 5, 6, 15]
def indexes(List, *vars):
return [index for index, item in enumerate(List) for i in vars if item % i == 0 ]
print(indexes(List, 2, 5))
Output
[1, 3, 5, 4, 6]
A more general and Pythonic approach that works for any number of variables is to use an any() or all() function that check the Truth value of the condition for all the arguments. If you want the index to belongs to an item that is divisible buy all the arguments you need all() other wise you can use any() that returns True right after it encounters a match.
def indexes(lst, *args):
return [i for i, j in enumerate(lst) if any(j % arg == 0 for arg in args)]
Demo:
>>> lst = [1, 2, 3, 4, 5, 6, 15, 99, 200, 13, 17, 400]
>>> indexes(lst, 99, 5, 2, 100)
[1, 3, 4, 5, 6, 7, 8, 11]
>>>
And with all():
>>> indexes(lst, 5, 2, 100)
[8, 11]
The issue is enumerate returns an iterator from an iterable. Once it is exhausted, you may not use it again. Therefore, you can simply define a new enumerate iterator:
lst = [1, 2, 3, 4, 5, 6, 15]
def Index( L, n, m):
# enumeration - notice we define 2 objects
E, F = enumerate(L), enumerate(L)
F_n = list(filter(lambda x: x[1]%n == 0, E))
F_m = list(filter(lambda x: x[1]%m == 0, F))
L_n = [ l[0] for l in F_n]
L_m = [ J[0] for J in F_m]
return L_n + L_m
res = Index(lst, 2, 5)
print(res)
[1, 3, 5, 4, 6]
Note there are better ways you can implement your algorithm.
Related
#Write a function named add_one_to_all that takes in a list of numbers and adds one to each of the original numbers
assert add_one_to_all([0, 0, 0]) == [1, 1, 1]
assert add_one_to_all([1, 2, 3]) == [2, 3, 4]
assert add_one_to_all([6, 7, 8]) == [7, 8, 9]
I've tried this
def my_list ([x,y,z]):
new_list = [x+1 for x in my_list]
def add_one_to_all(my_list):
return [x+1 for x in my_list]
def add_one_to_all(integer):
return integer+1
int_list = [1, 2, 3, 4, 5]
# Adding one on each integer
# using list comprehension.
output_list = [add_one_to_all(i) for i in int_list]
print(output_list)
The output will be [2,3,4,5,6]
You can use map
l = [1, 2, 3]
print(list(map(lambda x:x+1, l)))
result
[2, 3, 4]
I was curios about the question: Eliminate consecutive duplicates of list elements, and how it should be implemented in Python.
What I came up with is this:
list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
i = 0
while i < len(list)-1:
if list[i] == list[i+1]:
del list[i]
else:
i = i+1
Output:
[1, 2, 3, 4, 5, 1, 2]
Which I guess is ok.
So I got curious, and wanted to see if I could delete the elements that had consecutive duplicates and get this output:
[2, 3, 5, 1, 2]
For that I did this:
list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
i = 0
dupe = False
while i < len(list)-1:
if list[i] == list[i+1]:
del list[i]
dupe = True
elif dupe:
del list[i]
dupe = False
else:
i += 1
But it seems sort of clumsy and not pythonic, do you have any smarter / more elegant / more efficient way to implement this?
>>> L = [1,1,1,1,1,1,2,3,4,4,5,1,2]
>>> from itertools import groupby
>>> [key for key, _group in groupby(L)]
[1, 2, 3, 4, 5, 1, 2]
For the second part
>>> [k for k, g in groupby(L) if len(list(g)) < 2]
[2, 3, 5, 1, 2]
If you don't want to create the temporary list just to take the length, you can use sum over a generator expression
>>> [k for k, g in groupby(L) if sum(1 for i in g) < 2]
[2, 3, 5, 1, 2]
Oneliner in pure Python
[v for i, v in enumerate(your_list) if i == 0 or v != your_list[i-1]]
If you use Python 3.8+, you can use assignment expression :=:
list1 = [1, 2, 3, 3, 4, 3, 5, 5]
prev = object()
list1 = [prev:=v for v in list1 if prev!=v]
print(list1)
Prints:
[1, 2, 3, 4, 3, 5]
A "lazy" approach would be to use itertools.groupby.
import itertools
list1 = [1, 2, 3, 3, 4, 3, 5, 5]
list1 = [g for g, _ in itertools.groupby(list1)]
print(list1)
outputs
[1, 2, 3, 4, 3, 5]
You can do this by using zip_longest() + list comprehension.
from itertools import zip_longest
list1 = [1, 2, 3, 3, 4, 3, 5, 5].
# using zip_longest()+ list comprehension
res = [i for i, j in zip_longest(list1, list1[1:])
if i != j]
print ("List after removing consecutive duplicates : " + str(res))
Here is a solution without dependence on outside packages:
list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
L = list + [999] # append a unique dummy element to properly handle -1 index
[l for i, l in enumerate(L) if l != L[i - 1]][:-1] # drop the dummy element
Then I noted that Ulf Aslak's similar solution is cleaner :)
To Eliminate consecutive duplicates of list elements; as an alternative, you may use itertools.zip_longest() with list comprehension as:
>>> from itertools import zip_longest
>>> my_list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
>>> [i for i, j in zip_longest(my_list, my_list[1:]) if i!=j]
[1, 2, 3, 4, 5, 1, 2]
Plenty of better/more pythonic answers above, however one could also accomplish this task using list.pop():
my_list = [1, 2, 3, 3, 4, 3, 5, 5]
for x in my_list[:-1]:
next_index = my_list.index(x) + 1
if my_list[next_index] == x:
my_list.pop(next_index)
outputs
[1, 2, 3, 4, 3, 5]
Another possible one-liner, using functools.reduce (excluding the import) - with the downside that string and list require slightly different implementations:
>>> from functools import reduce
>>> reduce(lambda a, b: a if a[-1:] == [b] else a + [b], [1,1,2,3,4,4,5,1,2], [])
[1, 2, 3, 4, 5, 1, 2]
>>> reduce(lambda a, b: a if a[-1:] == b else a+b, 'aa bbb cc')
'a b c'
I have a list = [1, 2, 3, 3, 6, 8, 8, 10, 2, 5, 7, 7]
I am trying to use groupby to convert it into
1
2
3
3
6
8,8
10
2,
5
7,7
Basically, anything greater then 6, I like to group them, otherwise I want to keep them ungrouped.
Any hint on how I can do this with itertool groupby
My code currently:
for key, group in it.groupby(numbers, lambda x: x):
f = list(group)
if len(f) == 1:
split_list.append(group[0])
else:
if (f[0] > 6): #filter condition x>6
for num in f:
split_list.append(num + 100)
else:
for num in f:
split_list.append(num)
You can use itertools.groupby to group all elements greater than 6 and with groups of length greater than 1. All other elements remain ungrouped.
If we want groups as standalone lists, we can use append. If we want groups flattened, we can use extend.
from itertools import groupby
lst = [1, 2, 3, 3, 6, 8, 8, 10, 2, 5, 7, 7]
result = []
for k, g in groupby(lst):
group = list(g)
if k > 6 and len(group) > 1:
result.append(group)
else:
result.extend(group)
print(result)
Output:
[1, 2, 3, 3, 6, [8, 8], 10, 2, 5, [7, 7]]
You can use flatten() function shown below.
This function is modified version from https://stackoverflow.com/a/40857703/501852
The function below is a generator object (python ver >= 3.8).
Added conditioning function as input parameter.
from typing import Iterable
def flatten(items, cond_func=lambda x: True):
"""Yield items from any nested iterable"""
for x in items:
if isinstance(x, Iterable) \
and not isinstance(x, (str, bytes)) \
and cond_func(x): # Conditioning function
yield from flatten(x)
else:
yield x
Then you can use list comprehension variant:
res = flatten( [list(g) for k, g in groupby(lst)],
cond_func = lambda x: x[0] <= 6 or len(x) == 1)
# Conditions to flatten
print(res)
And you can use generator variant:
res = flatten( (list(g) for k, g in groupby(lst)),
cond_func = lambda x: x[0] <= 6 or len(x) == 1)
# Conditions to flatten
print(list(res))
Output is
[1, 2, 3, 3, 6, [8, 8], 10, 2, 5, [7, 7]]
On Python 2.7 I want to create a function which calculates the Mode of a list without using any built-in functions (e.g, not allowed to use Count)
Try the following function, which does not use sum(), count(), or any built-in function apart from append, to add to a list.
def mode(lst):
sums = {}
for i in lst:
if i in sums:
sums[i]+=1
else:
sums[i] = 1
maxModes = [[-1, -1]]
for j in sums:
if sums[j] > maxModes[0][0]:
maxModes = [[sums[j], j]]
elif sums[j] == maxModes[0][0]:
maxModes.append([sums[j], j])
indices = 0
for k in maxModes:
indices+=1
if indices == 1:
return maxModes[0][1]
else:
thisSum = 0
for l in maxModes:
thisSum+=l[1]
return float(thisSum)/indices
>>> mode([1, 2, 3, 3, 4])
3
>>> mode([1, 2, 3, 3, 4, 4])
3.5
>>> mode([1, 2, 3, 3, 4, 4, 5, 5, 5])
5
>>> mode([1, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 6])
5.5
>>> mode([1, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 6, 9, 9, 9])
6.666666666666667
>>> mode([1, 2])
1.5
>>> mode([1])
1
>>> mode([])
-1
>>>
Taking an example of k = [1,2,3,5,3,1,6,3] as a sample list whose mode needs to be calculated
def quickSrt(lst):
if len(lst) < 2:
return lst
pivot = lst[0]
l = quickSrt([x for x in lst[1:] if x < pivot])
r = quickSrt([x for x in lst[1:] if x >= pivot])
return l + [pivot] + r
k = [1,2,3,5,3,1,6,3]
d = {}
for i in k:
if i in d:
d[i] += 1
else:
d[i] = 1
values = [value for key, value in d.items()]
val = quickSrt(values)
for x,y in d.items():
if y == val[-1]:
print(x)
For example I have a list:
L = [1, 2, 2, 3, 1, 1, 6, 10, 1, 3]
And I want to remove all 1's from the list, so that I would get:
L = [2, 2, 3, 6, 10, 3]
I tried iterating over the list and then deleting the element if it equals the element I want to delete (1 in this case), but turns out you can't iterate and delete stuff from a list at the same time since it messes up the counting. The best thing I've come up with is just construct a new list L2 that doesn't contain any of the 1's and then put that into L, but is there a solution that only involves mutating L?
but is there a solution that only involves mutating L?
You can rather iterate over a copy of your List - L[:], and remove element from L. That won't mess up counting.
If you really don't want to create a new list, you would have to iterate in reverse using range(len(L) - 1, -1, -1), but that won't be 'Pythonic' anymore.
>>> for x in L[:]:
... if x == 1:
... L.remove(x)
...
>>> L
[2, 2, 3, 6, 10, 3]
However, you can also use List Comprehension:
>>> L = [1, 2, 2, 3, 1, 1, 6, 10, 1, 3]
>>> L[:] = [x for x in L if x != 1]
>>> L
[2, 2, 3, 6, 10, 3]
Using the filter built-in:
>>> L = [1, 2, 2, 3, 1, 1, 6, 10, 1, 3]
>>> filter(lambda x: x is not 1, L)
[2, 2, 3, 6, 10, 3]
Or you can assign it back to L:
>>> L = [1, 2, 2, 3, 1, 1, 6, 10, 1, 3]
>>> L = filter(lambda x: x is not 1, L)
>>> L
[2, 2, 3, 6, 10, 3]
You can also wrap this concept into methods, to be able to specify a list of items to include/exclude:
def exclude(collection, exclude_list):
return filter(lambda x: x not in exclude_list, collection)
def include(collection, include_list):
return filter(lambda x: x in include_list, collection)
>>> L = [1, 2, 2, 3, 1, 1, 6, 10, 1, 3]
>>> L = exclude(L, [1])
>>> L
[2, 2, 3, 6, 10, 3]
If you don't want to mutate the list or generate any new copy of it. You can loop from end to begin and use index:
>>> for i in range(len(L)-1, -1, -1):
... if L[i] == 1:
... del L[i]
This is awkward to do in python without making a copy of the list...
This will do it without making a copy.
a = range(6) # Some array [0,1,2,3,4,5]
i=0
while i < len(a):
if a[i] == 4:
del a[i]
else:
i += 1
OUTPUT
>>> a
[0, 1, 2, 3, 5]