【问题标题】:Sort list with numbers and letters用数字和字母排序列表
【发布时间】:2018-02-28 21:20:01
【问题描述】:

我正在尝试对包含数字和字母的列表进行排序:

names = ["5aG", "6bG", "10cG", "J1", ...]

输出应如下所示:

['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR','10aG', '10bG', '10cG', '10aR', 'J1', 'J2']

字符串的第一个元素始终是从 5 到 10 的数字,然后是 a - c 的一个字母,最后是另一个字母(“G”或“R”)。

此外还有字符串“J1”和“J2”。它们应该始终是最后一个(“J2”之前的“J1”)。

我怎样才能实现这样的目标?我考虑过使用 lambda 函数。

到目前为止,我对其进行了硬编码,但我认为应该有更好的解决方案。
这是我的硬编码版本:

classes = ['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR','10aG', '10bG', '10cG', '10aR', 'J1', 'J2']

def s(v):
  """Get index of element in list"""
  try:
    return classes.index(v)
  except ValueError:
return 500

l = ['5bG', '6aG', '6bG', '8aR', '9aG', '9bG', '9aR', '10cG', '10aR', 'J1', 'J2', '5aG', '']
w = sorted( l, key=s)
print(w)

【问题讨论】:

    标签: python list sorting lambda


    【解决方案1】:

    你可以使用re提取前面的整数,然后依靠tuple比较。

    import re
    
    def key(s):
        num, letters = re.match(r'(\d*)(.*)', s).groups()
        return float(num or 'inf'), letters
    
    sorted_names = sorted(names, key=key)
    

    请注意如何依靠 float('inf') 将不带前缀数字的令牌推到末尾。

    【讨论】:

    • 虽然float(num or 'inf') 的使用很可爱,但它并不是特别可读,或者不需要注释。可能会发现一个可用的N 来允许使用int(num) or N。考虑到元素只能由位置确定,使用re 是多余的。
    • 这里的问题是int('') 引发了一个错误。相反,我相信在排序对中使用float('inf') 是很自然的。在我看来,使用任意的N 缺乏通用性并且会阻碍可扩展性。
    • 如果数字实际上是浮点数,我可能会对您的技术感到满意。 int('') 的问题是我选择在自己的尝试中单独处理“J”值的原因。
    • 当然,我明白你的意思。我希望我的代码具有通用性和可扩展性,这在这里可能会被压倒,因为只有两个可能的值不以 int 开头。不过,在这一点上,我相信这确实是个人选择。
    • 当然,主要是口味问题。当然,我的比你的好:P
    【解决方案2】:

    你可以试试这个:

    加扰你想要的输出后:

    import re
    s = ['5aR', '7aR', '10aR', '10cG', '9bG', '8aR', '8bG', '6bR', '5aG', '9aG', 'J1', '6aR', '6aG', '5bR', '7aG', '7bG', '9aR', '5bG', 'J2', '6bG', '10bG', '8aG', '10aG', '6cG']
    c, d, *h = sorted(s, key=lambda x:[False if not x[0].isdigit() else int(re.findall('^\d+', x)[0]), x[-1], x[-2]])
    sorted_result = [*h, c, d]
    

    输出:

    ['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR', '10aG', '10bG', '10cG', '10aR', 'J1', 'J2']
    

    【讨论】:

      【解决方案3】:

      这是一种方法。

      lst = ['7aR', '9aG', '7bG', '10cG', '5bG', '6aG', '6bG', '10bG', 'J2', '5aR', '10aG', '9bG', '6aR', '7aG', '10aR', '9aR', '8aR', 'J1', '5bR', '6bR', '5aG', '8bG', '6cG', '8aG']
      
      sorted([i for i in lst if i[0]!='J'], key=lambda x: [int(x[:-2]), x[-1], x[-2]]) + \
      sorted(i for i in lst if i[0]=='J')
      
      # ['5aG', '5bG', '5aR', '5bR', '6aG', '6bG', '6cG', '6aR', '6bR', '7aG', '7bG', '7aR', '8aG', '8bG', '8aR', '9aG', '9bG', '9aR', '10aG', '10bG', '10cG', '10aR', 'J1', 'J2']
      

      【讨论】:

      • 可能是你的一种方式是错误的。请检查您的结果。
      【解决方案4】:

      最简单的方法是使用 Python 的内置排序函数。通过提供一个合适的函数作为key 参数,您可以按照您选择的任何顺序对事物进行排序。

      在内部,当您提供一个键函数时,排序会生成一个包含两个元素的元组的列表。元组的第一个元素是排序键,将键函数应用于第二个元素的结果,即列表中的值。然后它对这些元组进行排序,并返回第二个元素的列表。这称为装饰排序取消装饰。

      您的大多数字符串都是一个整数,后跟两个字母。您希望最后出现的其余部分是"J1""J2"。下面应该是一个合适的键功能。我采取了将int 函数应用于数字的预防措施,以确保它们按数字排序而不是按字典顺序排序(因为'2' > '10')。

      def key_func(s):
          # Ensure J-strings are at the end
          if s.startswith('J'):
              return (1000000, 'J', int(s[1:]))
          else:
              # The rest, split into digits and two characters
              return (int(s[:-2]), s[-2], s[-1])
      

      当使用数据的随机副本进行测试时,

      data = ['8aG', '5aR', '6aG', '10aG', '6cG', '8bG', '9aG',
              '5aG', '6bG', '7aR', 'J1', '10cG', '10bG', '10aR',
              '6bR', 'J2', '6aR', '8aR', '7aG', '9aR', '5bR',
              '9bG', '7bG', '5bG']
      print(sorted(data, key=key_func))
      

      似乎是正确的(为了可读性插入了换行符):

      ['5aG', '5aR', '5bG', '5bR', '6aG', '6aR', '6bG', '6bR',
       '6cG', '7aG', '7aR', '7bG', '8aG', '8aR', '8bG', '9aG',
       '9aR', '9bG', '10aG', '10aR', '10bG', '10cG', 'J1', 'J2']
      

      【讨论】:

        【解决方案5】:

        带有自定义compound_sort()函数:

        import re
        
        lst = ['9bG', '9aR', 'J2', '7bG', '7aG', '6bR', 'J1', '6cG', '6aG', '6bG', '5bG', '5aG', '8bG', '5bR', '8aR', '5aR', '10aR', '6aR', '10bG', '10aG', '9aG', '10cG', '7aR', '8aG']
        pat = re.compile(r'(\d+)(.*)|(J)(\d+)')
        
        def compound_sort(t):
          t = tuple(filter(None, t))    # filter empty(None) matches
          return (int(t[0]),) + t[1:] if t[0] != 'J' else (float('inf'), t[1])
        
        result = sorted(lst, key=lambda x: compound_sort(pat.search(x).groups()))
        print(result)
        

        输出:

        ['5aG', '5aR', '5bG', '5bR', '6aG', '6aR', '6bG', '6bR', '6cG', '7aG', '7aR', '7bG', '8aG', '8aR', '8bG', '9aG', '9aR', '9bG', '10aG', '10aR', '10bG', '10cG', 'J1', 'J2']
        

        【讨论】:

          猜你喜欢
          • 2014-10-08
          • 2021-04-04
          • 1970-01-01
          • 2014-05-05
          • 2018-01-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-19
          相关资源
          最近更新 更多