说我有一个字符串a。
a = "12 I have car 8 200 a"
我需要以输出应
的方式对此字符串进行排序8 a car have 12 200 I
即,对字符串进行排序,使所有单词按字母顺序排列,所有整数按数字顺序排列。此外,如果字符串中的第n个元素是整数,则它必须保持整数,如果是单词则必须保留为单词。
这就是我的尝试。
a = "12 I have car 8 200 a"
def is_digit(element_):
"""
Function to check the item is a number. We can make using of default isdigit function
but it will not work with negative numbers.
:param element_:
:return: is_digit_
"""
try:
int(element_)
is_digit_ = True
except ValueError:
is_digit_ = False
return is_digit_
space_separated = a.split()
integers = [int(i) for i in space_separated if is_digit(i)]
strings = [i for i in space_separated if i.isalpha()]
# sort list in place
integers.sort()
strings.sort(key=str.lower)
# This conversion to iter is to make use of next method.
int_iter = iter(integers)
st_iter = iter(strings)
final = [next(int_iter) if is_digit(element) else next(st_iter) if element.isalpha() else element for element in
space_separated]
print " ".join(map(str, final))
# 8 a car have 12 200 I
我得到了正确的输出。但我使用两个单独的排序函数来排序整数和单词(我认为这是昂贵的)。
是否可以使用单个排序功能进行整个排序?
答案 0 :(得分:4)
numpy
允许更简洁地书写,但不能消除对两种不同排序的需求:
$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> from numpy.core.defchararray import isdecimal, lower
>>>
>>> s = "12 I have car 8 200 a"
>>>
>>> a = np.array(s.split())
>>>
>>> integer_mask = isdecimal(a)
>>> string_mask = ~integer_mask
>>> strings = a[string_mask]
>>>
>>> a[integer_mask] = np.sort(np.int_(a[integer_mask]))
>>> a[string_mask] = strings[np.argsort(lower(strings))]
>>>
>>> ' '.join(a)
'8 a car have 12 200 I'
答案 1 :(得分:4)
是否可以使用单个排序功能进行整个排序?
为什么不呢?事实证明,答案已经存在于您的代码中。
integers.sort()
strings.sort(key=str.lower)
请注意您需要在此处按两种不同的功能进行排序。第一种是整数排序,第二种是小写字符串排序。我们可以尝试这样的事情:
def get_sort_order(element):
try:
value = int(element)
except ValueError:
value = element.lower()
return value
a.sort(key=get_sort_order)
但这也不起作用;它只是给我们结果
['8', '12', '200', 'a', 'car', 'have', 'I']
你可能会强迫这个解决方案,但它不会很漂亮。
但是,还有一点我想解决:
但我使用两个单独的排序功能来排序整数和单词(我觉得这很贵)。
无论如何,对两个不同的列表进行排序基本上总是更快。要找出原因,只需看看两个任务的时间复杂性:
假设一个长度为1000的列表,正好是半整数和半字符串,以及O(nlog(n))的排序算法:
单一种类:1000 * log(1000)= 3000
两种不同的排序:2 *(500 * log(500)= ~2699
因此,在单次运行中对列表进行排序更难以实现且速度更慢!
答案 2 :(得分:4)
可以通过在排序的'中应用自定义功能进行一种操作。作为上述用户的方法。我尝试过同样的简化版本。默认排序'方法通过一些调整来做奇迹。希望它能解决您的疑问。
import re
input = "12 I have car 8 200 a"
splitted = input.split()
s_lst=sorted(splitted, key=lambda a:int(a) if a.isdigit() else a.lower())
count_nos = re.findall(r'\d+',' '.join(s_lst))
str_index = len(count_nos)
no_index = 0
result=[]
for i in range(0,len(splitted)):
if splitted[i].isdigit():
result.append(s_lst[no_index])
no_index+=1
else:
result.append(s_lst[str_index])
str_index+=1
print ' '.join(result)
答案 3 :(得分:3)
如果您编写自定义函数进行比较,则可以进行一种排序。 这个想法是按照升序对单词进行排序,并在同一列表中按降序对整数进行排序。比较单词和整数,然后将单词视为与单词相比较小。
然后,如果找到一个单词,则打印最终结果增量为单词的索引,如果找到数字则递减整数索引。
以下代码适用于python2:
a = "12 I have car 8 200 a"
def custom_compare(x,y):
if x.isdigit() and y.isdigit():
return int(y) - int(x) #do a descending order
if x.isdigit() and y.isdigit() == False:
return 1
if x.isdigit() == False and y.isdigit():
return -1
if x.isdigit() == False and y.isdigit() == False:
# do ascending order
if x.lower() == y.lower():
return 0
elif x.lower() < y.lower():
return -1
else:
return 1
original_list = a.split(" ")
sorted_list = sorted(original_list, cmp=custom_compare)
result = []
integer_index = -1
string_index = 0
for word in original_list:
if word.isdigit():
result.append(sorted_list[integer_index])
integer_index = integer_index - 1
else:
result.append(sorted_list[string_index])
string_index = string_index + 1
result
['8', 'a', 'car', 'have', '12', '200', 'I']
Python 3: 导入functools
a = "12 I have car 8 200 a"
def custom_compare(x,y):
if x.isdigit() and y.isdigit():
return int(y) - int(x) #do a descending order
if x.isdigit() and y.isdigit() == False:
return 1
if x.isdigit() == False and y.isdigit():
return -1
if x.isdigit() == False and y.isdigit() == False:
# do ascending order
if x.lower() == y.lower():
return 0
elif x.lower() < y.lower():
return -1
else:
return 1
original_list = a.split(" ")
sorted_list = sorted(original_list, key=functools.cmp_to_key(custom_compare))
result = []
integer_index = -1
string_index = 0
for word in original_list:
if word.isdigit():
result.append(sorted_list[integer_index])
integer_index = integer_index - 1
else:
result.append(sorted_list[string_index])
string_index = string_index + 1
result
PS:可以有效地编写单词比较。 我来自C背景,我不确定比喻的pythonic方式。
答案 4 :(得分:1)
s = "2 is a A -3 car 11 I 0 a"
def magick(s):
s = s.split()
def reverse(tuples):
return [(a, b) for (b, a) in tuples]
def do_sort(tuples):
firsts = [a for a, _ in tuples]
seconds = [a for _, a in tuples]
return list(zip(sorted(firsts), seconds))
def str_is_int(x):
try:
int(x)
return True
except:
return False
indexed = list(enumerate(s))
ints = do_sort([(int(x), ix) for (ix, x) in indexed if str_is_int(x)])
strs = do_sort([( x , ix) for (ix, x) in indexed if not str_is_int(x)])
return ' '.join([str(b) for _, b in sorted(reverse(ints+strs))])
print(magick(s))
答案 5 :(得分:0)
在将原始输入分组为整数和字符串之后,此解决方案使用单个自定义排序算法:
def gt(a, b):
return a > b if isinstance(a, int) and isinstance(b, int) else a[0].lower() > b[0].lower()
def type_sort(d):
'''similar to bubble sort, but does not swap elements of different types.
For instance, type_sort([5, 3, 'b', 'a']) => [3, 5, 'a', 'b']
'''
for _ in d:
for i in range(len(d)-1):
_c = d[i]
_t = d[i+1]
if isinstance(_c, type(_t)):
if gt(_c, _t):
d[i+1] = _c
d[i] = _t
return d
def get_type(x):
return int(x) if x.isdigit() else x
def sort_in_place(s:str):
_s = list(map(get_type, s.split()))
new_s = type_sort([i for i in _s if isinstance(i, int)]+[i for i in _s if isinstance(i, str)])
ints = iter(i for i in new_s if isinstance(i, int))
strings = iter(i for i in new_s if isinstance(i, str))
return ' '.join(map(str, [next(ints) if isinstance(i, int) else next(strings) for i in _s]))
print(sort_in_place(a))
输出:
'8 a car have 12 200 I'