Reputation: 8704
How can I check if the elements of a list are of the same type, without checking individually every element if possible?
For example, I would like to have a function to check that every element of this list is an integer (which is clearly false):
x = [1, 2.5, 'a']
def checkIntegers(x):
# return True if all elements are integers, False otherwise
Upvotes: 117
Views: 135548
Reputation: 1
If there is no element in list, they all be True
lst = []
all(isinstance(x, int) for x in lst)
not any(not isinstance(y,(int)) for y in lst)
not any(not type(y) is int for y in lst)
you may need to check the list may be empty, e.g.
lst = []
if lst and not any(not type(y) is int for y in lst):
do_something()
also,
var1 = False
isinstance(var1, int)
will be True, you may use
var1 = False
type(var1) == int
to prevent such case.
I know the question is focus on all elements are the same type
in a list, but it is weird that return True
if no element
Upvotes: 0
Reputation: 309929
Try using all
in conjunction with isinstance
:
all(isinstance(x, int) for x in lst)
You can even check for multiple types with isinstance
if that is desireable:
all(isinstance(x, (int, long)) for x in lst)
Note that this will pick up inherited classes as well. e.g.:
class MyInt(int):
pass
print(isinstance(MyInt('3'),int)) #True
If you need to restrict yourself to just integers, you could use all(type(x) is int for x in lst)
. But that is a VERY rare scenario.
A fun function you could write with this is one which would return the type of the first element in a sequence if all the other elements are the same type:
def homogeneous_type(seq):
iseq = iter(seq)
first_type = type(next(iseq))
return first_type if all( (type(x) is first_type) for x in iseq ) else False
This will work for any arbitrary iterable, but it will consume "iterators" in the process.
Another fun function in the same vein which returns the set of common bases:
import inspect
def common_bases(seq):
iseq = iter(seq)
bases = set(inspect.getmro(type(next(iseq))))
for item in iseq:
bases = bases.intersection(inspect.getmro(type(item)))
if not bases:
break
return bases
Upvotes: 217
Reputation: 17219
Here is a concise function regarding this, currently it checks (in a list
) if all items are integer or if all items are string, or if mixed data type.
def check_item_dtype_in_list(item_range):
if all(map(lambda x: str(x).isdigit(), item_range)):
item_range = list(map(int, item_range))
print('all are integer')
print(item_range)
return
elif all(isinstance(item, str) for item in item_range):
print('all are string')
print(item_range)
return
elif any(map(lambda x: str(x), item_range)):
print('mixed dtype')
print(item_range)
return
check_item_dtype_in_list(['2', 2, 3])
check_item_dtype_in_list(["2", 2, 'Two'])
check_item_dtype_in_list(['Two', 'Two', 'Two'])
check_item_dtype_in_list([2, 2., 'Two'])
all are integer
[2, 2, 3]
mixed dtype
['2', 2, 'Two']
all are string
['Two', 'Two', 'Two']
mixed dtype
[2, 2.0, 'Two']
Upvotes: 0
Reputation: 199
I would prefer you use this method, where you iterate through each item in the list and check if they are all same data type, if Yes, returns True else, False.
def checkIntegers(x):
# return True if all elements are integers, False otherwise
return all(isinstance(i, type(x[0])) for i in x[1:])
x = [1, 2.5, 'a']
checkIntegers(x)
False
Upvotes: 1
Reputation: 5566
I like the function by EnricoGiampieri (above) but there is a simpler version using all_equal
from the "Itertools recipes" section of the itertools
docs:
from itertools import groupby
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
return next(g, True) and not next(g, False)
All of these recipes are packaged in more_itertools
:
Substantially all of these recipes and many, many others can be installed from the more-itertools project found on the Python Package Index:
pip install more-itertools
The extended tools offer the same high performance as the underlying toolset. The superior memory performance is kept by processing elements one at a time rather than bringing the whole iterable into memory all at once. Code volume is kept small by linking the tools together in a functional style which helps eliminate temporary variables. High speed is retained by preferring “vectorized” building blocks over the use of for-loops and generators which incur interpreter overhead.
from more_itertools import all_equal
all_equal(map(type, iterable))
or with isinstance
and a known type int
(as per the original question)
all_equal(map(lambda x: isinstance(x, int), iterable))
These two ways are more concise than Enrico's suggestion and handle 'void iterators' (e.g. range(0)
) just as Enrico's function does.
all_equal(map(type, range(0))) # True
all_equal(map(type, range(1))) # True
all_equal(map(type, range(2))) # True
all_equal(map(lambda x: isinstance(x, int), range(0))) # True
all_equal(map(lambda x: isinstance(x, int), range(1))) # True
all_equal(map(lambda x: isinstance(x, int), range(2))) # True
Upvotes: 2
Reputation: 1056
Combining some of the answers already given, using a combination of map(), type() and set() provides a imho rather readable answer. Assuming the limitation of not checking for type polymorphisms is ok. Also not the most computationally efficient answer, but it allows to easily check whether all elements are of the same type.
# To check whether all elements in a list are integers
set(map(type, [1,2,3])) == {int}
# To check whether all elements are of the same type
len(set(map(type, [1,2,3]))) == 1
Upvotes: 6
Reputation: 8533
I prefer to use map for a case like this:
from types import IntType
In [21]: map((lambda x: isinstance(x, IntType)), x)
Out[21]: [True, False, False]
Upvotes: -1
Reputation: 250961
Using any()
, no need to traverse whole list. Just break as soon as object which is not int
or long
is found:
>>> not any(not isinstance(y,(int,long)) for y in [1,2,3])
True
>>> not any(not isinstance(y,(int,long)) for y in [1,'a',2,3])
False
Upvotes: 11
Reputation: 20198
You can also use type()
if you want to exclude subclasses. See the difference between isinstance()
and type()
:
>>> not any(not type(y) is int for y in [1, 2, 3])
True
>>> not any(not type(y) == int for y in [1, 'a', 2.3])
False
Although you may not want to, because this will be more fragile. If y changes its type to a subclass of int, this code will break, whereas isinstance()
will still work.
It's OK to use is
because there is only one <type 'int'>
in memory, so they should return the same identity if they are the same type.
Upvotes: 1
Reputation: 428
def c(x):
for i in x:
if isinstance(i,str):
return False
if isinstance(i,float):
return False
return True
Upvotes: -1
Reputation: 6085
The simplest way to check if a list is composed of omogeneous elements can be with the groupby function of the itertools module:
from itertools import groupby
len(list(groupby(yourlist,lambda i:type(i)))) == 1
If th len is different from one it means that it found different kind of types in the list. This has the problem of running trough the entire sequence. If you want a lazy version you can write a function for that:
def same(iterable):
iterable = iter(iterable)
try:
first = type(next(iterable))
return all(isinstance(i,first) for i in iterable)
except StopIteration:
return True
This function store the type of the first element and stop as soon as it find a different type in one of the elements in the list.
Both of this methods are strongly sensitive to the type, so it will see as different int and float, but this should be as close as you can get to your request
EDIT:
replaced the for cycle with a call to all as suggested by mgilson
in case of void iterator it returns True to be consistent with the behavior of the bulitin all function
Upvotes: 3
Reputation: 43447
>>> def checkInt(l):
return all(isinstance(i, (int, long)) for i in l)
>>> checkInt([1,2,3])
True
>>> checkInt(['a',1,2,3])
False
>>> checkInt([1,2,3,238762384762364892364])
True
Upvotes: 2