This question already has answers here:
A quick way to return list without a specific element in Python
(9 answers)
Closed 5 months ago.
Just noticed that there is no function in Python to remove an item in a list by index, to be used while chaining.
For instance, I am looking for something like this:
another_list = list_of_items.remove[item-index]
instead of
del list_of_items[item_index]
Since, remove(item_in_list) returns the list after removing the item_in_list; I wonder why a similar function for index is left out. It seems very obvious and trivial to have been included, feels there is a reason to skip it.
Any thoughts on why such a function is unavailable?
----- EDIT -------
list_of_items.pop(item_at_index) is not suitable as it doesn't return the list without the specific item to remove, hence can't be used to chain. (As per the Docs: L.pop([index]) -> item -- remove and return item at index)
Here's a nice Pythonic way to do it using list comprehensions and enumerate (note that enumerate is zero-indexed):
>>> y = [3,4,5,6]
>>> [x for i, x in enumerate(y) if i != 1] # remove the second element
[3, 5, 6]
The advantage of this approach is that you can do several things at once:
>>> # remove the first and second elements
>>> [x for i, x in enumerate(y) if i != 0 and i != 1]
[5, 6]
>>> # remove the first element and all instances of 6
>>> [x for i, x in enumerate(y) if i != 0 and x != 6]
[4, 5]
Use list.pop:
>>> a = [1,2,3,4]
>>> a.pop(2)
3
>>> a
[1, 2, 4]
According to the documentation:
s.pop([i])
same as x = s[i]; del s[i]; return x
UPDATE
For chaining, you can use following trick. (using temporary sequence that contains the original list):
>>> a = [1,2,3,4]
>>> [a.pop(2), a][1] # Remove the 3rd element of a and 'return' a
[1, 2, 4]
>>> a # Notice that a is changed
[1, 2, 4]
To get the result of removing (i.e, a new list, not in-place) a single item by index, there is no reason to use enumerate or a list comprehension or any other manual, explicit iteration.
Instead, simply slice the list before and after, and put those pieces together. Thus:
def skipping(a_list, index):
return a_list[:index] + a_list[index+1:]
Let's test it:
>>> test = list('example')
>>> skipping(test, 0)
['x', 'a', 'm', 'p', 'l', 'e']
>>> skipping(test, 4)
['e', 'x', 'a', 'm', 'l', 'e']
>>> skipping(test, 6)
['e', 'x', 'a', 'm', 'p', 'l']
>>> skipping(test, 7)
['e', 'x', 'a', 'm', 'p', 'l', 'e']
>>> test
['e', 'x', 'a', 'm', 'p', 'l', 'e']
Notice that it does not complain about an out-of-bounds index, because slicing doesn't in general; this must be detected explicitly if you want an exception to be raised. If we want negative indices to work per Python's usual indexing rules, we also have to handle them specially, or at least -1 (it is left as an exercise to understand why).
Fixing those issues:
def skipping(a_list, index):
count = len(a_list)
if index < 0:
index += count
if not 0 <= index < count:
raise ValueError
return a_list[:index] + a_list[index+1:]
As Martijn Pieters noted in comments to the question, this is not implemented as: Python in-place operations, as a rule, return None, never the altered object.
Related
This question already has answers here:
Flatten an irregular (arbitrarily nested) list of lists
(51 answers)
Closed 1 year ago.
I have the following list:
my_list=[[['A','B'],'C'],[['S','A'],'Q']]
How I can remove the bracket from the first two elements?
output:
my_list=[['A','B','C'],['S','A','Q']]
Slice it in?
for a in my_list:
a[:1] = a[0]
This produces the desired output:
[x[0]+[x[1]] for x in my_list]
my_list=[
[['A','B'],'C'],
[['S','A'],'Q'],
]
result = [[item1, item2, item3] for (item1, item2), item3 in my_list]
>>> result
[['A', 'B', 'C'], ['S', 'A', 'Q']]
You can use the flattening solution in Cristian's answer on Flatten an irregular list of lists.
>>> [list(flatten(x)) for x in my_list]
[['A', 'B', 'C'], ['S', 'A', 'Q']]
Other people have already given the oneline solutions, but let's take a step back and think about how to approach this problem, so you can solve it for yourself.
The tools you need:
for [element] in list: Iterate over each element in a list
list[i]: Get the ith element of a list
list.append(element): Add an element to a list
Let's start with the simple case. We have [['A','B'],'C'] and want ['A','B','C'].
We want to
Get the sublist ['A', 'B']
Get the single item that isn't in the sublist, 'C'
Add the single item into the sublist
Make the sublist the main list
Sometimes it's easiest to sketch these things out in the python shell:
>>> l = [['A','B'],'C']
>>> l[0]
['A', 'B']
>>> l[1]
'C'
>>> l[0].append(l[1])
>>> l = l[0]
>>> l
['A', 'B', 'C']
Now, we can build up a function to do this
def fix_single_element(element):
"""
Given a list in the format [['A','B'],'C']
returns a list in the format ['A','B','C']
"""
# We use copy since we don't want to mess up the old list
internal_list = element[0].copy()
last_value = element[1]
internal_list.append(last_value)
return internal_list
Now we can use that:
>>> for sublist in my_list:
... print(sublist)
...
[['A', 'B'], 'C']
[['S', 'A'], 'Q']
Note that the sublists are exactly the problem we just solved.
>>> new_list = []
>>> for sublist in my_list:
... new_list.append(fix_single_element(sublist))
...
>>> new_list
[['A', 'B', 'C'], ['S', 'A', 'Q']]
There are LOTS of ways to do any particular task, and this is probably not the "best" way, but it's a way that will work. Focus on writing code you understand and can change, not the shortest code, especially when you start.
Trying to implement and form a very simple algorithm. This algorithm takes in a sequence of letters or numbers. It first creates an array (list) out of each character or digit. Then it checks each individual character compared with the following character in the sequence. If the two are equal, it removes the character from the array.
For example the input: 12223344112233 or AAAABBBCCCDDAAABB
And the output should be: 1234123 or ABCDAB
I believe the issue stems from the fact I created a counter and increment each loop. I use this counter for my comparison using the counter as an index marker in the array. Although, each time I remove an item from the array it changes the index while the counter increases.
Here is the code I have:
def sort(i):
iter = list(i)
counter = 0
for item in iter:
if item == iter[counter + 1]:
del iter[counter]
counter = counter + 1
return iter
You're iterating over the same list that you are deleting from. That usually causes behaviour that you would not expect. Make a copy of the list & iterate over that.
However, there is a simpler solution: Use itertools.groupby
import itertools
def sort(i):
return [x for x, _ in itertools.groupby(list(i))]
print(sort('12223344112233'))
Output:
['1', '2', '3', '4', '1', '2', '3']
A few alternatives, all using s = 'AAAABBBCCCDDAAABB' as setup:
>>> import re
>>> re.sub(r'(.)\1+', r'\1', s)
'ABCDAB'
>>> p = None
>>> [c for c in s if p != (p := c)]
['A', 'B', 'C', 'D', 'A', 'B']
>>> [c for c, p in zip(s, [None] + list(s)) if c != p]
['A', 'B', 'C', 'D', 'A', 'B']
>>> [c for i, c in enumerate(s) if not s.endswith(c, None, i)]
['A', 'B', 'C', 'D', 'A', 'B']
The other answers a good. This one iterates over the list in reverse to prevent skipping items, and uses the look ahead type algorithm OP described. Quick note OP this really isn't a sorting algorithm.
def sort(input_str: str) -> str:
as_list = list(input_str)
for idx in range(len(as_list), 0, -1)):
if item == as_list[idx-1]:
del as_list[idx]
return ''.join(as_list)
I am searching through a list like this:
my_list = [['a','b'],['b','c'],['a','x'],['f','r']]
and I want to see which elements come with 'a'. So first I have to find lists in which 'a' occurs. Then get access to the other element of the list. I do this by abs(pair.index('a')-1)
for pair in my_list:
if 'a' in pair:
print( pair[abs(pair.index('a')-1)] )
Is there any better pythonic way to do that?
Something like: pair.index(not 'a') maybe?
UPDATE:
Maybe it is good to point out that 'a' is not necessarily the first element.
in my case, ['a','a'] doesn't happen, but generally maybe it's good to choose a solution which handles this situation too
Are you looking for elements that accompany a? If so, a simple list comprehension will do:
In [110]: [x for x in my_list if 'a' in x]
Out[110]: [['a', 'b'], ['a', 'x']]
If you just want the elements and not the pairs, how about getting rid of a before printing:
In [112]: [(set(x) - {'a'}).pop() for x in my_list if 'a' in x]
Out[112]: ['b', 'x']
I use a set because a could either be the first or second element in the pair.
If I understand your question correctly, the following should work:
my_list = filter(
lambda e: 'a' not in e,
my_list
)
Note that in python 3, this returns a filter object instance. You may want to wrap the code in a list() command to get a list instance instead.
That technique works ok here, but it may be more efficient, and slightly more readable, to do it using sets. Here's one way to do that.
def paired_with(seq, ch):
chset = set(ch)
return [(set(pair) - chset).pop() for pair in seq if ch in pair]
my_list = [['a','b'], ['b','c'], ['x','a'], ['f','r']]
print(paired_with(my_list, 'a'))
output
['b', 'x']
If you want to do lots of tests on the same list, it would be more efficient to build a list of sets.
def paired_with(seq, ch):
chset = set(ch)
return [(pair - chset).pop() for pair in seq if ch in pair]
my_list = [['a','b'], ['b','c'], ['x','a'], ['f','r']]
my_sets = [set(u) for u in my_list]
print(my_sets)
print(paired_with(my_sets, 'a'))
output
[{'b', 'a'}, {'c', 'b'}, {'x', 'a'}, {'r', 'f'}]
['b', 'x']
This will fail if there's a pair like ['a', 'a'], but we can easily fix that:
def paired_with(seq, ch):
chset = set(ch)
return [(pair - chset or chset).pop() for pair in seq if ch in pair]
my_list = [['a','b'], ['b','c'], ['x','a'], ['f','r'], ['a', 'a']]
my_sets = [set(u) for u in my_list]
print(paired_with(my_sets, 'a'))
output
['b', 'x', 'a']
How would I unappend these appended variable:
if letterchoice == wordchoice[0]:
wc = []
for each in wordchoice:
wc.append(each)
wh = []
for each in wordhidden:
wh.append(each)
wh[0] = wc[0]
This is for part of a hangman program(I understand that this is probably not the most efficient way of doing this but I am new to python and wanted to give myself a challenge)
There are several ways you can remove item from a list, you can delete by the value or by the index:
>>> foo = [1,2,3,4]
>>> foo.pop(1)
2
>>> foo
[1, 3, 4]
>>> foo.remove(1)
>>> foo
[3, 4]
>>> del foo[1]
>>> foo
[3]
>>>
Nevertheless, I suggest to use .pop().
Edit: Based on your comment, is this what you want to do?
>>> foo = ['h', 'm', 'f', 'd', 's']
>>> foo
['h', 'm', 'f', 'd', 's']
>>> ''.join(foo)
'hmfds'
>>>
Use the python list pop() method, which removes and returns last object or obj from the list i.e.
wh.pop()
To avoid popping from an empty list, consider checking first:
if wh: # Makes sure there is something to pop()
wh.pop()
Also, you may want to "catch" the popped entry and avoid it from being printed to the terminal, as in:
if wh:
x = wh.pop()
How I make multiline list comprehension in Python?
Something like:
[
x for x in a:
if check(x):
....
#something multiline here
....
else:
....
#something multiline here
....
]
Of course I know that I can to something like:
def f(x):
if check(x):
....
else:
....
return x
map(a,f)
But I want it without additional functions.
Is it possible?
My understanding is that you can not do it as you suggested.
It's far more readable when you name the function and use list comprehension or map function compared to what you have suggested
You can't, in the way you're thinking; list comprehensions can only contain expressions, not statements.
You can have multiline list comprehensions and you can have if statements inside them, but the syntax is different from what you showed. I just tested this in the interpreter:
>>> tuple = ('a','b','c','d','e','f','g','h','i','j')
>>> list = [letter for letter in tuple
... if (letter != 'a' and letter != 'b')]
>>> list
['c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
You could also break it in other way, but the code will be a bit more messy:
>>> list = [letter for letter in
... tuple if (letter != 'j' and letter != 'i')]
>>> list
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
As the other guys pointed out, you might not use an else statement (couldn't make it work here). Besides, if the list comprehension start to grow too much it lose its conciseness (which I think it's strongest point).
Of course you can do multiple nested list comprehensions. I do it all the time. Here is one example:
A=[[[10 for y in range(3)],2][x<=3] for x in range(7)]
print A
It generates a list with 4 times 2 and three sublists with 3 times 10 as elements.
[2, 2, 2, 2, [10, 10, 10], [10, 10, 10], [10, 10, 10]]
As you can see, the outer list comprehension generates the 6 elements of the outer list, and the inner (first) list comprehension generates the inner 10s or the 2s, depending on whether x is smaller or equal to 3.