首页 > 解决方案 > Python如何根据元组列表中的值对字典列表进行排序

问题描述

unsorted = [{"#":"1", "files": [("test.txt",1),("example.out",1)]},
             {"#":"2", "files": [("other.txt",0),("test.txt",3)]},
             {"#":"3", "files": [("example.out",0)]},
             {"#":"4", "files": [("test.txt",2)]}]

files: 其中一个listtuples指一个文件及其rev。例如, ("test.txt",1) 是 rev 1 的文件 test.txt。

需要对对象进行排序unsorted,使其元素按以下顺序排列:

  1. 按书名排序
  2. 如果多个元素具有相同的书名,则次要版本在前

以下是想要的结果:

sorted = [{"#":"3", "files": [("example.out",0)]},
             {"#":"1", "files": [("test.txt",1),("example.out",1)]},
             {"#":"4", "files": [("test.txt",2)]},
              {"#":"2", "files": [("other.txt",0),("test.txt",3)]}]

PS,可以用lambda做吗?

标签: pythonpython-3.xsortinglambda

解决方案


正如已经指出的那样,鉴于您的数据的性质,您可能会得到不符合您的规则的条目组合。根据您的要求进行排序也不是一项简单的任务,因为每个节点都需要知道其他节点中的数据才能做出决定。

话虽如此,我已经给了它一个裂缝。虽然并不简单,但我可能过度考虑了这个过程。这也不是一个优雅的解决方案,也绝对不是一个有效的过程。我想在大型数据集上执行需要相当长的时间。

我已经用您的样本数据对其进行了测试,结果似乎很好。我添加了一些额外的数据,包括不能满足您的要求的数据,我觉得在这种情况下得到的订单就足够了。如果无法正确排序,将其更改为退出和/或导致错误会很简单。假设总是有正确的顺序,这应该有效。肯定需要更多测试:

class Node:
    """
    A node representing the inner dictionary of files. Keeps rack of
    lesser and greater nodes so that they can be placed into a list in appropriate order.
    """

    def __init__(self, file_dict: dict):
        self.file_dict = file_dict
        self.files = {i[0]: i[1] for i in file_dict['files']}
        self.greater_nodes = []
        self.lesser_nodes = []
        self.on_stack = False
        self.in_list = False

    def compare_nodes(self, other_node, file_name):
        """Compares the nodes to each other and lists each other in either their greater or lesser groups"""
        if self.files[file_name] < other_node.files[file_name]:
            self.lesser_nodes.append(other_node)
            other_node.greater_nodes.append(self)
        else:
            self.greater_nodes.append(other_node)
            other_node.lesser_nodes.append(self)

    def place_node(self, node_list, lesser_list):
        """Places node in given list, but first checks for any greater node in the list and places those first"""
        self.on_stack = True  # If on stack and referenced as greater, place into list.
        lesser_list += self.lesser_nodes
        for node in self.greater_nodes:
            if node not in lesser_list:  # Don't go to nodes that are in any way lower than those on the stack.
                if node.on_stack and not node.in_list:
                    node_list.append(node.file_dict)
                    node.in_list = True
                    node.on_stack = False
                elif not node.in_list:
                    node.place_node(node_list, lesser_list)
        if not self.in_list:
            node_list.append(self.file_dict)
            self.in_list = True
        self.on_stack = False

    def __repr__(self):
        return f'{self.file_dict["#"]}'


class FileObj:
    """
    Representation for each different file name.
    Tracks nodes with common file name so that they can be appropriately compared.
    """

    def __init__(self, name: str):
        self.name = name
        self.nodes = []

    def add_node(self, new_node: Node):
        """Add another node to the list for the file object, making sure to
        compare any new nodes to old based on file name."""
        for old_node in self.nodes:
            new_node.compare_nodes(old_node, self.name)
        self.nodes.append(new_node)

    def place_nodes(self, node_list):
        """Place all nodes into a given list."""
        # Sorts nodes first (shouldn't be necessary in most cases, as they should all have valid relational data).
        self.nodes.sort(key=lambda x: x.files[self.name])
        for node in self.nodes:
            if not node.in_list:
                node.place_node(node_list, [])

    def __repr__(self):
        return f"{self.name} : {self.nodes}"


def sort_function(unsorted_list):
    # Get dict of file objects.
    file_dict = {str(y[0]): FileObj(y[0]) for x in unsorted_list for y in x['files']}
    file_order = sorted([x for x in file_dict.keys()])  # Reference of file names in sorted order.

    # Get list of dicts from the unsorted list as node-objects.
    node_list = [Node(x) for x in unsorted_list]

    # Get nodes to assign themselves to file_objects
    for node in node_list:
        for file in node.files.keys():
            file_dict[file].add_node(node)

    # Go through file_order to start placing nodes in lexicographical order.
    final_list = []
    for file_name in file_order:
        file_obj = file_dict[file_name]
        file_obj.place_nodes(final_list)

    # Print results to check.
    print(final_list, "\n")
    for d in final_list:
        print(d)


def main():
    unsorted_list = [{"#": "1", "files": [("test.txt", 1), ("example.out", 1)]},
                     {"#": "2", "files": [("other.txt", 0), ("test.txt", 3)]},
                     {"#": "3", "files": [("example.out", 0)]},
                     {"#": "4", "files": [("test.txt", 2)]}]

    sort_function(unsorted_list)


if __name__ == '__main__':
    main()

推荐阅读