31 votes

Duplication des chaînes de caractères dans une liste et ajout de suffixes entiers aux chaînes nouvellement ajoutées

Supposons que j'ai une liste :

l = ['a', 'b', 'c']

Et sa liste de suffixes :

l2 = ['a_1', 'b_1', 'c_1']

J'aimerais que le résultat souhaité soit le suivant :

out_l = ['a', 'a_1', 'b', 'b_2', 'c', 'c_3']

Le résultat est la version entrelacée des deux listes ci-dessus.

Je peux écrire régulièrement for pour y arriver, mais je me demande s'il n'y a pas une façon plus Pythonique (par exemple, en utilisant la compréhension de liste ou lambda) pour y arriver.

J'ai essayé quelque chose comme ça :

list(map(lambda x: x[1]+'_'+str(x[0]+1), enumerate(a)))
# this only returns ['a_1', 'b_2', 'c_3']

En outre, quelles modifications devraient être apportées pour le cas général, c'est-à-dire pour 2 listes ou plus où l2 n'est pas nécessairement une dérivée de l ?

67voto

coldspeed Points 111053

yield

Vous pouvez utiliser un générateur pour une solution élégante. A chaque itération, donnez deux fois -une fois avec l'élément original, et une fois avec l'élément avec le suffixe ajouté.

Le générateur devra être épuisé, ce qui peut être fait en ajoutant une carte de crédit. list appel à la fin.

def transform(l):
    for i, x in enumerate(l, 1):
        yield x
        yield f'{x}_{i}'  # {}_{}'.format(x, i)

Vous pouvez également réécrire ceci en utilisant l'option yield from syntaxe pour la délégation de générateur :

def transform(l):
    for i, x in enumerate(l, 1):
        yield from (x, f'{x}_{i}') # (x, {}_{}'.format(x, i))

out_l = list(transform(l))
print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']

Si vous êtes sur des versions antérieures à python-3.6, remplacez f'{x}_{i}' con '{}_{}'.format(x, i) .

Généraliser
Considérons un scénario général où vous avez N listes de la forme :

l1 = [v11, v12, ...]
l2 = [v21, v22, ...]
l3 = [v31, v32, ...]
...

Que vous souhaitez intercaler. Ces listes ne sont pas nécessairement dérivées les unes des autres.

Pour gérer les opérations d'entrelacement avec ces N listes, vous devrez itérer sur les paires :

def transformN(*args):
    for vals in zip(*args):
        yield from vals

out_l = transformN(l1, l2, l3, ...)

Tranches de list.__setitem__

Je le recommande du point de vue de la performance. Commencez par allouer de l'espace à une liste vide, puis affectez les éléments de la liste à leurs positions appropriées en utilisant l'affectation de liste tranchée. l va dans les index pairs, et l' ( l modifié) va dans des index impairs.

out_l = [None] * (len(l) * 2)
out_l[::2] = l
out_l[1::2] = [f'{x}_{i}' for i, x in enumerate(l, 1)]  # [{}_{}'.format(x, i) ...]

print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']

C'est toujours le plus rapide d'après mes chronométrages (ci-dessous).

Généraliser
Pour gérer N listes, on assigne itérativement aux tranches.

list_of_lists = [l1, l2, ...]

out_l = [None] * len(list_of_lists[0]) * len(list_of_lists)
for i, l in enumerate(list_of_lists):
    out_l[i::2] = l

zip + chain.from_iterable

Une approche fonctionnelle, similaire à la solution de @chrisz. Construisez des paires en utilisant zip et ensuite l'aplatir en utilisant itertools.chain .

from itertools import chain
# [{}_{}'.format(x, i) ...]
out_l = list(chain.from_iterable(zip(l, [f'{x}_{i}' for i, x in enumerate(l, 1)]))) 

print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']

iterools.chain est largement considérée comme l'approche d'aplatissement des listes pythiques.

Généraliser
C'est la solution la plus simple à généraliser, et je soupçonne que c'est la plus efficace pour les listes multiples lorsque N est grand.

list_of_lists = [l1, l2, ...]
out_l = list(chain.from_iterable(zip(*list_of_lists)))

Performance

Jetons un coup d'oeil à quelques tests de performance pour le cas simple de deux listes (une liste avec son suffixe). Les cas généraux ne seront pas testés car les résultats varient largement en fonction des données.

enter image description here

Code d'étalonnage, pour référence.

Fonctions

def cs1(l):
    def _cs1(l):
        for i, x in enumerate(l, 1):
            yield x
            yield f'{x}_{i}'

    return list(_cs1(l))

def cs2(l):
    out_l = [None] * (len(l) * 2)
    out_l[::2] = l
    out_l[1::2] = [f'{x}_{i}' for i, x in enumerate(l, 1)]

    return out_l

def cs3(l):
    return list(chain.from_iterable(
        zip(l, [f'{x}_{i}' for i, x in enumerate(l, 1)])))

def ajax(l):
    return [
        i for b in [[a, '{}_{}'.format(a, i)] 
        for i, a in enumerate(l, start=1)] 
        for i in b
    ]

def ajax_cs0(l):
    # suggested improvement to ajax solution
    return [j for i, a in enumerate(l, 1) for j in [a, '{}_{}'.format(a, i)]]

def chrisz(l):
    return [
        val 
        for pair in zip(l, [f'{k}_{j+1}' for j, k in enumerate(l)]) 
        for val in pair
    ]

4 votes

Je recommande le yield du point de vue de la lisibilité, de la simplicité et de la maintenance, car il est peu probable que cela constitue un goulot d'étranglement majeur (le volume de données n'est probablement pas assez élevé, et il ne s'agit probablement pas d'une application dont les performances sont critiques). Le générateur est extraordinairement simple à comprendre. L'OP peut revenir en arrière et optimiser si cela s'avère être un problème. +1

2 votes

@user1717828 Je suis heureux que vous ayez appris quelque chose de cela ! Elles sont appelées f-strings et ont été introduites pour python-3.6+. Jetez un coup d'œil à cette section de la docs pour plus d'informations. Bon apprentissage !

0 votes

Je ne comprends pas le pourquoi yield from . Pourriez-vous ajouter plus d'explications à ce sujet, s'il vous plaît ?

6voto

Ajax1234 Points 42210

Vous pouvez utiliser une liste de compréhension comme suit :

l=['a','b','c']
new_l = [i for b in [[a, '{}_{}'.format(a, i)] for i, a in enumerate(l, start=1)] for i in b]

Sortie :

['a', 'a_1', 'b', 'b_2', 'c', 'c_3']

Méthode facultative, plus courte :

[j for i, a in enumerate(l, 1) for j in [a, '{}_{}'.format(a, i)]]

5voto

user3483203 Points 28606

Vous pourriez utiliser zip :

[val for pair in zip(l, [f'{k}_{j+1}' for j, k in enumerate(l)]) for val in pair]

Sortie :

['a', 'a_1', 'b', 'b_2', 'c', 'c_3']

0 votes

Vous pourriez utiliser une liste de compréhension à la place de zip. Je ne suis pas sûr que ce soit le plus rapide...

3 votes

Si vous regardez les temps, c'est plus rapide que d'utiliser une compréhension de liste. Beaucoup plus rapide.

2voto

Isaac Boakye Points 71

Voici ma mise en œuvre simple

l=['a','b','c']
# generate new list with the indices of the original list
new_list=l + ['{0}_{1}'.format(i, (l.index(i) + 1)) for i in l]
# sort the new list in ascending order
new_list.sort()
print new_list
# Should display ['a', 'a_1', 'b', 'b_2', 'c', 'c_3']

0voto

Especially Lime Points 111

Si vous vouliez retourner [["a","a_1"],["b","b_2"],["c","c_3"]] vous pourriez écrire

new_l=[[x,"{}_{}".format(x,i+1)] for i,x in enumerate(l)]

Ce n'est pas ce que vous voulez, vous voulez plutôt ["a","a_1"]+["b","b_2"]+["c","c_3"] . On peut le faire à partir du résultat de l'opération ci-dessus en utilisant sum() ; puisque vous additionnez des listes, vous devez ajouter la liste vide comme argument pour éviter une erreur. Cela donne donc

new_l=sum(([x,"{}_{}".format(x,i+1)] for i,x in enumerate(l)),[])

Je ne sais pas comment cela se compare en termes de vitesse (probablement pas bien), mais je trouve que c'est plus facile de comprendre ce qui se passe que les autres réponses basées sur la compréhension des listes.

0 votes

En quoi n'est-ce pas ce qui a été demandé ? Si l==['a','b','c'] le résultat est ['a', 'a_1', 'b', 'b_2', 'c', 'c_3'] selon les besoins, et il évite l'utilisation d'une for boucle.

1 votes

Eh désolé, je n'ai pas lu plus loin que la première ligne. CEPENDANT, appeler sum() sur une liste est généralement mal vu, c'est pire qu'une boucle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X