J'ai une liste de listes:
colours=[["#660000","#863030","#ba4a4a","#de7e7e","#ffaaaa"],["#a34b00","#d46200","#ff7a04","#ff9b42","#fec28d"],["#dfd248","#fff224","#eefd5d","#f5ff92","#f9ffbf"],["#006600","#308630","#4aba4a","#7ede7e","#aaffaa"]]
quel est le moyen le plus propre de parcourir la liste et de renvoyer la position de l’un des éléments, par ex. "#660000"
?
J'ai examiné la méthode d'indexation, mais cela ne semble pas décompresser la liste dans la liste.
postion=colours.index("#660000")
donne: ValueError: ['#660000'] is not in list
, pas [0][0]
comme je l’attendais ...
Je ferais quelque chose comme ça:
[(i, colour.index(c))
for i, colour in enumerate(colours)
if c in colour]
Cela retournera une liste de tuples où le premier index est la position dans la première liste et le second index la position dans la deuxième liste (note: c
est la couleur que vous recherchez, c'est-à-dire "#660000"
).
Pour l'exemple de la question, la valeur renvoyée est la suivante:
[(0, 0)]
Si vous avez juste besoin de trouver la première position dans laquelle la couleur se trouve de manière paresseuse, vous pouvez utiliser ceci:
next(((i, colour.index(c))
for i, colour in enumerate(colours)
if c in colour),
None)
Cela retournera le tuple pour le premier élément trouvé ou None
si aucun élément n'est trouvé (vous pouvez également supprimer l'argument None
ci-dessus, ce qui déclenchera une exception StopIteration
si aucun élément n'est trouvé).
Edit: Comme @RikPoggi le fait correctement remarquer, si le nombre de correspondances est élevé, cela entraînera une surcharge car colour
sera itéré deux fois pour trouver c
. J'ai supposé que cela était raisonnable pour un faible nombre de correspondances et pour avoir une réponse dans une seule expression. Toutefois, pour éviter cela, vous pouvez également définir une méthode en utilisant la même idée, comme suit:
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')]
Notez que, puisque find
est un générateur, vous pouvez l’utiliser comme dans l’exemple ci-dessus, avec next
pour vous arrêter au premier match et éviter de chercher plus loin.
En utilisant enumerate()
vous pourriez écrire une fonction comme celle-ci:
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 vous voulez éviter d'itérer deux fois la sous-liste de cibles, il semble que le meilleur chemin à suivre (et le plus pythonique) est la boucle:
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)
Ce serait peut-être plus simple avec numpy
:
>>> import numpy
>>> ar = numpy.array(colours)
>>> numpy.where(ar=="#fff224")
(array([2]), array([1]))
Comme vous le voyez, vous obtenez un tuple avec tous les index de lignes et de colonnes.
En Python 3, j'ai utilisé ce motif:
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)