使用整数和单词对字符串进行排序,而不改变其位置

时间:2017-11-15 15:48:55

标签: python python-2.7 performance sorting iterator

说我有一个字符串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

我得到了正确的输出。但我使用两个单独的排序函数来排序整数和单词(我认为这是昂贵的)。

是否可以使用单个排序功能进行整个排序?

6 个答案:

答案 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'