web-dev-qa-db-fra.com

Python - trouve l'index d'un élément dans une liste de listes

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 ...

10
Jay Gattuso

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.

15
jcollado

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)
8
Rik Poggi

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)
4
bereal

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.

3
Rik Poggi

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)
0
Aaron Lelevier