Python – encuentra el índice de un elemento en una lista de listas

Tengo una lista de listas:

colours=[["#660000","#863030","#ba4a4a","#de7e7e","#ffaaaa"],["#a34b00","#d46200","#ff7a04","#ff9b42","#fec28d"],["#dfd248","#fff224","#eefd5d","#f5ff92","#f9ffbf"],["#006600","#308630","#4aba4a","#7ede7e","#aaffaa"]] 

¿Cuál es la forma más limpia de buscar en la lista y devolver la posición de uno de los elementos, por ejemplo, "#660000" ?

He mirado el método de índice, pero eso no parece descomprimir la lista dentro de la lista.

 postion=colours.index("#660000") 

da: ValueError: ['#660000'] is not in list , no [0][0] como espero …

Haría algo como esto:

 [(i, colour.index(c)) for i, colour in enumerate(colours) if c in colour] 

Esto devolverá una lista de tuplas donde el primer índice es la posición en la primera lista y el segundo índice la posición en la segunda lista (nota: c es el color que está buscando, es decir, "#660000" ).

Para el ejemplo en la pregunta, el valor devuelto es:

 [(0, 0)] 

Si solo necesita encontrar la primera posición en la que se encuentra el color de forma perezosa, puede usar esto:

 next(((i, colour.index(c)) for i, colour in enumerate(colours) if c in colour), None) 

Esto devolverá la tupla para el primer elemento encontrado o None si no se encuentra ningún elemento (también puede eliminar el argumento None que se encuentra arriba y generará una excepción StopIteration si no se encuentra ningún elemento).

Edición: como @RikPoggi señala correctamente, si el número de coincidencias es alto, esto generará cierta sobrecarga porque el colour se itera dos veces para encontrar c . Supuse que esto era razonable para un número bajo de coincidencias y para tener una respuesta en una sola expresión. Sin embargo, para evitar esto, también puede definir un método utilizando la misma idea de la siguiente manera:

 def find(c): for i, colour in enumerate(colours): try: j = colour.index(c) except ValueError: continue yield i, j matches = [match for match in find('#660000')] 

Tenga en cuenta que, dado que find es un generador, puede usarlo como en el ejemplo anterior con next para detenerse en la primera coincidencia y omitir la búsqueda.

Usando enumerate() puedes escribir una función como esta:

 def find(target): for i,lst in enumerate(colours): for j,color in enumerate(lst): if color == "#660000": return (i, j) return (None, None) 

Si quieres evadir la iteración de la sublista de destino dos veces, parece que la mejor manera (y la más pythonica) de hacerlo es un bucle:

 def find_in_sublists(lst, value): for sub_i, sublist in enumerate(lst): try: return (sub_i, sublist.index(value)) except ValueError: pass raise ValueError('%s is not in lists' % value) 

Sería quizás más simple usando numpy :

 >>> import numpy >>> ar = numpy.array(colours) >>> numpy.where(ar=="#fff224") (array([2]), array([1])) 

Como ves, obtendrás una tupla con todos los índices de fila y columna.

En Python 3, utilicé este patrón:

 CATEGORIES = [ [1, 'New', 'Sub-Issue', '', 1], [2, 'Replace', 'Sub-Issue', '', 5], [3, 'Move', 'Sub-Issue', '', 7], ] # return single item by indexing the sub list next(c for c in CATEGORIES if c[0] == 2) 

Otra cosa que puedes hacer es seleccionar la sección de la lista que deseas y luego usar el índice para encontrarla.

 list_name[0].index("I want coffee")