Remove element in list using list comprehension - Python - python

I have a list like this:
['A','B','C']
What I need is to remove one element based on the input I got in the function. For example, if I decide to remove A it should return:
['B','C']
I tried with no success
list = ['A','B','C']
[var for var in list if list[var] != 'A']
How can I do it?
Thanks

Simple lst.remove('A') will work:
>>> lst = ['A','B','C']
>>> lst.remove('A')
['B', 'C']
However, one call to .remove only removes the first occurrence of 'A' in a list. To remove all 'A' values you can use a loop:
for x in range(lst.count('A')):
lst.remove('A')
If you insist on using list comprehension you can use
>>> [x for x in lst if x != 'A']
['B', 'C']
The above will remove all elements equal to 'A'.

The improvement to your code (which is almost correct) would be:
list = ['A','B','C']
[var for var in list if var != 'A']
However, #frostnational's approach is better for single values.
If you are going to have a list of values to disallow, you can do that as:
list = ['A','B','C', 'D']
not_allowed = ['A', 'B']
[var for var in list if var not in not_allowed]

If you not sure whether the element exists or not, you might want to check before you delete:
if 'A' in lst:
lst.remove('A')

Find this simplified code:
list1 = [12,24,35,24,88,120,155]
while 24 in list1:
list1.remove(24)
print(list1)
Best Luck!

originalList=['A','B','C']
print([val for val in originalList if val!='A'])
This prints
['B', 'C']

You can just use the remove method of list. Just do list.remove('A') and it will be removed.
If you have the index of the item to be removed, use the pop method. list.pop(0).

You were really close:
list = ['A','B','C']
[var for var in list if list[list.index(var)] != 'A']
You tried to refer to a list item using a syntax that calls for an index value.

Related

How to split up elements of a list separated by ],[ in Python

I have a list that looks like :
mylist=[[["A","B"],["A","C","B"]],[["A","D"]]]
and I want to return :
mylist=[["A","B"],["A","C","B"],["A","D"]]
Using the split() function returns an error of :
list object has no attribute split
Therefore, I am unsure how I should split the elements of this list.
Thanks!
I am not sure why you think splitting will do any good for you; after all, you are -- if anything -- merging the second layer lists. But flattenening by one level can be done by a comprehension:
mylist = [inner for outer in mylist for inner in outer]
# [['A', 'B'], ['A', 'C', 'B'], ['A', 'D']]
One util to (maybe a matter of taste) simplify this is itertools.chain:
from itertools import chain
mylist = list(chain(*mylist))
Use for-loop in order to do this.
Here is an example code:
output = []
for list_element in my_list:
for single_list in list_element:
output.append(single_list)

Reorder the position of the string in the list based on similarity

I have a list of string like this -
list = ["A","V","C,"D",X","Y","V_RT","D_RT"]
I want to reorder the strings with suffix "_RT" right after the parent string(string without the suffix).
For example, the above string should become something like this -
list = ["A","V","V_RT","C,"D","D_RT",X","Y"] #notice how the strings with _RT moved after the string without _RT.
My approach-
Right now I am finding the strings with _RT, then searching the index of the parent string without _RT and then inserting it there. Finally, deleting the original prefixed string.
The above approach works but I believe there must be some short(one-two liner way) of doing the required which I don't know.
Please help.
Thanks.
EDIT
I forgot to mention but can't change the order of appearance. After "A", there will be "V" then "V_RT", "C", "D", "D_RT", etc. The strings are not necessarily of length 1. The above is just an example.
Another approach using for-loop
Check if current element + '_RT' is in original list
if True add current element and current element + '_RT' to the new list
if False and also if substring '_RT' is not in current element add the element to the new list
Code:
l = ["A","V","C","D","X","Y","V_RT","D_RT"]
l2 = []
for x in l:
if x+'_RT' in l:
l2+=[x, x+'_RT']
elif '_RT' not in x:
l2.append(x)
print(l2)
Output:
['A', 'V', 'V_RT', 'C', 'D', 'D_RT', 'X', 'Y']
This does it
list1 = ["A","V","C" ,"D","X","Y","V_RT","D_RT"]
dict1={}
for x in list1:
dict1[x[0]]=x
list2=[]
for key,value in dict1.items():
list2.append(key)
if key!=value:
list2.append(value)
print(list2)

Returning semi-unique values from a list

Not sure how else to word this, but say I have a list containing the following sequence:
[a,a,a,b,b,b,a,a,a]
and I would like to return:
[a,b,a]
How would one do this in principle?
You can use itertools.groupby, this groups consecutive same elements in the same group and return an iterator of key value pairs where the key is the unique element you are looking for:
from itertools import groupby
[k for k, _ in groupby(lst)]
# ['a', 'b', 'a']
lst = ['a','a','a','b','b','b','a','a','a']
Psidoms way is a lot better, but I may as well write this so you can see how it'd be possible just using basic loops and statements. It's always good to figure out what steps you'd need to take for any problem, as it usually makes coding the simple things a bit easier :)
original = ['a','a','a','b','b','b','a','a','a']
new = [original[0]]
for letter in original[1:]:
if letter != new[-1]:
new.append(letter)
Basically it will append a letter if the previous letter is something different.
Using list comprehension:
original = ['a','a','a','b','b','b','a','a','a']
packed = [original[i] for i in range(len(original)) if i == 0 or original[i] != original[i-1]]
print(packed) # > ['a', 'b', 'a']
Similarly (thanks to pylang) you can use enumerate instead of range:
[ x for i,x in enumerate(original) if i == 0 or x != original[i-1] ]
more_itertools has an implementation of the unique_justseen recipe from itertools:
import more_itertools as mit
list(mit.unique_justseen(["a","a","a","b","b","b","a","a","a"]))
# ['a', 'b', 'a']

having trouble with lists in python

The function satisfiesF() takes a list L of strings as a paramenter. function f takes a string as a parameter returns true or false. Function satisfiesF() modifies L to contain only those strings,s for which f(s) returns true.
I have two different programs aimed to produce the same output. But I am getting different outputs.
First program:
def f(s):
return 'a' in s
def satisfiesF(L):
k=[]
for i in L:
if f(i)==True:
k.append(i)
L=k
print L
print
return len(L)
L = ['a', 'b', 'a']
print satisfiesF(L)
print L
Output:
['a', 'a']
2
['a', 'b', 'a']
Second program:
def f(s):
return 'a' in s
def satisfiesF(L):
for i in L:
if f(i)==False:
L.remove(i)
print L
print
return len(L)
L = ['a', 'b', 'a']
print satisfiesF(L)
print L
output:
['a', 'a']
2
['a', 'a']
Please explain why these are giving differnt outputs.
In your second function you are seeing 2 as the length and all the elements in L outside the function because you are setting a local variableL which is a reference to k, your L created outside the function is not affected. To see the change in L you would need to use L[:] = k, then printing L will give you ['a', 'a'] outside the function as you are changing the original list object L list passed in to the function.
In the first you are directly modifying L so you see the changes in L outside the function.
Also never iterate over a list you are removing element from, if you make
L = ['a', 'b', 'a','a','d','e','a'], you will get behaviour you won't expect. Either make a copy for i in L[:] or use reversed for i in reversed(L):
In the first function, you assign over L in satisfiesF(), but you never modify the original list. When you write L=k, that makes the reference L now refer to the same list as k. It doesn't assign to the original list.
In contrast, in the second function you modify L without reassigning to it.
Also, as a side note, you shouldn't modify a list while you iterate over it.
As a second side note, you can rewrite satisfiesF as a one-liner:
L = [item for item in L if f(item)]
This was down voted mistakenly. The question was changed. So, the answer got outdated. Following is the answer for changed question:
L=k
Above would mean that we lost the reference to L.
So, Try this:
To the 1st program, comment the above assignment, do below, to retain reference to L:
# L=k
del L[:]
L[:] = k
Now both programs will output same, below one:
['a', 'a']
2
['a', 'a']
Best of luck.
In the question, there are two Ls. A global one and a local one. The
print L
statement prints the GLOBAL L, which you did not mutate in the programme.
Therefore, in order to let the programme knows that you want to mutate the global L, instead of the local L, you can add the line
globals()['L'] = L
to your first programme. I hope this can help!
In the first program, if you want to mutate the original list L and see the change made by your function, you should replace L = K in your code with L[:] = k:
def satisfiesF(L):
k=[]
for i in L:
if f(i)==True:
k.append(i)
# new code --------
L[:] = k # before: L = K
# -----------------
print L
print
return len(L)
This will give you ['a', 'a'] outside the function.
About mutating a list within a loop in the second program...
Just to remember that during a "for" loop, python keeps track of where it is in the list using an internal counter that is incremented at the end of each iteration.
When the value of the counter reaches the current length of the list, the loop terminates. This means that if you are mutating the list within the loop you can have surprising consequence.
For example, look at the for loop below:
my_list = ['a', 'b', 'c', 'd']
>>>print "my_list - before loop: ", my_list
my_list - before loop: ['a', 'b', 'c', 'd']
for char in my_list:
if char == 'a' or char == 'b':
my_list.remove(char)
>>>print "my_list - after loop: ", my_list
my_list - after loop: ['b', 'c', 'd']
Here, the hidden counter starts out at the index 0, discovers that "a" (in my_list[0]) is in the list, and remove it, reducing the length of my_list to 3 (from the initial 4). The counter is then incremented to 1, and the code proceeds to check the "if" condition, at the position my_list[1], in the mutated list (now of len == 3). This means that you will skip the "b" character (present now at the index 0) even if it had to be remove it.
One solution for this is to use slicing to clone and create a new list where you can remove items from it:
cloneList = my_list[:]

Python - List of Lists Slicing Behavior

When I define a list and try to change a single item like this:
list_of_lists = [['a', 'a', 'a'], ['a', 'a', 'a'], ['a', 'a', 'a']]
list_of_lists[1][1] = 'b'
for row in list_of_lists:
print row
It works as intended. But when I try to use list comprehension to create the list:
row = ['a' for range in xrange(3)]
list_of_lists = [row for range in xrange(3)]
list_of_lists[1][1] = 'b'
for row in list_of_lists:
print row
It results in an entire column of items in the list being changed. Why is this? How can I achieve the desired effect with list comprehension?
Think about if you do this:
>>> row = ['a' for range in xrange(3)]
>>> row2 = row
>>> row2[0] = 'b'
>>> row
['b', 'a', 'a']
This happens because row and row2 are two different names for the same list (you have row is row2) - your example with nested lists only obscures this a little.
To make them different lists, you can cause it to re-run the list-creation code each time instead of doing a variable assignment:
list_of_lists = [['a' for range in xrange(3)] for _ in xrange(3)]
or, create a new list each time by using a slice of the full old list:
list_of_lists = [row[:] for range in xrange(3)]
Although this isn't guaranteed to work in general for all sequences - it just happens that list slicing makes a new list for the slice. This doesn't happen for, eg, numpy arrays - a slice in those is a view of part of the array rather than a copy. If you need to work more generally than just lists, use the copy module:
from copy import copy
list_of_lists = [copy(row) for range in xrange(3)]
Also, note that range isn't the best name for a variable, since it shadows the builtin - for a throwaway like this, _ is reasonably common.
This happens because most objects in python (exept for strings and numbers) get passed reference (not exactly by reference, but here you have a better explanation) so when you try to do it in the "list comprehensive" way, yo get a list of 3 references to the same list (the one you called "row"). So when you change the value of one row you see that change in all of them)
So what you have to do is to change your "matrix" creation like this:
list_of_lists = [list(row) for range in xrange(3)]
Here you have some ideas on how to correctly get a copy of a list. Depending on what you are trying to do, you may use one or another...
Hope it helps!
Copy the list instead of just the reference.
list_of_lists = [row[:] for range in xrange(3)]

Categories

Resources