Python insert element based on condition - python

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.

Related

Filter elements from list based on True/False from another list

Is there an idiomatic way to mask elements of an array in vanilla Python 3? For example:
a = [True, False, True, False]
b = [2, 3, 5, 7]
b[a]
I was hoping b[a] would return [2, 5], but I get an error:
TypeError: list indices must be integers or slices, not list
In R, this works as I expected (using c() instead of [] to create the lists). I know NumPy has MaskedArray that can do this, I'm looking for an idiomatic way to do this in plain vanilla Python. Of course, I could use a loop and iterate through the mask list and the element list, but I'm hoping there's a more efficient way to mask elements using a higher level abstraction.
You can use itertools.compress:
>>> from itertools import compress
>>> a = [True, False, True, False]
>>> b = [2, 3, 5, 7]
>>> list(compress(b, a))
[2, 5]
Refer "itertools.compress()" document for more details
You can use a pandas.Series which allows, like for dataframe, to filter data with a boolean array
from pandas import Series
a = [True, False, True, False]
b = [2, 3, 5, 7]
res = Series(b)[a].tolist()
print(res) # [2, 5]
I think there aren't a lot of ways to do this, you could use a zip:
print([y for x, y in zip(a, b) if x])
Output:
[2, 5]
You could also create a class with __getitem__ for this:
class index:
def __init__(self, seq):
self.seq = seq
def __getitem__(self, boolseq):
return [x for x, y in zip(boolseq, self.seq) if y]
print(index(a)[b])
Output:
[2, 5]
You could use a list-comprehension:
[b[i] for i in range(len(a)) if a[i]]
or
[b[i] for i,mask in enumerate(a) if maks]
In both cases a list is created by iterating over each element and only inserting it if the mask is true.
Among the many options you can:
1. Use itertools compress
from itertools import compress
a = [True, False, True, False]
b = [2, 3, 5, 7]
result_itertools = list(compress(b, a))
print(result_itertools)
2. Use Filter Function
result_filter = list(filter(lambda x: x[0], zip(a, b)))
for item in result_filter:
print(item[1])
# 2
# 5
3. Use List Comprehension
result_comprehension = [value for bool_, value in zip(a, b) if bool_]
print(result_comprehension)
# [2, 5]

Python difference between filter() and map()

Being new to python I am just trying to figure out the difference between filter() and map().
I wrote a sample script as follows:
def f(x): return x % 2 == 0
def m(y): return y * 2
list = [1,2,3,4]
flist = filter(f, list)
print(list)
print(flist)
mlist = map(m, list)
print(list)
print(mlist)
We see that to both the filter and map we pass a list and assign their output to a new list.
Output of this script is
[1, 2, 3, 4]
[2, 4]
[1, 2, 3, 4]
[2, 4, 6, 8]
Question arises is that function call of both filter and map looks same so how will they behave if we interchange the contents of functions passed to them.
def f(x): return x * 2
def m(y): return y % 2 == 0
list = [1,2,3,4]
flist = filter(f, list)
print(list)
print(flist)
mlist = map(m, list)
print(list)
print(mlist)
This results in
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4]
[False, True, False, True]
This shows filter evaluates the function and if true it returns back the passed element.
Here the function
def f(x): return x * 2
evaluates to
def f(x): return x * 2 != 0
In contrast map evaluates the function expression and returns back the result as items.
So filter always expects its function to do comparison type of task to filter out the elements while map expects its functions to evaluate a statement to get some result.
Is this understanding correct?
They both work a little bit differently but you've got the right idea.
Map takes all objects in a list and allows you to apply a function to it
Filter takes all objects in a list and runs that through a function to create a new list with all objects that return True in that function.
Here's an example
def square(num):
return num * num
nums = [1, 2, 3, 4, 5]
mapped = map(square, nums)
print(*nums)
print(*mapped)
The output of this is
1 2 3 4 5
1 4 9 16 25
Here's an example of filter
def is_even(num):
return num % 2 == 0
nums = [2, 4, 6, 7, 8]
filtered = filter(is_even, nums)
print(*nums)
print(*filtered)
The output of this would be
2 4 6 7 8
2 4 6 8
In map: Function will be applied to all objects of iterable.
In filter: Function will be applied to only those objects of iterable who goes True on the condition specified in expression.
As per my understanding below are the difference between map and filter:
def even(num):
if(num % 2 == 0):
return 'Even'
num_list = [1,2,3,4,5]
print(list(filter(even,num_list))) ->>>>>>>output: [2, 4]
print(list(map(even,num_list))) ->>>>>>> output: [None, 'Even', None, 'Even', None]
So, we can say that:
filter(): formats new list that contains elements which satisfy specific condition.
map(): function iterates through a all items in the given iterable and executes a function which we passed as an argument.
I think yes you got the picture pretty much.
both Map and filter are ways of applying function to iterables.
in Map you can use multiple iterables
definition : map(function_object, iterable1, iterable2,...)
whereas
in filter only one iterable can be used
definition : filter(function_object, iterable)
further in filter the function_object has to return boolean only.
for sake of example following is the Map with multiple iterables as input
list_a = [1, 2, 3]
list_b = [10, 20, 30]
map(lambda x, y: x + y, list_a, list_b) # Output: [11, 22, 33]
The filter() and map() functions are a little bit different.
While Maps takes a normal function, Filter takes Boolean functions. As a matter of fact, filter are maps with conditional logic, a Boolean logic.
Your example is too accurate.
In filter function your supposed to pass a function and a list(the function must evaluate to true or false). If the element passed in the function returns true the filter function will put the element passed into a new list. Where as map function will take an element pass it through a function and return the output of the function and store that to the new list.
map(): Function will be applied to all objects of iterable, we can use as many literables as wee needed
filter(): Function will be applied to only those objects of iterable and added to result which item is True, we can use only one literable
In the below, code 0 is not add in the filter function because 0 is a representation for False in some cases so it is not added to the filter and added in the map function result
def check(num):
return num*1
nums = [0,2, 4, 6, 7, 8]
result = filter(check, nums)
print(list(result))
def check(num):
return num*1
nums = [0,2, 4, 6, 7, 8]
result = map(check, nums)
print(list(result))
map() applies any applicable logic presented to any number of arguments of type list and returns an iterable containing values mapped to each respective members of the argument list(s).
example:
m = map(lambda x,y: 10+x+y, [1,2,3,4],[10,20,30,40])
print(list(m))
output:
[21, 32, 43, 54]
filter() applies the condition specified to one argument of type list and returns an iterable containing values that satisfy the specified condition and thus selected from the argument.
example:
f = filter(lambda x: x<3, [1,2,3,4])
print(list(f))
output:
[1, 2]
The main difference between a map and a filter is the return of values. A map will always have a representation for elements in the list. The filter will filter out the only elements that will meet the conditions in the function.
def checkElementIn(a):
nameList = ['b','a','l','l']
if a in nameList:
return a
testList = ['r','e','d','b','a','l','l']
m_list = map(checkElementIn,testList)
for i in m_list:
print(i)
None
None
None
b
a
l
l
f_list = filter(checkElementIn,testList)
for i in f_list:
print(i)
b
a
l
l
Those are completely different
just take a look at this clear example down below:
def sqr(x):
return x%2==0
mp = map(sqr, [-1,0,1,2,3,4,5,6])
print(list(mp))
[False, True, False, True, False, True, False, True]
fl = filter(sqr, [-1,0,1,2,3,4,5,6])
print(list(fl))
[0, 2, 4, 6]
as you can see in this clear example the filter doesn't care about the function results! It just checks which one of the list items would be true belonging to the calculation def, and the return is a list [0, 2, 4, 6] which means we have got a true result of numbers

Return True if item is empty string

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

summing only the numbers contained in a list

Give a method that sums all the numbers in a list. The method should be able to skip elements that are not numbers. So, sum([1, 2, 3]) should be 6 but sum(['A', 1, 'B', 2, 3]) should also be 6. How can I accomplish this?
What I have already tried so far:
def foo(list):
dict = "ABCDEFGHIJKLMN"
n = 0
for i in range(0, len(list) - 1):
if list[i].str in dict:
""
else:
n= n + list[i]
return n
print foo([1, 2, 3, 4, 5, 6, "A", "B"])
You can do this with a simple one liner:
l1 = [1, 2, 3, 'A']
sum(filter(lambda i: isinstance(i, int), l1))
# prints 6
Or, if you need it inside a function:
def foo(l1):
return sum(filter(lambda i: isinstance(i, int), l1))
Additionally, as noted in the comments, don't use names like dict and list for your variables; *they will shadow they build-in names for the dictionary (dict) and (list) types. You'll then need to explicitly del dict, list in order to use them as intended.
But, let me explain. What filter does is here is:
a) It takes a function as its first argument:
# this function will return True if i is an int
# and false otherwise
lambda i: isinstance(i, int)
and then takes every element inside the list l1 (second argument) and evaluates whether it is True or False based on the function.
b) Then, filter will essentially filter out any objects inside list l1 that are not instances of int (i.e the function returns False for them). As a result, for a list like [1, 2, 3, 'A'] filter is going to return [1, 2, 3] which will then be summed up by sum().
Some Examples:
foo([1, 2, 3, 'A'])
# 6
foo([1, 2, 3])
# 6
foo([1, 2, 3, 'HELLO', 'WORLD'])
# 6
Slight caveat:
As is, this doesn't sum up float values, it drops them (and any other numeric types for that case). If you need that too, simply add the float type in the lambda function as so:
lambda i: isinstance(i, (int, float))
Now, your function sums floats too:
foo([1, 2, 3, 3.1, 'HELLO', 'WORLD'])
# 9.1
Add any other types as necessary in the lambda function to catch the cases that you need.
A catch all case:
As noted by #Copperfield you can check for objects that are instances of any number by utilizing the numbers.Number abstract base class in the numbers module. This acts as a catch-all case for numeric values:
import numbers # must import
sum(filter(lambda i: isinstance(i, numbers.Number), l1))
Simpler and a bit faster, too:
Additionally, as noted by #ShadowRanger, and since lambda might not be the most comfortable construct for new users, one could simply use a generator expression (which is also faster) with sum to get the same exact result:
sum(val for val in l1 if isinstance(val, numbers.Number))
The Pythonic way is to do a try/except. While you could do this in a one liner, I prefer to break things out a bit to see exactly what is happening.
val=0
for item in list:
try:
val+=int(item)
except ValueError:
pass
If you want to include floating points, simply change the int to a float. Floating points are anything with a decimal, among others.
sum([x for x in list if isinstance(x, (int, long, float))])
use filter and isinstance like this
>>> test = [1,2,3,4,5,6,"A","B"]
>>> sum(filter(lambda x:isinstance(x,int),test))
21
>>>
def foo(list):
dict= "ABCDEFGHIJKLMN"
n=0
for i in range(0,len(list)-1):
if str(list[i]) in dict:
""
else:
n= n+list[i]
return n
print foo([1,2,3,4,5,6,"A","B"])
def filtersum(L):
if not L: return 0
if not isinstance(L[0], int): return filtersum(L[1:])
return L[0] + filtersum(L[1:])
Output:
In [28]: filtersum([1,2,3])
Out[28]: 6
In [29]: filtersum([1,'A', 2,3])
Out[29]: 6
def sum_intable(object_list):
return sum(val for val in object_list
if isinstance(val, (int, float)))

How to check if an element is part of a list of lists and integers?

I need to check if an element is part of a list, but the list looks like:
>>> lst = [[1,2], 3, [1,2], [1,4]]
>>> possible = [1,4]
I tried to check it with multiple for-loops, but the problem is the integer, it isn't iterable.
>>> for pos_elem in range(len(possible)):
for i in lst:
for j in i:
if possible[pos_elem] == j:
print j
Is there a code that will check every element of lst without error?
if possible in lst:
#do something
Python has membership operators, which test for membership in a sequence, such as strings, lists, or tuples. There are two membership operators. in and not in
in Evaluates to true if it finds a variable in the specified sequence
and false otherwise.
not in Evaluates to true if it does not finds a variable in the
specified sequence and false otherwise.
You could use python's built in type to check if the element in the list is a list or not like so:
lst = [[1, 2], 3, [1, 2], [1, 4]]
possible = [1, 4]
for element in lst:
#checks if the type of element is list
if type(element) == list:
for x in element:
if x in possible:
print x
else:
if element in possible:
print element
Prints:
1
1
1
4

Categories

Resources