2017-10-20 4 views
2

J'écris du code pour convertir un fichier CSV en XML. Supposons que j'ai une seule liste comme:Compréhension de liste Python avec plusieurs éléments de longueur variable?

input = ['name', 'val', 0, \ 
     'name', 'val', 1, 'tag', 'val', \ 
     'name', 'val', 2, 'tag', 'val', 'tag', 'val', \ 
     'name', 'val', 0] 

Chaque tranche de cette liste commençant par « nom » désigne un élément avec un nom, une valeur et un nombre variable de paires option-valeur de la variable.

Je veux transformer cela en:

output = [['name', 'val', []], 
      ['name', 'val', ['tag', 'val']], 
      ['name', 'val', ['tag', 'val', 'tag', 'val']], 
      ['name', 'val', []]] 

Pas besoin de séparer les paires de valeurs de balise dans tuples, cela est géré dans une méthode distincte. J'ai une solution, mais ce n'est pas très pythonique:

output=[] 
cursor=0 

while cursor < len(input): 
    name=input[cursor] 
    val=input[cursor+1] 
    ntags=int(input[cursor+2]) 
    optslice=input[cursor+3:cursor+3+2*ntags] 
    cursor = cursor+3+2*ntags 
    print name, val, ntags, optslice, cursor 
    output.append([name, val, optslice])  
print output 

> name val 0 [] 3 
> name val 1 ['tag', 'val'] 8 
> name val 2 ['tag', 'val', 'tag', 'val'] 15 
> name val 0 [] 18 

> [['name', 'val', []], ['name', 'val', ['tag', 'val']], ['name', 'val', ['tag', 'val', 'tag', 'val']], ['name', 'val', []]] 

Je pense que je pourrais probablement le faire comme la compréhension de la liste, mais la longueur variable de chaque élément est de me jeter pour une boucle. L'entrée est analysée à partir d'un CSV, et je peux changer le format pour mieux adapter une solution différente. Des idées?

Répondre

1

Je ne sais pas comment pythonique vous considérez cela, mais vous pouvez faire quelque chose comme ça

finallist = [] 
therest = x 

while therest: 
    name, val, count, *therest = therest 
    sublist, therest = rest[:2*count], rest[2*count:] 
    finallist.append([name, val] + [sublist]) 
6

j'utiliser un itérateur au lieu de votre curseur, puis conduire la compréhension avec for name in it.

it = iter(input) 
output = [[name, next(it), [next(it) for _ in range(2 * next(it))]] for name in it] 

Ou avec islice:

from itertools import islice 

it = iter(input) 
output = [[name, next(it), list(islice(it, 2 * next(it)))] for name in it] 

Cela dit, je suppose que vous ne devriez pas avoir toutes les données dans cette liste à plat en premier lieu. Votre fichier CSV a probablement une structure à utiliser à la place. C'est-à-dire, ne pas aplatir les données bidimensionnelles juste pour que vous ayez besoin de les relâcher. Mais votre question est intéressante quand même :-)

0

Voici mon code:

data = ['name', 'val', 0, 
     'name', 'val', 1, 'tag', 'val', 
     'name', 'val', 2, 'tag', 'val', 'tag', 'val', 
     'name', 'val', 0] 

tmp = [ 
    [ 
     data[pos:pos + 2], 
     [i for i in data[pos + 3:pos + 3 + data[pos + 2] * 2]] 
    ] for pos, e in enumerate(data) if e == 'name'] 

for e in tmp: 
    print e 

La sortie est:

# [['name', 'val'], []] 
# [['name', 'val'], ['tag', 'val']] 
# [['name', 'val'], ['tag', 'val', 'tag', 'val']] 
# [['name', 'val'], []] 
0

Si vous voulez vraiment utiliser la compréhension de la liste pure

a = ['name', 'val', 0, \ 
       'name', 'val', 1, 'tag', 'val', \ 
       'name', 'val', 2, 'tag', 'val', 'tag', 'val', \ 
       'name', 'val', 0] 


print(
[grouped[:2] + [tag for tag in grouped[3:]] for grouped in 
    [ 
     a[i:i+(a[i+1:].index("name") + 1 if a[i+1:].count("name") else len(a[i:])+1)] 
     for i, x in enumerate(a) if x == "name" 
    ] 
]) 

C'est vraiment moche quand même.