Return True if item is empty string - python

Please consider this snippet:
>>> i = ["", 1, 2]
>>> all([x for x in i])
False
What would be Pythonic way to make this snippet return True even if item in iterable is empty string?
Standard restrictions should apply - if item is None or False etc, it should return False as expected.

>>> lst = ["", 1, 2]
>>>
>>> all(True if i=='' else i for i in lst)
True
If you want True if there is at least one item that evaluates to True use any().
>>> any(lst)
True
Note that in general any() and all() accept iterable argument and you don't need to loop over them.

This option looks nice to me.
all(x or x=="" for x in i)

all([x for x in i if not isinstance(x, str)])
The only falsy string is the empty string, so if you don't want to test for it, then filter out the strings.

You can use all, for instance:
tests = [
[None, False, "", 1, 2],
["", 1, 2],
[1, 2],
[]
]
for i in tests:
print i
print all(filter(lambda x: x != '', i))
print all(True if x == '' else x for x in i)
print '-' * 80

Related

Python insert element based on condition

I'm trying to make a list where based on a condition, an element may or may not exist. For example, if it's true, the list is [1, 2, 3], and otherwise, it's [1, 3]. Currently, what I could do is either initialize the list and call .insert or .append the elements individually, or alternatively, I could do something like [1] + ([2] if condition else []) + [3], but that's ugly.
I was wondering if there was some sort of syntax like [1, 2 if condition, 3] for example, but I can't seem to find anything of that sort. Is there a similar syntax to this?
EDIT My list isn't [1, 2, 3]. I want a general solution for any type of object because I'm not even working with numbers (these are WTForms validators)
I'm also used to doing this in Perl with a pattern like:
my #arr = (1, (condition? (2) : ()), 3);
In Python you can get somewhat close to this with a solution that's pretty close to what you have with the list +, but uses * unpacking to avoid a lot of the other arrays:
arr = [1, *((2,) if condition else ()), 3]
Could clean that up a little with a helper that yields the value if the condition is true.
T = TypeVar("T")
def cond(val: T, ok: bool) -> Iterable[T]:
if ok:
yield val
arr = [1, *cond(2, condition), 3]
This has the downside of not short circuiting though, so if the creation of the used value is expensive you might want to provide a function instead of a value to be called and returned when the condition is true.
Yet another option could be to use a sentinel value and filter for it when constructing the list. Can combine this with a helper to do the filtering.
class _Ignore: pass
IGNORE = _Ignore()
def cond_list(*elements: Union[T, _Ignore]) -> list[T]:
return [e for e in elements if not isinstance(e, _Ignore)]
arr = cond_list(1, 2 if condition else IGNORE, 3)
If you want to stay flexible for the resulting container type you could choose to return an iterable which you can pass to a container constructor:
def cond_iter(*elements: Union[T, _Ignore]) -> Iterable[T]:
yield from (e for e in elements if not isinstance(e, _Ignore))
arr = set(cond_iter(1, 2 if condition else IGNORE, 3))
You can create a list with conditions and use list comprehension:
condition = [True, False, True, True, False]
[i for i in range(len(condition)) if condition[i]]
if you have a list of contidions for all elements you could to this:
elems = ['a', 'b', 'c']
conditions = [True, False, True]
lst = [item for item, condition in zip(elems, conditions) if condition]
print(lst)
that could also be done using itertools.compress:
from itertools import compress
elems = ['a', 'b', 'c']
conditions = [True, False, True]
lst = list(compress(elems, conditions))
or you generate your list and remove the element afterwards:
lst = ['a', 'b', 'c']
if condition:
lst.remove('b')
Is this what you're looking for?
[i for i in range(1,4) if i!=2]
UPDATE: based on nice answer above.
Making condition a function instead of a list will generalize it.
def condition(x):
if(x==2):
return False
else:
return True
[i for i in range(1,4) if condition(i)]
You should consider to generate the list with a generator like:
def generate(condition: bool, value: int):
yield 1
if condition:
yield value
yield 3
l = list(generate(True, 2)
You can use the splat/unpacking operator to expand an element either to the object you want to add or to nothing. Try changing the condition from True to False to check the results.
class FirstClass:
x = 5
class SecondClass:
y = 10
possible_object = (SecondClass().y,) if True else ()
l = [
FirstClass().x,
*possible_object,
FirstClass().x,
*possible_object,
]
print(l)
I think you got a variation of this answer before, but maybe this makes it more readable, like you wanted?
def is_not_dog(item):
return item != "dog"
items = ["cat", "dog", "horse", "snail"]
not_dog = [i for i in items if is_not_dog(i)]
not_dog
['cat', 'horse', 'snail']
Here's an attempt using filter():
conditioned = {2}
condition = True
[i for i in filter(lambda x: condition if x in conditioned else True, [1,2,3,4])]
# [1, 2, 3, 4]
condition = False
[i for i in filter(lambda x: condition if x in conditioned else True, [1,2,3,4])]
[1, 3, 4]
Of course you could replace the boolean condition with a function condition(), and use it as a filter instead of the lambda function.
If I have understood your question correctly, you want to output 1 predefined list if condition is true or return the same list plus an element if condition is false (for example). If that's correct you might want something like this:
a = [1, 2]
my_condition = 5 > 6
print(a if my_condition else a + [3])
Or if you want to define the list in the same line:
my_condition = 5 > 6
print([1,2] if my_condition else [1, 2] + [3])
In these cases 5 is not greater than 6 so the condition is false so [1, 2, 3] will be output.
Edit: just reading your question it looks like you might want a single value then for true + [one or more values] or for false + [one or more values] so this might be better:
a = [1]
print(a + [3] if my_condition else a + [2, 3])
print([1] + [3] if my_condition else [1] + [2, 3])
(Just using prints to demonstrate but you can assign this output to a variable using brackets)
I hope this answers your question.

The best way to check if all elements of a list matches a condition in for loop?

My question is quite similar to How to check if all elements of a list matches a condition.
But I couldn't find a right way to do the same thing in a for loop.
For example, using all in python is like:
>>> items = [[1, 2, 0], [1, 0, 1], [1, 2, 0]]
>>> all(item[2] == 0 for item in items)
False
But when I want to use the similar method to check all elements in a for loop like this
>>> for item in items:
>>> if item[2] == 0:
>>> do sth
>>> elif all(item[1] != 0)
>>> do sth
The "all" expression cannot be used in here. Is there any possible way like "elif all(item[2] == 0)" to be used here. And how to check if all elements in list match a condition in a for loop?
If you want to have an if and an else, you can still use the any method:
if any(item[2] == 0 for item in items):
print('There is an item with item[2] == 0')
else:
print('There is no item with item[2] == 0')
The any comes from this answer.
Here:
items = [[1, 2, 0], [1, 0, 1], [1, 2, 0]]
def check_list(items):
for item in items:
if item[2] != 0:
return False
return True
print(check_list(items))
If you want to make it a bit more generic:
def my_all(enumerable, condition):
for item in enumerable:
if not condition(item):
return False
return True
print(my_all(items, lambda x: x[2]==0)
Try This:-
prinBool = True
for item in items:
if item[2] != 0:
prinBool = False
break
print prinBool
You could use a for loop with the else clause:
for item in items:
if item[2] != 0:
print False
break
else:
print True
The statements after else are executed when the items of a sequence are exhausted, i.e. when the loop wasn't terminated by a break.
With functools, it will be easier :
from functools import reduce
items = [[1, 2, 0], [1, 0, 1], [1, 2, 0]]
f = lambda y,x : y and x[2] == 0
reduce(f,items)
Do you mean something like this?
for item in items:
for x in range (0,3):
if item[x] == 0:
print "True"

How to achieve python's any() with a custom predicate?

>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> if filter(lambda x: x > 10, l):
... print "foo"
... else: # the list will be empty, so bar will be printed
... print "bar"
...
bar
I'd like to use any() for this instead, but any() only takes one argument: the iterable. Is there a better way?
Use a generator expression as that one argument:
any(x > 10 for x in l)
Here the predicate is in the expression side of the generator expression, but you can use any expression there, including using functions.
Demo:
>>> l = range(10)
>>> any(x > 10 for x in l)
False
>>> l = range(20)
>>> any(x > 10 for x in l)
True
The generator expression will be iterated over until any() finds a True result, and no further:
>>> from itertools import count
>>> endless_counter = count()
>>> any(x > 10 for x in endless_counter)
True
>>> # endless_counter last yielded 11, the first value over 10:
...
>>> next(endless_counter)
12
Use a generator expression inside of any():
pred = lambda x: x > 10
if any(pred(i) for i in l):
print "foo"
else:
print "bar"
This assumes you already have some predicate function you want to use, of course if it is something simple like this you can just use the Boolean expression directly: any(i > 10 for i in l).

Know if there are lists of lists or not - Python

Suppose we have two lists:
A = [2, 3, [6, 7], 0]
B = [4, 7, 10, 2]
I want to input those values to a function like this:
def gettype(self, a):
for x in a:
if isinstance(x, list):
ident = True
else:
ident = False
return ident
That returns True if there are a list of lists or false otherwise.
ident = gettype(A)
print ident
True
ident = gettype(B)
print ident
False
PS: That function above doesn't work, it's returning False in both cases
Like
A = [2, 3, [6, 7], 0]
B = [4, 7, 10, 2]
def has_lists(lst):
return any(isinstance(x, list) for x in lst)
print has_lists(A) # True
print has_lists(B) # False
The problem is that you change the value of ident for every member of the list. So in fact you only get True if the last element of the list is a list.
Try this instead. If there is a list in the list, return True. If the end of the list is reached without encountering a list, return False.
def gettype(self, a):
for x in a:
if isinstance(x, list):
return True
return False
You are iterating over all elements of the list. The problem is you are overwriting the value of ident and as the last value of the list is an integer the function returns False.
You have to return True when you find the first case of list element like the other implementations in the other answers do.
The issue with your code is that you update ident on each iteration, and the last value in A is not a list, hence the False result. Here's what you likely want:
def gettype(self, a):
for x in a:
if isinstance(x, list):
return True
return False
Notice that the return True statement immediately breaks out of the for loop and terminates the algorithm as soon as a list element is encountered.

How to check if all elements of a list match a condition?

I have a list consisting of like 20000 lists. I use each list's 3rd element as a flag. I want to do some operations on this list as long as at least one element's flag is 0, it's like:
my_list = [["a", "b", 0], ["c", "d", 0], ["e", "f", 0], .....]
In the beginning, all flags are 0. I use a while loop to check if at least one element's flag is 0:
def check(list_):
for item in list_:
if item[2] == 0:
return True
return False
If check(my_list) returns True, then I continue working on my list:
while check(my_list):
for item in my_list:
if condition:
item[2] = 1
else:
do_sth()
Actually, I wanted to remove an element in my_list as I iterated over it, but I'm not allowed to remove items as I iterate over it.
Original my_list didn't have flags:
my_list = [["a", "b"], ["c", "d"], ["e", "f"], .....]
Since I couldn't remove elements as I iterated over it, I invented these flags. But the my_list contains many items, and while loop reads all of them at each for loop, and it consumes lots of time! Do you have any suggestions?
The best answer here is to use all(), which is the builtin for this situation. We combine this with a generator expression to produce the result you want cleanly and efficiently. For example:
>>> items = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
True
>>> items = [[1, 2, 0], [1, 2, 1], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
False
Note that all(flag == 0 for (_, _, flag) in items) is directly equivalent to all(item[2] == 0 for item in items), it's just a little nicer to read in this case.
And, for the filter example, a list comprehension (of course, you could use a generator expression where appropriate):
>>> [x for x in items if x[2] == 0]
[[1, 2, 0], [1, 2, 0]]
If you want to check at least one element is 0, the better option is to use any() which is more readable:
>>> any(flag == 0 for (_, _, flag) in items)
True
If you want to check if any item in the list violates a condition use all:
if all([x[2] == 0 for x in lista]):
# Will run if all elements in the list has x[2] = 0 (use not to invert if necessary)
To remove all elements not matching, use filter
# Will remove all elements where x[2] is 0
listb = filter(lambda x: x[2] != 0, listb)
You could use itertools's takewhile like this, it will stop once a condition is met that fails your statement. The opposite method would be dropwhile
for x in itertools.takewhile(lambda x: x[2] == 0, list)
print x
this way is a bit more flexible than using all():
my_list = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
all_zeros = False if False in [x[2] == 0 for x in my_list] else True
any_zeros = True if True in [x[2] == 0 for x in my_list] else False
or more succinctly:
all_zeros = not False in [x[2] == 0 for x in my_list]
any_zeros = 0 in [x[2] for x in my_list]
Another way to use itertools.ifilter. This checks truthiness and process
(using lambda)
Sample-
for x in itertools.ifilter(lambda x: x[2] == 0, my_list):
print x

Categories

Resources