Francesco Cariaggi
Francesco Cariaggi

Reputation: 838

Get index of tuple with "NaN" from list of tuples

I have a list of tuples with one element as NaN:

l = [('a', 7.0), ('b', float('nan'))]

And I want to find the index of tuple ('b', float('nan')) in the above list.

l.index(('b', float('nan')) is unable to find the element in the list even though its index is 1. It is raising ValueError exception as:

>>> l.index(('b', float('nan'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: ('b', nan) is not in list

Most likely this is due to the fact that each float('nan') is an independent NaN object, meaning that the two tuples are different objects as well.

How do I solve this problem in general?

Upvotes: 2

Views: 552

Answers (1)

Moinuddin Quadri
Moinuddin Quadri

Reputation: 48090

float('nan') == float('nan') returns False because it is designed to not to match with itself. That's why list.index() function is unable to find a match for NaN value and is raising ValueError exception.

Please read Why is NaN not equal to NaN? to know more about this behaviour.

Below is a custom function check_nan_match() to check whether passed objects are having same value or not. This function will be able to match for NaN objects too based on the above property i.e. NaNs return False when matched with itself.

# Function too check passed values are match, including `NaN`
def check_nan_match(a, b):
    return (b != b and a != a) or a == b
          # ^    ^ `NaN` property to return False when matched with itself

To get the index of tuple in the list containing NaN, here I am creating another custom function as get_nan_index. This function accepts my_list and my_tuple as param, iterates over the my_list to get the index of my_tuple. To check for the equality, I am using previously created check_nan_match function which is capable to match NaN values too.

# Get index from list of tuple , when tuple is passed
def get_nan_index(my_list, my_tuple):
    for i, t in enumerate(my_list):
        if all(check_nan_match(x, y) for x, y in zip(t, my_tuple)):
            return i
    else:
        raise ValueError  # Raise `ValueError` exception in case of no match.
                          # Similar to `list.index(...)` function

Sample run:

# check for tuple with `NaN` 
>>> get_nan_index([('a', 7.0), ('b', float('nan'))], ('b', float('nan')))
1

# check for tuple without `NaN`
>>> get_nan_index([('a', 1), ('b', 2)], ('b', 2))
1

# `ValueError` exception if no match
>>> get_nan_index([('a', 7.0), ('b', 3)], ('b', float('nan')))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in get_nan_index
ValueError

Upvotes: 2

Related Questions