Quiero hacer algo similar a esto:
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> y = [1,3,5,7,9]
>>> y
[1, 3, 5, 7, 9]
>>> y - x # (should return [2,4,6,8,0])
Pero esto no es compatible con las listas de Python. ¿Cuál es la mejor manera de hacerlo?
Respuestas:
Use una lista de comprensión:
Si desea utilizar la
-
sintaxis infija, simplemente puede hacer:entonces puedes usarlo como:
Pero si no necesita absolutamente las propiedades de la lista (por ejemplo, ordenar), simplemente use conjuntos como recomiendan las otras respuestas.
fuente
list
para nombres de variables ya que sombrea allist
constructor. Si utiliza 'lista', preceda con un guión bajo. Además, al*
[1,1,2,2] - [1,2]
, obtendrá una lista vacía.[1,1,2,2] - [2]
da[1,1]
Por lo tanto, en realidad no se trata de la resta de la lista, es más como "Lista de la Lista X sin elementos del conjunto Y " .y
en unset
antes de cada cheque (que es un costo similar al trabajo original). Tendría que haceryset = set(y)
fuera de la lista de compilación, luego probarif item not in yset
, o como un hack atroz, hacer lo[item for yset in [set(y)] for item in x if item not in yset]
que abusa de las listas de compilación anidadas para almacenar en cachéyset
como una sola línea. Una solución un poco menos fea de un solo trazo que funcione adecuadamente sería usarlist(itertools.filterfalse(set(y).__contains__, x))
porque el argumentofilterfalse
solo se construye una vez.Usar diferencia de conjunto
O puede que solo se establezcan x e y para que no tenga que hacer ninguna conversión.
fuente
TypeError: unhashable type: 'dict'
Esa es una operación de "resta de conjuntos". Use la estructura de datos establecida para eso.
En Python 2.7:
Salida:
fuente
Si los artículos duplicados y pedidos son un problema:
[i for i in a if not i in b or b.remove(i)]
fuente
O(m * n)
tiempo de ejecución (y me avergüenzo cada vez que un listcomp incluye efectos secundarios); puedes mejorarlo usandocollections.Counter
para obtenerO(m + n)
tiempo de ejecución.Para muchos casos de uso, la respuesta que desea es:
Este es un híbrido entre la respuesta de aaronasterling y la respuesta de quantumSoup .
La versión de aaronasterling hace
len(y)
comparaciones de elementos para cada elementox
, por lo que lleva tiempo cuadrático. La versión de quantumSoup usa conjuntos, por lo que realiza una sola búsqueda de conjuntos de tiempo constante para cada elemento enx
, pero porque convierte ambosx
yy
en conjuntos, pierde el orden de sus elementos.Al convertir solo
y
en un conjunto e iterarx
en orden, obtienes 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á más o menos integrado en la naturaleza de los conjuntos. ** Si está tratando de restar, por ejemplo, una lista de dictos de otra lista de dictos, pero la lista para restar es grande, ¿qué debe hacer?
Si puede decorar sus valores de alguna manera para que se puedan compartir, eso resuelve el problema. Por ejemplo, con un diccionario plano cuyos valores son en sí mismos hashables:
Si sus tipos son un poco más complicados (por ejemplo, a menudo se trata de valores compatibles con JSON, que son hashable, 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 hashaable.
Si sus elementos no son, y no se pueden hacer, hashables, pero son comparables, al menos puede obtener un tiempo de registro lineal (
O(N*log M)
que es mucho mejor que elO(N*M)
tiempo de la solución de la lista, pero no tan bueno como elO(N+M)
tiempo de la solución establecida) ordenando y usandobisect
:Si sus artículos no son hashables ni comparables, entonces está atrapado con la solución cuadrática.
* Tenga en cuenta que también puede hacerlo utilizando un par de
OrderedSet
objetos, para los cuales 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 calcular el valor hash y ver si hay una entrada para ese hash. Si no puede cambiar el valor, esto no funcionará.
fuente
Buscar valores en conjuntos es más rápido que buscarlos en listas:
Creo que esto escalará un poco mejor que:
Ambos preservan el orden de las listas.
fuente
set(y)
y no se convertiráy
en un nuevo conjunto en cada bucle? De lo contrario, respondía necesidad de abarnert:ys = set(y); [i for i in x if i not in ys]
.if i not in set(y)
lleva un 25% más de tiempo queif i not in y
(dondey
hay una lista). La conversión previa del conjunto lleva un 55% menos de tiempo. Probado con bastante cortox
yy
, pero las diferencias deberían ser más pronunciadas con la longitud, en todo caso.y
de cada elemento de lax
; a menos que la comparación de igualdad sea realmente costosa en relación con el cómputo hash, esto siempre se perderá por completoitem not in y
.Si las listas permiten elementos duplicados, puede usar Contador de colecciones:
Si necesita preservar el orden de los elementos de x:
fuente
Counter.subtract
no elimina elementos de valor cero (-
y lo-=
hace, pero nosubtract
), por lo que nunca dejaría de eliminar elementos. Desea reemplazarnot v in c
connot c[v]
(que devuelve cero para elementos inexistentes, por lo que puede probar de forma segura el retorno para "cero" a través denot
).Creo que la forma más fácil de lograr esto es usando set ().
fuente
Las otras soluciones tienen uno de los pocos problemas:
x = [1, 2, 2, 2]
yy = [2, 2]
se convierten eny
aset
, y eliminan todos los elementos coincidentes (dejando[1]
solo) o eliminan uno de cada elemento único (salir[1, 2, 2]
), cuando el comportamiento adecuado sería eliminar2
dos veces, dejando[1, 2]
oO(m * n)
, donde una solución óptima puedeO(m + n)
funcionarAlain estaba en el camino correcto
Counter
para resolver el # 2 y el # 3, pero esa solución perderá el orden. La solución que conserva el orden (eliminando las primerasn
copias de cada valor para lasn
repeticiones en loslist
valores a eliminar) es:Pruébalo en línea!
Para que elimine las últimas copias de cada elemento, simplemente cambie el
for
buclefor val in reversed(x):
y agrégueloout.reverse()
inmediatamente después de salir delfor
ciclo.La construcción de
Counter
esO(n)
en términos dey
longitud, la iteraciónx
esO(n)
en términos dex
longitud, yCounter
las pruebas de membresía y la mutación sonO(1)
, mientras quelist.append
se amortizaO(1)
(un hechoappend
puede serO(n)
, pero para muchosappend
s, los promedios generales de Big-OO(1)
ya que cada vez menos de ellos requieren una reasignación), por lo que el trabajo general realizado esO(m + n)
.También puede probar para determinar si había algún elemento
y
que no se eliminóx
mediante la prueba:fuente
int
s en matriz de longitud fija) o tiene que hacer algo más queO(m + n)
el trabajo (por ejemplo, la siguiente mejor grande -O sería hacer un ordenlist
de pares de valor / recuento únicos, cambiando lasO(1)
dict
búsquedas en búsquedasO(log n)
binarias; necesitaría valores únicos con sus recuentos, no solo valores no únicos ordenados, porque de lo contrario estaría pagandoO(n)
costos para eliminar el elementos de la ordenadalist
).Prueba esto.
fuente
La respuesta proporcionada por @aaronasterling se ve bien, sin embargo, no es compatible con la interfaz por defecto de la lista:
x = MyList(1, 2, 3, 4)
vsx = MyList([1, 2, 3, 4])
. Por lo tanto, el siguiente código se puede usar como una lista más amigable para python:Ejemplo:
fuente
Creo que esto es más rápido:
fuente
Este ejemplo resta dos listas:
fuente