Union de deux listes

Le problème exposé dans ce sujet a été résolu.

Bonsoir,

Je cherche si il y a une méthode native en python pour fusionner deux listes. Plus exactement, le comportement attendu serait qu’en fusionnant les listes a et b, on obtienne la liste c.

a=[1,2,2,5]
b=[1,2,3,5]
c=[1,2,2,3,5]

Ce que j’ai vu qui marche presque, c’est de passer par les sets, avec la méthode union(), mais ça ne gère pas quand une liste a plusieurs éléments identiques. La méthode count() pour les listes retourne le nombre d’éléments avec une valeur donnée, mais il n’y a pas, de ce que j’ai vu, de méthode d’union pour les listes (extend() est probablement le plus proche, mais pas non plus ce que je souhaite).

Je précise ici que je sais que c’est facile à programmer. Je n’en ai pas besoin dans le cadre d’un projet (sinon, j’écris les quelques lignes et on n’en parle plus), mais plutôt pour renforcer mes connaissances dans le langage.

Merci !

Bonsoir,

À ma connaissance, ça n’existe pas. Tu veux ne pas créer de nouveaux doublons, mais garder ceux existants. En supprimant tous les doublons, les set marchent, mais en gardant les précédant… Ça se fait effectivement en 3 lignes,

c = []
for i in a+b:
 if c.count(i) < max(a.count(i), b.count(i)):
  c += [i]

par contre, en natif, je ne vois rien.

Il y a peut-être un moyen de faire un truc en 1 ligne en passant par des dicos : on transforme [1,2,2,5] en {1:1, 2:2, 5:1}, on fusionne, et on recrée…

Ça donne en gros

d = {i:a.count(i) for i in a}
d2 = {i:b.count(i) for i in b}
# Le or 0 traite le cas None
d3 = {i:max(d.get(i) or 0, d2.get(i) or 0) for i in a+b}

# En une ligne, avec des perfs probablement aux fraises car je recrée le dico à chaque fois...
d4 = { i:max({j:a.count(j) for j in a}.get(i) or 0, {j:b.count(j) for j in b}.get(i) or 0) for i in a+b}

Bon, je n’arrive pas à recréer la liste en partant du dico avec un truc en une ligne, mais j’imagine que ça se fait.

+1 -0

Pour fusionner deux listes triées, tu peux utiliser merge du module heapq.

from heapq import merge

a =[1,2,2,5]
b =[1,2,3,5]

c = list(merge(a, b))

Si tu n’as pas besoin de performance, j’aurais sorted après une concatenation de liste.

a =[1,2,2,5]
b =[1,2,3,5]

c = sorted(a+b)
+2 -1

Je dirais que tu peux t’en sortir avec la classe Counter du module collections et quelques opérations ensemblistes. Le Counter est équivalent à un set sauf qu’il compte le nombre d’éléments.

>>> a = [1,2,2,5]
>>> b = [1,2,3,5]
>>> from collections import Counter
>>> a, b = Counter(a), Counter(b)
>>> a
Counter({2: 2, 1: 1, 5: 1})
>>> b
Counter({1: 1, 2: 1, 3: 1, 5: 1})
>>> common = a & b
>>> common
Counter({1: 1, 2: 1, 5: 1})
>>> c = common + (a-common) + (b-common)
>>> c
Counter({2: 2, 1: 1, 5: 1, 3: 1})
>>> sorted(c.elements())
[1, 2, 2, 3, 5]

@Melcore Ta solution ne donne pas le résultat voulu, elle crée des doublons.

J’y connais rien au python, mais est ce qu’on peut m’expliquer la manipulation attendue ? Notamment, pourquoi dans la liste c, il y a deux 2, et pas un ? (ou pourquoi il n’y a qu’un 1 et un 5 ? et pourquoi pas trois 2 ? Enfin bref je peux aisément inventer une règle faisant que foo(a,b) -> c, mais elles ne sont pas triviales, donc je ne comprends pas ce qui est attendu)

a=[1,2,2,5]
b=[1,2,3,5]
c=[1,2,2,3,5]

Rockaround

En fait mon précédent exemple est inutilement compliqué : j’utilise & pour calculer l’intersection des ensembles, mais avait totalement oublié que je pouvais juste faire une union avec |.

Enfin bref, le code pour calculer c peut simplement se résumer à :

>>> from collections import Counter
>>> a = [1,2,2,5]
>>> b = [1,2,3,5]
>>> union = Counter(a) | Counter(b)
>>> sorted(union.elements())
[1, 2, 2, 3, 5]

Merci à tous pour vos réponses !

Je suis soulagé de voir que je n’avais rien raté d’evident dans la documentation, et je remarque aussi que je n’ai pas du tout eu le reflexe de faire appel à d’autres modules. Il faudra que j’y travaille.

@Gabbro, ma solution était assez proche de ta deuxième tentative, mais j’aime bien la première aussi, plus simple.

@Melcore, merci pour la réponse. Comme l’a dit entwanne, ce n’était pas ce que je voulais, mais Jacen a aussi mentionné que je n’avais pas forcément été très clair. Mea culpa.

@Jacen: Je veux garder les éléments présents dans les deux listes, mais si certains ont la même valeur, je veux en garder autant que dans la liste où il en a le plus. Du coup, 1 est présent dans les deux listes, j’en garde 1. La même chose pour 5. 2 est aussi présent dans les deux listes, mais il y en a 2 dans la première, du coup j’en garde 2. 3 n’est présent que dans la seconde liste, je le garde aussi. On peut aussi considérer que je garde le nombre maximum de 3, entre 0 dans la liste a et 1 dans la liste b. En regardant les codes d’entwanne et Gabbro, ça devrait aussi être plus clair.

@entwanne: Réponses intéressantes, il faut que je regarde d’avantage les méthodes sur les dictionnaires, et le module collections. Pour la première, en voulant rendre la ligne 12 claire en détaillant, je l’ai trouvée plus compliquée à suivre qu’en simplifiant les termes :)

Merci encore à tous pour vos réponses, et mes excuses pour le manque de clarté.

Là, sans dire que c’est optimisé ni rien, c’était pour trouver le plus petit nombre possible qui est un multiple d’une liste d’autres nombres. Je faisais la décomposition en facteurs premiers, de chaque nombre de la liste, et ensuite il ne me reste qu’à faire cette opération et la multiplication. Comme je l’ai dit dans le premier message, ce n’est pas pour un projet, mais juste pour apprendre. D’où aussi que la solution que j’avais qui me semblait bien alambiquée ne me satisfaisait pas.

Par example, pour 6 je récupérais la liste [2,3], et pour 4, la liste [2,2]. Avec l’opération, ça donnait la liste [2,2,3], et donc 2*2*3=12 était la solution.

La solution des Counters me semble largement la plus simple.

En premier lieu, j’avais pensé à un ajouter la position à chaque élement, de fusionner et ensuite de retirer les éléments. Mais ça devient lourd avec l’écriture python.

from heapq import merge
from itertools import groupby

a = [1, 2, 2, 5]
b = [1, 2, 3, 5]

a_ = zip(a, range(len(a))) # On index chacun des éléments
b_ = zip(b, range(len(b)))

c_ = merge(a_, b_) # On fusionne les deux listes

c = [x[0] for x, _ in groupby(c_)] # Là, on supprime les éléments identiques cote à cote.

print(c)

Ça profite juste du fait que les listes étaient ordonnées en premier lieu. De là à dire qu’il y a un gain, absolument pas sûr.

Edit: J’ai trouvé un truc mieux que dict.fromkeys de 3.7. Les groupby. C’était évident, mais je n’y ai pas pensé. 🤷‍♂️

+0 -0

@Rockaround, au vu de ton objectif:

c’était pour trouver le plus petit nombre possible qui est un multiple d’une liste d’autres nombres.

il n’est pas nécessaire de passer par la factorisation de chaque nombre, le module math contient la fonction gcd, pour calculer le plus grand diviseur commun qui peut servir pour calculer le plus petit multiple comun :PPCM(a,b) = a * b / PGCD(a, b)

import math

def ppcm(lst):
  """
  lst : liste des nombres dont on cherche le PPCM
  ex : ppcm([6,4]) -> 12
  """
    p = 1
    for i in lst:
        p = i * p // math.gcd(p,i)
    return p 
+4 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte