简体   繁体   English

基于另一个较短列表的列表排序

[英]List sort based on another shorter list

I need to sort a list based on the order of the elements in another list which is shorter ie, doesn't have all the elements compared to the list I'm sorting.我需要根据另一个更短的列表中元素的顺序对列表进行排序,即与我正在排序的列表相比,没有所有元素。 I run into this error when using the sort(key=short_list) :使用sort(key=short_list)时遇到此错误:

long_list = ['y', 'z', 'x', 'c', 'a', 'b']
short_list = ['b', 'c', 'a']
long_list.sort(key=short_list.index)

ValueError: 'x' is not in list

Is there another way to sort the long_list to result in a list that maintains the order of short_list followed by the order of the elements in the long_list ?是否有另一种方法对long_list进行排序以long_list一个保持short_list顺序的list ,然后是short_list中元素的long_list

['b', 'c', 'a', 'y', 'z', 'x']

Something like this should work:这样的事情应该工作:

def position(value):
    try:
        return short_list.index(value)
    except ValueError:
        return len(short_list)

long_list.sort(key=position)

Sorting is guaranteed to be stable , so using len(short_list) ensures that the unknown values sort last.排序保证是稳定的,因此使用len(short_list)确保未知值排在最后。

You can use in to detect if the element is in the short_list and a ternary to return a tuple based on that.:您可以使用in来检测元素是否在 short_list 和三元组中以基于该元素返回元组。:

>>> long_list = ['y', 'z', 'x', 'c', 'a', 'b']
>>> short_list = ['b', 'c', 'a']
>>> sorted(long_list, key=lambda e: (short_list.index(e),e) if e in short_list  else (len(short_list),e))
['b', 'c', 'a', 'x', 'y', 'z']

Since Python sorts are stable, the order will only change based on a change of the elements themselves.由于 Python 排序是稳定的,因此顺序只会根据元素本身的变化而变化。 To make that change, we can use a tuple with either a (index_of_the_element, element) of (len(short_list), element) to effect that change.为了进行这种更改,我们可以使用带有(index_of_the_element, element)(len(short_list), element)来实现该更改。

If you want the elements to not change order if the element is not in short list, just return an empty tuple:如果您希望元素不在短列表中时不改变顺序,只需返回一个空元组:

>>> sorted(long_list, key=lambda e: (short_list.index(e),e) if e in short_list  else (len(short_list),))
['b', 'c', 'a', 'y', 'z', 'x']

I would search in the short list first and in the long list if necessary:如果需要,我会先在短名单中搜索,然后在长名单中搜索:

>>> def index(list1, list2):
...  def inner(value):
...   try:
...    return list1.index(value)
...   except ValueError:
...    return list2.index(value)
... 
>>> long_list = ['x', 'y', 'z', 'a', 'b', 'c']
>>> short_list = ['a', 'b', 'c']
>>> long_list.sort(key=index(short_list, long_list))
>>> long_list
['a', 'b', 'c', 'x', 'y', 'z']

Edit: as florian-weimer pointed out , this solution does not always work.编辑:正如 florian-weimer 指出的那样,这个解决方案并不总是有效。 Joining the two solutions:加入两种解决方案:

>>> def index(list1, list2):
...  def inner(value, l=len(list1)):
...   try:
...    return list1.index(value)
...   except ValueError:
...    return l
...  return inner
... 
>>> long_list = ['x', 'y', 'z', 'a', 'b', 'c']
>>> short_list = ['a', 'b', 'c', 'y']
>>> sorted(long_list, key=index(short_list, long_list))
['a', 'b', 'c', 'y', 'x', 'z']
>>> 

Basically, what i'm doing here is i am first sorting elements of long_list by their index in short_list.基本上,我在这里做的是首先按 long_list 中的索引对 long_list 的元素进行排序。 If the element is not in short_list, I'll assign them all the same value of len(short_list).如果该元素不在 short_list 中,我将为它们分配与 len(short_list) 相同的值。 While sorting, if the first_sort is the same for two elements, it'll sort by second_sort which is the position in the long_list.排序时,如果两个元素的 first_sort 相同,则按照 long_list 中的位置 second_sort 进行排序。 lets look at each case让我们看看每个案例

  1. 'a', 'b' and 'c' are all sorted by their position in short_list. 'a'、'b' 和 'c' 都按它们在 short_list 中的位置排序。 If there are repetitions of 'a', 'b' or 'c' in long_list, then they are sorted by their respective positions.如果long_list中有'a'、'b'或'c'的重复,则按各自的位置排序。 So you'll get ['b', 'c', 'c', 'a', 'a', 'a'].所以你会得到 ['b', 'c', 'c', 'a', 'a', 'a']。
  2. 'y', 'z', 'x' get sorted first by len(short_list). 'y', 'z', 'x' 首先按 len(short_list) 排序。 This puts them last compared to the elements in the short list.与短列表中的元素相比,这将它们放在最后。 Then they get sorted in the order they come in long_list.然后它们按照它们在 long_list 中的顺序进行排序。

Key takeaways:关键要点:

  1. You can pass a function to key that will let you sort according to any function.您可以将一个函数传递给 key,让您可以根据任何函数进行排序。
  2. You can pass a function that returns multiple values, so that any ties in the first value are resolved by the second value and so on.您可以传递一个返回多个值的函数,以便第一个值中的任何关系都由第二个值解析,依此类推。
long_list = ['y', 'z', 'x', 'c', 'a', 'b']
short_list = ['b', 'c', 'a']

def sorter(x):
    if x in short_list:
        first_sort = short_list.index(x)
    else:
        first_sort = len(short_list)
    second_sort = long_list.index(x)
    return first_sort, second_sort

print(sorted(long_list, key=sorter)) 
['b', 'c', 'a', 'y', 'z', 'x']

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM