Finding if a string exists in a nested tuple in Python

What is the best (and fastest) way in Python 2.7.x to check if a string (or any other data type) exists in a nested tuple?

For instance:

RECIPES = ( ('apple', 'sugar', 'extreme_Force'), ('banana', 'syrup', 'magical_ends'), ('caramel', 'chocolate', 'pancake_MONSTER'), ('banana',('someAnother','banana')) ) 

This set should be checked if banana appears in any nested tuple and returns a location index, in this case 1,0 .

In addition, tuples can be nested at any depth.

+4
source share
5 answers

Recursive Multiposition Indexing:

 import sys from collections import Sequence,defaultdict #making code python3-compatible if sys.version_info[0] == 3: basestring = str def buildLocator(tree): locator = defaultdict(list) def fillLocator(tree, locator,location): for index,item in enumerate(tree): if isinstance(item,basestring): locator[item].append(location+(index,)) elif isinstance(item,Sequence): fillLocator(item,locator, location+(index,)) fillLocator(tree,locator,()) return locator RECIPES = ( ('apple', 'sugar', 'extreme_Force'), ('banana', 'syrup', 'magical_ends'), ('caramel', 'chocolate', 'pancake_MONSTER'), ('banana',('someAnother','banana')) ) locator = buildLocator(RECIPES) print(locator['banana']) 

prints

 [(1, 0), (3, 0), (3, 1, 1)] 
+7
source

A generator could do it nicely if you only need the first match:

 def find_location(text): try: return next((i, j) for i, t in enumerate(RECIPES) for j, v in enumerate(t) if v == text) except StopIteration: return (None, None) # not found 

Using:

 >>> find_location('banana') (1, 0) >>> find_location('apple') (0, 0) >>> find_location('chocolate') (2, 1) >>> find_location('spam') (None, None) 

Note that the first value is the index in the proper RECIPES sequence, the second is the index in a separate tuple; RECIPES[1][0] == 'banana'

+4
source

use a for loop to determine if an element exists or not, and break the loop as soon as it is found.

 In [48]: RECIPES = ( ....: ('apple', 'sugar', 'extreme_Force'), ....: ('banana', 'syrup', 'magical_ends'), ....: ('caramel', 'chocolate', 'pancake_MONSTER'), ....: ) In [49]: for i,x in enumerate(RECIPES): ....: if 'banana' in x: ....: print i,x.index('banana') ....: break ....: ....: 1 0 
+1
source

Why not try numpy ?

 import numpy as np RECIPES = ( ('apple', 'sugar', 'extreme_Force'), ('banana', 'syrup', 'magical_ends'), ('caramel', 'chocolate', 'pancake_MONSTER'), ) np_recipes = np.array(recipes) indices = zip(*np.where( np_recipes == 'banana' ) ) #[(1, 0)] 

This works for your example because the data is nicely ordered. I suggest that it should be noted that this will not work for arbitrary nesting as you requested (but I will leave it here if someone else finds this with a similar, more limited question).

+1
source

Recursively find the first situation:

 RECIPES = ( ('apple', 'sugar', 'extreme_Force'), ('banana', 'syrup', 'magical_ends'), ('caramel', 'chocolate', 'pancake_MONSTER'), ) def find_str(seq, s): for idx, item in enumerate(seq): if hasattr(item, "__iter__"): r = find_str(item, s) if r is not None: return [idx]+r elif item == s: return [idx] print find_str(RECIPES, "banana") # prints [1, 0] 
0
source

Source: https://habr.com/ru/post/1434924/


All Articles