I have a list and I want to use a certain function only on those entries of it that fulfills a certain condition - leaving the other entries unmodified.
Example: Say I want to multiply by 2 only those elements who are even.
a_list = [1, 2, 3, 4, 5]
Wanted result:
a_list => [1, 4, 3, 8, 5]
But [elem * 2 for elem in a_list if elem %2 == 0] yields [4, 8] (it acted as a filter in addition).
What is the correct way to go about it?
Use a conditional expression:
[x * 2 if x % 2 == 0 else x
for x in a_list]
(Math geek's note: you can also solve this particular case with
[x * (2 - x % 2) for x in a_list]
but I'd prefer the first option anyway ;)
a_list = [1, 2, 3, 4, 5]
print [elem*2 if elem%2==0 else elem for elem in a_list ]
or, if you have a very long list that you want to modify in place:
a_list = [1, 2, 3, 4, 5]
for i,elem in enumerate(a_list):
if elem%2==0:
a_list[i] = elem*2
so, only the even elements are modified
You could use lambda:
>>> a_list = [1, 2, 3, 4, 5]
>>> f = lambda x: x%2 and x or x*2
>>> a_list = [f(i) for i in a_list]
>>> a_list
[1, 4, 3, 8, 5]
Edit - Thinking about agf's remark I made a 2nd version of my code:
>>> a_list = [1, 2, 3, 4, 5]
>>> f = lambda x: x if x%2 else x*2
>>> a_list = [f(i) for i in a_list]
>>> a_list
[1, 4, 3, 8, 5]
Related
Note: I know there is probably an answer for this on StackOverflow already, I just can't find it.
I need to do this:
>>> lst = [1, 2, 3, 4, 5, 6]
>>> first_two = lst.magic_pop(2)
>>> first_two
[1, 2]
>>> lst
[3, 4, 5, 6]
Now magic_pop doesn't exist, I used it just to show an example of what I need. Is there a method like magic_pop that would help me to do everything in a pythonic way?
Do it in two steps. Use a slice to get the first two elements, then remove that slice from the list.
first_list = lst[:2]
del lst[:2]
If you want a one-liner, you can wrap it in a function.
def splice(lst, start = 0, end = None):
if end is None:
end = len(lst)
partial = lst[start:end]
del lst[start:end]
return partial
first_list = splice(lst, end = 2)
One option would be using slices
def func(lst: list, n: int) -> tuple:
return lst[:n], lst[n:]
lst = [1, 2, 3, 4, 5, 6]
first_two, lst = func(lst, 2)
print(lst)
# [3, 4, 5, 6]
Alternative, here is a kafkaesque approach using builtins:
def func(lst: list, i: int) -> list:
return list(map(lambda _: lst.pop(0), range(i)))
lst = list(range(10))
first_two = func(lst, 2)
print(first_two)
# [0, 1]
print(lst)
# [2, 3, 4, 5, 6, 7, 8, 9]
Try:
lst = [1, 2, 3, 4, 5, 6]
first_two, lst = lst[:2], lst[2:]
print(first_two)
print(lst)
Prints:
[1, 2]
[3, 4, 5, 6]
You can just create a lambda function to do this.
magic_pop = lambda x,y:(x[:y],x[y:]) if y<len(lst) else x
magic_pop(lst,3)
Out[8]: ([1, 2, 3], [4, 5, 6])
The case is if I want to reverse select a python list to n like:
n = 3
l = [1,2,3,4,5,6]
s = l[5:n:-1] # s is [6, 5]
OK, it works, but how can I set n's value to select the whole list?
let's see this example, what I expect the first line is [5, 4, 3, 2, 1]
[40]: for i in range(-1, 5):
...: print(l[4:i:-1])
...:
[]
[5, 4, 3, 2]
[5, 4, 3]
[5, 4]
[5]
[]
if the upper bound n set to 0, the result will lost 0. but if n is -1, the result is empty because -1 means "the last one".
The only way I can do is:
if n < 0:
s = l[5::-1]
else:
s = l[5:n:-1]
a bit confusing.
To fully reverse the list by slicing:
l = [1,2,3,4,5,6]
print(l[::-1])
#[6, 5, 4, 3, 2, 1]
If you want to be able to partially or fully reverse the list based on the value of n, you can do it like this:
l = [1,2,3,4,5,6]
def custom_reverse(l,n):
return [l[i] for i in range(len(l)-1,n,-1)]
print(custom_reverse(l,3)) #[6, 5]
print(custom_reverse(l,-1)) #[6, 5, 4, 3, 2, 1]
Hopefully this is what you mean.
print(l[n+1::-1])
What will be the logic or code of this problem? The reverse() function does not take any argument and list[::-1] is also the same. How to do it in easy way?
Example, given:
list = [1,5,4,3,2,6]
Reversing 5...2, the output will be:
list = [1,2,3,4,5,6]
You could use list slice assignment, which modifies the list in-place:
>>> L = [1, 5, 4, 3, 2, 6]
>>> L[1:5] = L[4:0:-1]
>>> L
[1, 2, 3, 4, 5, 6]
You can reassign the slice of the list to that same slice in reverse
l = [1,5,4,3,2,6]
l[1:5] = l[1:5][::-1]
print(l)
#[1, 2, 3, 4, 5, 6]
You could do something like this:
l = [1,5,4,3,2,6]
index5 = l.index(5)
index2 = l.index(2)
l[index5:index2+1] = l[index2:index5-1:-1]
If you want it to be more explicit, you could use reversed:
l[index5:index2+1] = reversed(l[index5:index2+1])
Edit: Sorry, I misunderstood your question.
>>> l = [1, 5, 4, 3, 2, 6]
>>> temp = l[1:5]
>>> temp.reverse()
>>> l[1:5] = temp
>>> l
>>> [1, 2, 3, 4, 5, 6]
I have a list in python, that consists of both alphabetic and numeric elements, say something like list = ["a", 1, 2, 3, "b", 4, 5, 6] and I want to slice it into 2 lists, containing numbers that follow the alphabetic characters, so list1 = [1, 2, 3] and list2 = [4, 5, 6]. a and b elements could be in reversed order, but generally, I want to store numeric elements that follow a and b elements in separate lists. The easiest solution that I came up with was creating a loop with condition:
#Generating a list for numeric elements following "a":
for e in list[list.index("a")+1:]:
if not str.isdigit(e):
break
else:
list1.append(e)
I'd do it similarly for list2 and numeric elements after "b".
But could there be more elegant solutions? I'm new to Python, but I've seen beautiful one-liner constructions, could there be something like that in my case? Thanks in advance.
Something like this, maybe?
>>> import itertools
>>> import numbers
>>> lst = ["a", 1, 2, 3, "b", 4, 5, 6]
>>> groups = itertools.groupby(lst, key=lambda x: isinstance(x, numbers.Number))
>>> result = [[x for x in group_iter] for is_number, group_iter in groups if is_number]
>>> result
[[1, 2, 3], [4, 5, 6]]
And here is a less “sexy” version that outputs a list of tuple pairs (group_key, group_numbers):
>>> import itertools
>>> import numbers
>>> lst = ["a", 1, 2, 3, "b", 4, 5, 6]
>>> groups = itertools.groupby(lst, key=lambda x: isinstance(x, numbers.Number))
>>> group_key = None
>>> result = []
>>> for is_number, group_iter in groups:
... if not is_number:
... for x in group_iter:
... group_key = x
... else:
... result.append((group_key, [x for x in group_iter]))
>>> result
[('a', [1, 2, 3]), ('b', [4, 5, 6])]
Note that it is a quick and dirty version which expects the input data to be well-formed.
Here you have a functional aproach:
>>> l = ["a", 1, 2, 3, "b", 4, 5, 6]
>>> dig = [x for (x, y) in enumerate(l) if type(y) is str] + [len(l)]
>>> dig
[0, 4, 8]
>>> slices = zip(map(lambda x:x+1, dig), dig[1:])
>>> slices
[(1, 4), (5, 8)]
>>> lists = map(lambda (i, e): l[i:e], slices)
>>> lists
[[1, 2, 3], [4, 5, 6]]
First we get the index of the letters with, notice that we need the size of the list to know the end of it:
[x for (x, y) in enumerate(l) if type(y) is str] + [len(l)]
Then we get the pair of slices where the lists are:
zip(map(lambda x:x+1, dig), dig[1:])
Finally, we get each slice from the original list:
map(lambda (i, e): l[i:e], slices)
You can use slices:
list = ["a", 1, 2, 3, "b", 4, 5, 6]
lista = list[list.index('a')+1:list.index('b')]
listb = list[list.index('b')+1:]
Another approach (Python 3 only):
def chunks(values, idx=0):
''' Yield successive integer values delimited by a character. '''
tmp = []
for idx, val in enumerate(values[1:], idx):
if not isinstance(val, int):
yield from chunks(values[idx + 1:], idx)
break
tmp.append(val)
yield tmp
>>> values = ['a', 1, 2, 3, 'b', 4, 5, 6]
>>> list(chunks(values))
[[4, 5, 6], [1, 2, 3]]
Suppose I have a list x = [1,2,3] and I want to output every other value than the indexed one, is there a list operation I can use?, ie: x[0]=> [2,3], x[1] => [1,3], x[2] =>[1,2] ?
You could use this:
def exclude(l, e):
return [v for i, v in enumerate(l) if i != e]
>>> exclude(range(10), 3)
[0, 1, 2, 4, 5, 6, 7, 8, 9]
You could do it with a slice, but I'd be tempted to try:
a = [1, 2, 3]
b = a[:]
del b[1]
edit
The above does "technically" use a slice operation, which happens to be on list objects in effect a shallow-copy.
What's more flexible and shouldn't have any downsides is to use:
a = [1, 2, 3]
b = list(a)
del b[1]
The list builtin works on any iterable (while the slice operation [:] works on lists or those that are indexable) so that you could even extend it to constructs such as:
>>> a = '123'
>>> b = list(a)
>>> del b[0]
>>> ''.join(b)
'23'
You could use x[:i] + x[i+1:]:
In [8]: x = [1, 2, 3]
In [9]: i = 0
In [10]: x[:i] + x[i+1:]
Out[10]: [2, 3]
Depending on the context, x.pop(i) might also be useful. It modifies the list in place by removing and returning the i-th element. If you don't need the element, del x[i] is also an option.
>>> x = [1, 2, 3]
>>> x[:1] + x[2:]
[1, 3]
my_list = [1, 2, 3]
my_list.remove(my_list[_index_]) #_index_ is the index you want to remove
Such that: my_list.remove(my_list[0]) would yield [2, 3],
my_list.remove(my_list[1]) would yield [0, 3], and
my_list.remove(my_list[2]) would yield [1, 2].
So you want every value except the indexed value:
Where i is the index:
>>> list = [1,2,3,4,5,6,7,8]
>>> [x for x in list if not x == list[i]]
This will give you a list with no instances of the i't element, so e.g.:
>>> i = 2
>>> list = [1,2,3,4,5,6,7,1,2,3,4,5,6,7]
>>> [x for x in list if not x == list[i]]
[1, 2, 4, 5, 6, 7, 1, 2, 4, 5, 6, 7]
note how 3 is not in that list at all.
Every other is
x[::2], start at 0
x[1::2], start at 1