I have a 2D Array in Python that is set up like
MY_ARRAY = [
['URL1', "ABC"],
['URL2'],
['URL3'],
['URL4', "ABC"]
]
I want to make an array of first element of each array only if the 2nd parameter is "ABC". So the result of the above example would be ['URL1', 'URL4']
I tried to do [x[0] for x in MY_ARRAY if x[1] == 'ABC'] but this returns IndexError: list index out of range. I think this is because sometimes x[1] is nonexistant.
I am looking for this to be a simple one-liner.
You could simply add a length check to the filtering criteria first. This works because Python short-circuits boolean expressions.
[ele[0] for ele in MY_ARRAY if len(ele) > 1 and ele[1] == 'ABC']
Also note that the proper terminology here is a list of lists, not an array.
I think you should try doing this:
if len(x) > 1:
if x[1] == 'ABC':
#do something here
This is a work around, but you can try it on one-liner code using:
if len(x) > 1 and x[1] == "ABC"
Cheers!
Try this
[x[0] for x in MY_ARRAY if len(x) > 1 and x[1] == 'ABC']
It is happening because you have two list are having only one item and you are trying to access second item from that list
First, let me tell you are on the right track, when x[1] doesn't exist you get the error
But, it's a bad habit to insist on doing things as a one-liner if it complicates matters.
Having said that, here's a one-liner that does that:
NEW_ARRAY = [x[0] for x in MY_ARRAY if len(x)>1 and x[1]=='ABC']
I found a simple solution that works for my usage.
[x[0] for x in MY_ARRAY if x[-1] == 'ABC']
x[-1] will always exist since thats the last element in the array/list.
Related
I want delete some elements of one list equal to a value:
I can do it :
List =[1,2,3.....]
List = [x for x in List if x != 2]
How can i save the indexs of the deleted elements ?
I want to use this index to delete elements of another list.
Simplest solution is to make a list of indices to keep, then use that to strip the elements from both of your lists. itertools provides a handy compress utility to apply the indices to keep quickly:
from itertools import compress
tokeep = [x != 2 for x in List]
List = list(compress(List, tokeep))
otherlist = list(compress(otherlist, tokeep))
Alternatively (and frankly more clearly) you can just use one loop to strip both inputs; listcomps are fun, but sometimes they're not the way to go.
newlist = []
newotherlist = []
for x, y in zip(List, otherlist):
if x != 2:
newlist.append(x)
newotherlist.append(y)
which gets the same effect in a single pass. Even if it does feel less overtly clever, it's very clear, which is a good thing; brevity for the sake of brevity that creates complexity is not a win.
And now, to contradict that last paragraph, the amusingly overtly clever and brief solution to one-line this:
List, otherlist = map(list, zip(*[(x, y) for x, y in zip(List, otherlist) if x != 2]))
For the love of sanity, please don't actually use this, I just had to write it for funsies.
You can also leverage enumerate
for index, val in enumerate(List):
if val == value:
del List[index]
break
print(index)
Based on documentation
list_first = ['d', 'a']
list_second = ['x', 'z']
def remove_from_lists(element):
index_deleted = list_first.index(element)
list_first.remove(element)
list_second.pop(index_deleted)
remove_from_lists('d')
I have a list with lists and I would like remove a wildcard matching item from each list if present, otherwise return it as it is.
Example
nested_list = [["abc","fds","gfssdf"],["dfsdf","cds","dvc"],["dsaf","abcvs","ewq"],...]
What I tried to do is:
for x in nested_list :
for y in x:
if re.search('abc.+', y) in x:
nested_list.remove(x)
However it returns the same list, without any changes
My desirable output would be:
nested_list = [["fds","gfssdf"],["dfsdf","cds","dvc"],["dsaf","ewq"],...]
Is there a solution?
Here is one way to do this with a nested 2D list comprehension:
nested_list = [["abc","fds","gfssdf"],["dfsdf","cds","dvc"],["dsaf","abcvs","ewq"]]
output = [[y for y in x if not re.search(r'^abc', y)] for x in nested_list]
print(output) # [['fds', 'gfssdf'], ['dfsdf', 'cds', 'dvc'], ['dsaf', 'ewq']]
You could also do this using startswith instead of re:
>>> [[y for y in x if not y.startswith("abc")] for x in nested_list]
[['fds', 'gfssdf'], ['dfsdf', 'cds', 'dvc'], ['dsaf', 'ewq']]
The other answers are providing a nice solution, but I wanted to answer OP's original question for learning purposes
There are some mistakes in your code, I'll adress them one by one:
if re.search('abc.+', y) in x:
re.search returns None if it's not found, so you can remove the in x
The + in abc.+ searched for 1 or more, since you want to match abc, change the + to a ? to match 0 or more
If you'd remove all the elements from an deeper list, you'll end op with a empty list, so lets add a check for that and remove the empty list:
if not x:
nested_list.remove(x)
Applying those fixes gives us:
import re
nested_list = [["abc","fds","gfssdf"],["dfsdf","cds","dvc"],["dsaf","abcvs","ewq"], ["abc"]]
for x in nested_list :
for y in x:
if re.search('abc.?', y):
x.remove(y)
if not x:
nested_list.remove(x)
print(nested_list)
Witch gives the expected output:
[['fds', 'gfssdf'], ['dfsdf', 'cds', 'dvc'], ['dsaf', 'ewq']]
As you can test in this online demo.
I'm not sure if the title is a great explanation of what I'm trying to do so here is an example:
Array = ['orangutan', 'sun', 'tan']
I want to print:
'orangutan', 'sun'
I've tried looping through the array and looking to see if one item contains another item as followed:
for i in Array:
remove_duplicates = [x for x in Array if x in i]
But this hasn't worked.
Any help would be greatly appreciated, thanks!
Here is a possible solution:
result = [x
for i, x in enumerate(Array)
if all(x not in y for j, y in enumerate(Array) if i != j)]
You can do:
lst = ['orangutan', 'sun', 'tan']
print([x for i, x in enumerate(lst)
if not any(x in y for j, y in enumerate(lst) if i != j)])
Update:
It won't escape notice that I have arrived at essentially the same solution as Riccardo Bucco. But the tests are different here. For the equivalence of these two tests, see De Morgan's laws. The any (i.e. a reduction of or over the boolean inputs) gives the inverse of all (i.e. reduction of and) on the inverse of those inputs -- hence the not any(... in ...) is equivalent to all(... not in ...).
In either case, iteration will stop at the first match: all will stop iterating and return False if it sees a false-like value, and any will stop iterating and return True if it sees a true-like value.
I assume that you have another array with the strings that you want to keep.
If this is the case, you can loop and use .pop():
array=['orangutan', 'sun', 'tan']
other_array=['orangutan', 'sun']
for i in range(0,len(array)):
if array[i] not in other_array:
array.pop(i)
print(array)
array=['orangutan', 'sun']
The remove() method removes the first matching element (which is passed as an argument) from the list.
I've tried looping through the array and looking to see if one item contains another item as followed.
you were almost there:
list_ = []
Array = ['orangutan', 'sun', 'tan']
for i in Array:
list_.extend([x for x in Array if x in i and x != i])
print(list_)
output:
['tan']
I'm using Python 2.7. I have a list of objects that each include a property called vote that can have values of either yes or no, and I want to count the number of yes and no votes. One way to do this is:
list = [ {vote:'yes'}, {vote:'no'}, {vote:'yes'} ] #...
numYesVotes = len([x for x in list if x.vote=='yes'])
numNoVotes = len([x for x in list if x.vote=='no'])
but it seems horribly wasteful/inefficient to me to build these lists only to get their lengths and
First question: Am I right about that? Wouldn't it a good bit more efficient to simply loop through the list once and increment counter variables? i.e:
numYesVotes = numNoVotes = 0;
for x in list:
if x.vote == 'yes':
numYesVotes++
else:
numNoVotes++
or is there something about list comprehensions that would make them more efficient here?
Second question: I'm just learning to use lambdas, and I have a feeling this is a perfect use case for one - but I can't quite figure out how to use one here how might I do that?
See Counter
Counter(x.vote for x in mylst)
Edit:
Example:
yn = Counter("yes" if x%2 else "no" for x in range(10))
print(yn["yes"],yn["no"])
Note that it is faster to do:
sum(1 for x in L if x.vote=='yes')
Than:
len([x for x in L if x.vote=='yes'])
As no list has to be created.
The lambda form;
t =lambda lst: len([ v for x in lst for k,v in x.items() if v=='yes'])
#print (t(mylst))
mylst is the list that you want to check how much yes or no in that. Use key-dict methods.
Demo;
mylst = [ {"vote":'yes'}, {"vote":'no'}, {"vote":'yes'} ]
t =lambda lst: len([ v for x in lst for k,v in x.items() if v=='yes'])
print (t(mylst))
>>>
2
>>>
I probably didn't ask correctly: I would like a list value that can match any list: the "inverse" of (None,)
but even with (None,) it will match item as None (which I don't want)
The point is I have a function working with: [x for x in my_list if x[field] not in filter_list]
and I would like to filter everything or nothing without making tests like:
if filter_list==(None,): return [] and if filter_list==('*',): return my_list
PS: I wanted to simplify my question leading to some errors (list identifier) or stupid thing [x for x in x] ;)
Hi,
I need to do some filtering with list comprehension in python.
if I do something like that:
[x for x in list if x in (None,)]
I get rid of all values, which is fine
but I would like to have the same thing to match everything
I can do something like:
[x for x in list if x not in (None,)]
but it won't be homogeneous with the rest
I tried some things but for example (True,) matches only 1
Note than the values to filter are numeric, but if you have something generic (like (None,) to match nothing), it would be great
Thanks
Louis
__contains__ is the magic method that checks if something is in a sequence:
class everything(object):
def __contains__(self, _):
return True
for x in (1,2,3):
print x in everything()
The better syntax would be:
[x for x in lst if x is None]
[x for x in lst if x is not None]
What do you mean by
I would like to have the same thing to match everything
Just do
[x for x in list]
and every item in list is matched.
You could change your program to accept a filter object, instead of a list.
The abstract base filter would have a matches method, that returns true if x *matches".
Your general case filters would be constructed with a list argument, and would filter on membership of the list - the matches function would search the list and return true if the argument was in the list.
You could also have two special subclasses of the filter object : none and all.
These would have special match functions which either always return true (all) or false (none).
You don't need an if, you can just say
[x for x in list]
but I would like to have the same
thing to match everything
To match everything, you don't need if statement
[x for x in list1]
or If you really like to do
[x for x in list1 if x in [x]]
Answering your revised question: the list that "matches" all possible values is effectively of infinite length. So you can't do what you want to do without an if test. I suggest that your arg should be either a list or one of two values representing the "all" and "none" cases:
FILTER_NONE = object() # or []
FILTER_ALL = object()
def filter_func(alist, filter_list):
if filter_list is FILTER_ALL:
return []
elif filter_list is FILTER_NONE:
return alist
# or maybe alist[:] # copy the list
return [x for x in alist if x not in filter_list]
If filter_list is large, you may wish the replace the last line by:
filter_set = set(filter_list)
return [x for x in alist if x not in filter_set]
Alternatively, don't bother; just document that filter_list (renamed as filter_collection) can be anything that supports __contains__() and remind readers that sets will be faster than lists.