Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I know which element in a list triggered an any() function?

I'm developing a Python program to detect names of cities in a list of records. The code I've developed so far is the following:

aCities = ['MELBOURNE', 'SYDNEY', 'PERTH', 'DUBAI', 'LONDON']

cxTrx = db.cursor()
cxTrx.execute( 'SELECT desc FROM AccountingRecords' )

for row in cxTrx.fetchall() :
    if any( city in row[0] for city in aCities ) :
        #print the name of the city that fired the any() function
    else :
        # no city name found in the accounting record

The code works well to detect when a city in the aCities' list is found in the accounting record but as the any() function just returns True or False I'm struggling to know which city (Melbourne, Sydney, Perth, Dubai or London) triggered the exit.

I've tried with aCities.index and queue but no success so far.

like image 501
Luis U. Avatar asked Aug 01 '15 07:08

Luis U.


4 Answers

I don't think it's possible with any. You can use next with default value:

for row in cxTrx.fetchall() :
    city = next((city for city in aCities if city in row[0]), None)
    if city is not None:
        #print the name of the city that fired the any() function
    else :
        # no city name found in the accounting record
like image 65
falsetru Avatar answered Nov 15 '22 23:11

falsetru


You won't because any returns only a boolean value. But you can use next:

city = next((city for city in aCities if city in row[0]), None)
if city:
   ...

With this syntax you'll find the first city that is a substring of the description stored in the database row. If there isn't one, the second parameter e.g. None, will be returned.

like image 40
JuniorCompressor Avatar answered Nov 15 '22 23:11

JuniorCompressor


No, it is possible with any. It's a bit of a stunt - it "reads funny" - but it does work:

if any(city in row[0] and not print(city) for city in aCities):
    # city in row[0] found, and already printed :)
    # do whatever else you might want to do
else:
    # no city name found in the accounting record

or more concisely, if all you really want to do is print the city:

if not any(city in row[0] and not print(city) for city in aCities):
    # no city name found in the accounting record

It works for three reasons:

  1. any stops at the first true (truthy) item,
  2. and is short-circuiting, so not print(city) will only be eval'd if city in row[0] is true, and
  3. print returns None, so not print(...) is always True.

PS: As @falsetru points out, in Python 2.x print isn't a function, so you'll have to first say:

from __future__ import print_function

As I said, it works for 3 reasons - Python 3 reasons ;) Oh, wait - that's 4 reasons...

like image 27
BrianO Avatar answered Nov 15 '22 22:11

BrianO


For completeness, here is a solution with a standard for-loop:

for city in aCities:
    if city in row[0]:
        print 'Found city', city
        break
else:
    print 'Did not find any city'

This should have the same short-circuit behavior as any, since it breaks out of the for-loop when the condition is fulfilled. The else part is executed when the for-loop runs till the end without breaking, see this question.

Although this solution uses more lines, it actually uses less characters than the other solutions, since there is no call to next(..., None), it does not have the extra city = assignment and there is no second if city is None (at the cost of one extra break). When things get more complicated, it is sometimes clearer to write out the for-loop explicitly, then to string together some generator expressions and next statements.

like image 25
Bas Swinckels Avatar answered Nov 15 '22 22:11

Bas Swinckels