soñador
quiero tomar el diferencia entre listas x
y y
:
>>> x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> y = [1, 3, 5, 7, 9]
>>> x - y
# should return [0, 2, 4, 6, 8]
aaronasterling
Use una lista de comprensión para calcular la diferencia manteniendo el original orden de x
:
[item for item in x if item not in y]
Si no necesita las propiedades de la lista (por ejemplo, ordenar), use un establecer diferenciacomo sugieren las otras respuestas:
list(set(x) - set(y))
Permitir x - y
sintaxis infija, anular __sub__
en una clase que hereda de list
:
class MyList(list):
def __init__(self, *args):
super(MyList, self).__init__(args)
def __sub__(self, other):
return self.__class__(*[item for item in self if item not in other])
Uso:
x = MyList(1, 2, 3, 4)
y = MyList(2, 5, 2)
z = x - y
-
Si lo haces
[1,1,2,2] - [1,2]
obtendrá una lista vacía.[1,1,2,2] - [2]
da[1,1]
Así que no es realmente una resta de listas, es más como “Lista de lista X sin elementos del conjunto Y“.-Alfred Zien
6 de febrero de 2016 a las 10:25
-
El método de comprensión de listas es mucho más lento (en mi ejemplo) que el método de diferencia de conjuntos.
– redfiloux
26 de febrero de 2019 a las 10:22
-
para y más grandes: [item for item in x if item not in set(y)]
– Barney Szabolcs
4 sep 2019 a las 12:58
-
@BarnabasSzabolcs: Eso no salvará nada, porque convertirá
y
a unset
antes cada cheque (que tiene un costo similar al del trabajo original). Necesitarías haceryset = set(y)
fuera del listcomp, luego pruebeif item not in yset
o como un truco atroz, haz[item for yset in [set(y)] for item in x if item not in yset]
que abusa de listcomps anidados para almacenar en caché elyset
como una sola línea. Una solución de una sola línea un poco menos fea que funciona adecuadamente sería usarlist(itertools.filterfalse(set(y).__contains__, x))
porque el argumento defilterfalse
solo se construye una vez.– ShadowRanger
5 sep 2019 a las 22:03
-
Esto funciona, pero tenga en cuenta que las iteraciones son lentas. Prefiero la siguiente solución con restar un conjunto de un conjunto, lo que parece funcionar súper rápido.
– R3qUi3M
3 de febrero de 2022 a las 15:24
>>> z = list(set(x) - set(y))
>>> z
[0, 8, 2, 4, 6]
O puede que simplemente tenga x e y configurados para que no tenga que hacer ninguna conversión.
-
esto perderá cualquier pedido. Eso puede o no importar dependiendo del contexto.
– aaronasterling
7 de agosto de 2010 a las 0:19
-
Esto también perderá cualquier posible duplicado que pueda necesitar/querer mantenimiento.
– Ópalo
24 de junio de 2011 a las 5:31
-
yo obtengo
TypeError: unhashable type: 'dict'
– Havnar
2 de agosto de 2017 a las 22:26
-
Esto es mucho más rápido en los casos en que las listas que se comparan son grandes.
– JqueryParaAgregarNúmeros
6 de octubre de 2018 a las 2:57
-
Si el orden y los duplicados de elementos en la lista no son importantes para el contexto, esta es una excelente respuesta y además es muy legible.
– Watt Iamsuri
25 de abril de 2019 a las 5:36
si los artículos duplicados y pedidos son un problema:
[i for i in a if not i in b or b.remove(i)]
a = [1,2,3,3,3,3,4]
b = [1,3]
result: [2, 3, 3, 3, 4]
-
Esto funciona, aunque es
O(m * n)
tiempo de ejecución (y me estremezco cada vez que un listcomp incluye efectos secundarios); puedes mejorarlo usandocollections.Counter
LlegarO(m + n)
tiempo de ejecución– ShadowRanger
6 sep 2019 a las 18:50
-
Me cuesta entender esto, ¿alguien puede explicarme?
– anushka
23 de octubre de 2019 a las 7:28
-
@anushka En lugar de
[item for item in a if not item in b]
(que funciona más como una resta de conjuntos), esto tiene... if not item in b or b.remove(item)
.b.remove(item)
devolucionesfalse
siitem
no está dentrob
y eliminaitem
deb
de lo contrario. Esto evita que los elementos de la segunda lista (a - b
, en este caso) de ser restado más de una vez por cada ocurrencia. Esto evita la eliminación de duplicados, lo que sucede si sigue algunas de las otras respuestas. No es súper eficiente (definitivamente siga la sugerencia de @ShaworRangers para la eficiencia), pero creo que esta es probablemente la respuesta más correcta.– jmorganmartin
1 de julio de 2022 a las 0:38
Papa Noel
Esa es una operación de “resta de conjunto”. Use la estructura de datos establecida para eso.
En Pitón 2.7:
x = {1,2,3,4,5,6,7,8,9,0}
y = {1,3,5,7,9}
print x - y
Producción:
>>> print x - y
set([0, 8, 2, 4, 6])
abarnert
Para muchos casos de uso, la respuesta que desea es:
ys = set(y)
[item for item in x if item not in ys]
Este es un híbrido entre la respuesta de aaronasterling y la respuesta de quantumSoup.
la versión de aaronasterling sí len(y)
comparaciones de artículos para cada elemento en x
, entonces toma tiempo cuadrático. La versión de quantumSoup usa conjuntos, por lo que realiza una única búsqueda de conjuntos en tiempo constante para cada elemento en x
—sino, porque convierte ambos x
y y
en conjuntos, pierde el orden de sus elementos.
Convirtiendo solo y
en un conjunto, e iterando x
en orden, obtiene lo mejor de ambos mundos: tiempo lineal y preservación del orden.*
Sin embargo, esto todavía tiene un problema con la versión de quantumSoup: requiere que sus elementos sean hashable. Eso está bastante integrado en la naturaleza de los conjuntos.** Si está intentando, por ejemplo, restar una lista de dictados de otra lista de dictados, pero la lista para restar es grande, ¿qué debe hacer?
Si puede decorar sus valores de alguna manera que se puedan modificar, eso resuelve el problema. Por ejemplo, con un diccionario plano cuyos valores son en sí mismos hashable:
ys = {tuple(item.items()) for item in y}
[item for item in x if tuple(item.items()) not in ys]
Si sus tipos son un poco más complicados (por ejemplo, a menudo se trata de valores compatibles con JSON, que se pueden modificar, o listas o dictados cuyos valores son recursivamente del mismo tipo), aún puede usar esta solución. Pero algunos tipos simplemente no se pueden convertir en algo hashable.
Si sus elementos no son, y no se pueden hacer, hashable, pero son comparables, al menos puede obtener un tiempo logarítmico lineal (O(N*log M)
que es mucho mejor que el O(N*M)
tiempo de la solución de la lista, pero no tan bueno como el O(N+M)
tiempo de la solución establecida) ordenando y usando bisect
:
ys = sorted(y)
def bisect_contains(seq, item):
index = bisect.bisect(seq, item)
return index < len(seq) and seq[index] == item
[item for item in x if bisect_contains(ys, item)]
Si sus elementos no se pueden modificar ni comparar, entonces está atascado con la solución cuadrática.
* Tenga en cuenta que también puede hacer esto usando un par de OrderedSet
objetos, para los que puede encontrar recetas y módulos de terceros. Pero creo que esto es más simple.
** La razón por la que las búsquedas de conjuntos son de tiempo constante es que todo lo que tiene que hacer es codificar el valor y ver si hay una entrada para ese hash. Si no puede codificar el valor, esto no funcionará.
Si las listas permiten elementos duplicados, puede usar Contador de colecciones:
from collections import Counter
result = list((Counter(x)-Counter(y)).elements())
Si necesita conservar el orden de los elementos de x:
result = [ v for c in [Counter(y)] for v in x if not c[v] or c.subtract([v]) ]
Las otras soluciones tienen uno de algunos problemas:
- No preservan el orden, o
- No eliminan un recuento preciso de elementos, por ejemplo, para
x = [1, 2, 2, 2]
yy = [2, 2]
se convierteny
a unset
y eliminar todos los elementos coincidentes (dejando[1]
solamente) o quitar uno de cada elemento único (dejando[1, 2, 2]
), cuando el comportamiento adecuado sería eliminar2
dos veces, dejando[1, 2]
o - Ellas hacen
O(m * n)
trabajo, donde una solución óptima puede hacerO(m + n)
trabajar
Alain estaba en el camino correcto con Counter
para resolver #2 y #3, pero esa solución perderá el orden. La solución que preserva el orden (quitando el primer n
copias de cada valor para n
repeticiones en el list
de valores a eliminar) es:
from collections import Counter
x = [1,2,3,4,3,2,1]
y = [1,2,2]
remaining = Counter(y)
out = []
for val in x:
if remaining[val]:
remaining[val] -= 1
else:
out.append(val)
# out is now [3, 4, 3, 1], having removed the first 1 and both 2s.
Para hacerlo quitar el último copias de cada elemento, simplemente cambie el for
bucle a for val in reversed(x):
y añadir out.reverse()
inmediatamente después de salir del for
bucle.
Construyendo el Counter
es O(n)
en términos de y
longitud de , iterando x
es O(n)
en términos de x
longitud de , y Counter
las pruebas de membresía y la mutación son O(1)
mientras list.append
se amortiza O(1)
(un dado append
puede ser O(n)
pero para muchos append
s, los promedios generales de O grande O(1)
ya que cada vez menos de ellos requieren una reasignación), por lo que el trabajo total realizado es O(m + n)
.
También puede probar para determinar si había algún elemento en y
que no fueron quitados de x
probando:
remaining = +remaining # Removes all keys with zero counts from Counter
if remaining:
# remaining contained elements with non-zero counts
-
Nota: Este hace requieren que los valores sean hashable, pero cualquier solución que no requiera objetos hashable tampoco es de propósito general (por ejemplo, puede contar
int
s en una matriz de longitud fija) o tiene que hacer más queO(m + n)
trabajo (por ejemplo, el siguiente mejor gran-O sería hacer una ordenadalist
de valores únicos/pares de conteo, cambiandoO(1)
dict
búsquedas enO(log n)
búsquedas binarias; necesitaría valores únicos con sus conteos, no solo valores no únicos ordenados, porque de lo contrario estaría pagandoO(n)
Costos para eliminar los elementos del clasificado.list
).– ShadowRanger
6 sep 2019 a las 18:47
-
Creo que esta es la mejor respuesta hasta ahora, pero como referencia, creo que sería mejor si se refactorizara en una función, ya que supongo que escribir más de 5 líneas de código cada vez que uno quiere restar dos listas es un poco engorroso. .
-Vladimir Vilimaitis
6 mayo 2022 a las 14:31
-
¡Esta debería ser la respuesta seleccionada aunque solo sea para el punto 2!
– Brian Riesgo
23 de junio de 2022 a las 1:21
Que debería [2, 2] – [2] ¿devolver? []? [2]?
– McKay
24 de enero de 2017 a las 20:08
Que debería [2, 1, 2, 3, 2, 4, 2] – [2, 3, 2] volver y por qué? ¿Debería encontrar el 232 en el medio y devolver 2142? ¿O debería encontrar el primero cada vez y devolver 1242? ¿O algo mas? Lo que digo es que estas no son respuestas obvias y dependen de la necesidad.
– McKay
5 de julio de 2017 a las 15:07