Sorting list based on values from another list?

后端 未结 15 2330
温柔的废话
温柔的废话 2020-11-21 07:14

I have a list of strings like this:

X = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\"]
Y = [ 0,   1,   1,   0,   1,   2,   2,   0,   1 ]


        
相关标签:
15条回答
  • 2020-11-21 08:02

    Zip the two lists together, sort it, then take the parts you want:

    >>> yx = zip(Y, X)
    >>> yx
    [(0, 'a'), (1, 'b'), (1, 'c'), (0, 'd'), (1, 'e'), (2, 'f'), (2, 'g'), (0, 'h'), (1, 'i')]
    >>> yx.sort()
    >>> yx
    [(0, 'a'), (0, 'd'), (0, 'h'), (1, 'b'), (1, 'c'), (1, 'e'), (1, 'i'), (2, 'f'), (2, 'g')]
    >>> x_sorted = [x for y, x in yx]
    >>> x_sorted
    ['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']
    

    Combine these together to get:

    [x for y, x in sorted(zip(Y, X))]
    
    0 讨论(0)
  • 2020-11-21 08:06

    Another alternative, combining several of the answers.

    zip(*sorted(zip(Y,X)))[1]
    

    In order to work for python3:

    list(zip(*sorted(zip(B,A))))[1]
    
    0 讨论(0)
  • 2020-11-21 08:06

    This is an old question but some of the answers I see posted don't actually work because zip is not scriptable. Other answers didn't bother to import operator and provide more info about this module and its benefits here.

    There are at least two good idioms for this problem. Starting with the example input you provided:

    X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
    Y = [ 0,   1,   1,   0,   1,   2,   2,   0,   1 ]
    

    Using the "Decorate-Sort-Undecorate" idiom

    This is also known as the Schwartzian_transform after R. Schwartz who popularized this pattern in Perl in the 90s:

    # Zip (decorate), sort and unzip (undecorate).
    # Converting to list to script the output and extract X
    list(zip(*(sorted(zip(Y,X)))))[1]                                                                                                                       
    # Results in: ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')
    

    Note that in this case Y and X are sorted and compared lexicographically. That is, the first items (from Y) are compared; and if they are the same then the second items (from X) are compared, and so on. This can create unstable outputs unless you include the original list indices for the lexicographic ordering to keep duplicates in their original order.

    Using the operator module

    This gives you more direct control over how to sort the input, so you can get sorting stability by simply stating the specific key to sort by. See more examples here.

    import operator    
    
    # Sort by Y (1) and extract X [0]
    list(zip(*sorted(zip(X,Y), key=operator.itemgetter(1))))[0]                                                                                                 
    # Results in: ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')
    
    0 讨论(0)
  • 2020-11-21 08:07

    Shortest Code

    [x for _,x in sorted(zip(Y,X))]
    

    Example:

    X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
    Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
    
    Z = [x for _,x in sorted(zip(Y,X))]
    print(Z)  # ["a", "d", "h", "b", "c", "e", "i", "f", "g"]
    

    Generally Speaking

    [x for _, x in sorted(zip(Y,X), key=lambda pair: pair[0])]
    

    Explained:

    1. zip the two lists.
    2. create a new, sorted list based on the zip using sorted().
    3. using a list comprehension extract the first elements of each pair from the sorted, zipped list.

    For more information on how to set\use the key parameter as well as the sorted function in general, take a look at this.


    0 讨论(0)
  • 2020-11-21 08:11

    I have created a more general function, that sorts more than two lists based on another one, inspired by @Whatang's answer.

    def parallel_sort(*lists):
        """
        Sorts the given lists, based on the first one.
        :param lists: lists to be sorted
    
        :return: a tuple containing the sorted lists
        """
    
        # Create the initially empty lists to later store the sorted items
        sorted_lists = tuple([] for _ in range(len(lists)))
    
        # Unpack the lists, sort them, zip them and iterate over them
        for t in sorted(zip(*lists)):
            # list items are now sorted based on the first list
            for i, item in enumerate(t):    # for each item...
                sorted_lists[i].append(item)  # ...store it in the appropriate list
    
        return sorted_lists
    
    0 讨论(0)
  • 2020-11-21 08:13

    Here is Whatangs answer if you want to get both sorted lists (python3).

    X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
    Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
    
    Zx, Zy = zip(*[(x, y) for x, y in sorted(zip(Y, X))])
    
    print(list(Zx))  # [0, 0, 0, 1, 1, 1, 1, 2, 2]
    print(list(Zy))  # ['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']
    

    Just remember Zx and Zy are tuples. I am also wandering if there is a better way to do that.

    Warning: If you run it with empty lists it crashes.

    0 讨论(0)
提交回复
热议问题