Solución:
Aquí hay un enfoque optimizado usando defaultdict()
y bisect()
:
In [14]: lst = [5, 'a', 'x', 3, 6, 'b']
In [15]: from collections import defaultdict
In [16]: import bisect
In [17]: def use_dict_with_bisect(lst):
d = defaultdict(list)
for i in lst:
bisect.insort(d[type(i)], i)
# since bisect doesn't accept key we need to reverse the sorted integers
d[int].sort(reverse=True)
return [d[type(i)].pop() for i in lst]
.....:
Demostración:
In [18]: lst
Out[18]: [5, 'a', 'x', 3, 6, 'b']
In [19]: use_dict_with_bisect(lst)
Out[19]: [3, 'x', 'b', 5, 6, 'a']
En caso de que esté tratando con listas más grandes, está más optimizado para eliminar usando bisect
que tiene una complejidad sobre O (n2) y solo usa Python incorporado sort()
función con complejidad Nlog (n).
In [26]: def use_dict(lst):
d = defaultdict(list)
for i in lst:
d[type(i)].append(i)
d[int].sort(reverse=True); d[str].sort()
return [d[type(i)].pop() for i in lst]
Compare con otras respuestas que muestren el enfoque más reciente utilizando dict
y incorporado sort
es casi 1 ms más rápido que los otros enfoques:
In [29]: def use_sorted1(lst):
letters = sorted(let for let in lst if isinstance(let,str))
numbers = sorted((num for num in lst if not isinstance(num,str)), reverse = True)
return [letters.pop() if isinstance(elt,str) else numbers.pop() for elt in lst]
.....:
In [31]: def use_sorted2(lst):
f1 = iter(sorted(filter(lambda x: isinstance(x, str), lst), reverse=True))
f2 = iter(sorted(filter(lambda x: not isinstance(x, str), lst)))
return [next(f1) if isinstance(x, str) else next(f2) for x in lst]
.....:
In [32]: %timeit use_sorted1(lst * 1000)
100 loops, best of 3: 3.05 ms per loop
In [33]: %timeit use_sorted2(lst * 1000)
100 loops, best of 3: 3.63 ms per loop
In [34]: %timeit use_dict(lst * 1000) # <-- WINNER
100 loops, best of 3: 2.15 ms per loop
Aquí hay un punto de referencia que muestra cómo usar bisect
puede ralentizar el proceso para listas largas:
In [37]: %timeit use_dict_with_bisect(lst * 1000)
100 loops, best of 3: 4.46 ms per loop
Mira ma, no iter
:
lst = ['a', 2, 'b', 1, 'c', 3]
letters = sorted(let for let in lst if isinstance(let,str))
numbers = sorted((num for num in lst if not isinstance(num,str)), reverse = True)
lst = [(letters if isinstance(elt,str) else numbers).pop()for elt in lst]
Estoy buscando una manera de convertir esto en una (horrible) frase de una sola línea, pero no he tenido suerte hasta ahora, ¡sugerencias bienvenidas!
Intenté esto creando dos generadores y luego tomándolos condicionalmente:
f1 = iter(sorted(filter(lambda x: isinstance(x, str), lst), reverse=True))
f2 = iter(sorted(filter(lambda x: not isinstance(x, str), lst)))
[next(f1) if isinstance(x, str) else next(f2) for x in lst]
# ['c', 1, 'b', 2, 'a', 3]