使用可变数量的键对来自 Json 文件的列表进行排序



编辑:关于解决方案的一些评论。

正如用户@jasonharper所说:"请注意,您不必在单个 key= 函数中执行所有操作 - 您可以一次按一个条件排序,从最小到最重要,并且 .sort()/sorted() 是稳定的这一事实意味着你得到了正确的结果。

这最终成为解决方案:创建一个函数,该函数在 For 循环中相应地输入 .sort()/sorted() 以创建所需的顺序。但是,我确实邀请任何对该问题感兴趣的人阅读其余的建议,因为它们确实包含一些非常好的相关想法。




这篇文章结束得有点长,希望我解释一下自己,因为它确实需要细节。让我知道如何改进它。


问题的摘要是:

  • 例程读取 Json 文件并使用 json 库在 Python 中创建该文件的列表。
  • 列表的这些条目当然是字典。我对原始数据的设置方式没有发言权,尽管可以在例程中操作数据,但输出格式应与输入格式相同。json 是统一的,它始终是相同的格式。
  • 每个键的值的类型不同,一个值可以是字符串,其他值可以是字典、布尔值或列表。(见下面的示例)
  • 这个想法是给一组键排序,但键的数量通常是可变的(取决于用户)。可能是在某些时候,用户只想使用一个键进行排序,但另一个用户需要基于两个键的顺序,而另一个用户可能使用三个键。
  • 不仅如此,密钥本身可能会更改。例如,一个用户可能希望根据键 A、B、C 进行排序,但另一个用户可能希望使用键 B、D 进行排序。
  • 考虑到上述两点,可能需要递归顺序,因为所讨论的键可能引用键值中的字典,而不是列表中的字典。

让我们来看看细节。

首先是数据样本。这既是一个简化,也是一个说明性的例子,因为我无法分享有问题的实际数据。当我加载 json 文件时,列表如下所示:

[ {'district': 'Cave', 'profession': 'Teacher', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Mountain', 'profession': 'Baker', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Castle', 'profession': 'Professor', 'details': {'gender': 'F', 'status': 'Single', 'kids': False}, 'availability': False, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Dungeon', 'profession': 'Professor', 'details': {'gender': 'M', 'status': 'Married', 'kids': True}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Castle', 'profession': 'Policeman', 'details': {'gender': 'NA', 'status': 'Single', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Dungeon', 'profession': 'Policeman', 'details': {'gender': 'NA', 'status': 'Single', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Cave', 'profession': 'Secretary', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]

此数据示例确实反映了我在现实生活中遇到的案例类型:

地区:给定数量的地区名称。会有很多重复。多个条目将具有相同的学区。 职业:同上。 详细信息:这是一个字典,可以包含字符串和布尔值,如图所示。 可用性:布尔数据。 首选项:一个首选项列表,如图所示,在大多数情况下它是相同的(这在我们的现实生活中的例子中是有意义的)。这个列表可以更短或更大,但我虽然我包括这个列表,因为这是我现实生活中的例子的一部分,我们可以认为这个条目的优先级很低,以解决问题。我想专注于其余的键。

重要的是要说每个键都会有重复,很多条目将具有相同的区域,其他条目将具有相同的职业,其他细节(如性别和孩子)当然会在条目之间重叠。

因此,给定数据,如果我只想按 Key = 地区排序,结果应该是这样的(如果您看到错误,请纠正我,我将编辑,我手动做这个例子):


[ {'district': 'Castle', 'profession': 'Professor', 'details': {'gender': 'F', 'status': 'Single', 'kids': False}, 'availability': False, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Castle', 'profession': 'Policeman', 'details': {'gender': 'NA', 'status': 'Single', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Cave', 'profession': 'Secretary', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Cave', 'profession': 'Teacher', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]

[ {'district': 'Dungeon', 'profession': 'Professor', 'details': {'gender': 'M', 'status': 'Married', 'kids': True}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Dungeon', 'profession': 'Policeman', 'details': {'gender': 'NA', 'status': 'Single', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Mountain', 'profession': 'Baker', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]

对于地区和专业,它将是:


[ {'district': 'Castle', 'profession': 'Policeman', 'details': {'gender': 'NA', 'status': 'Single', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Castle', 'profession': 'Professor', 'details': {'gender': 'F', 'status': 'Single', 'kids': False}, 'availability': False, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]

[ {'district': 'Cave', 'profession': 'Secretary', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Cave', 'profession': 'Teacher', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Dungeon', 'profession': 'Policeman', 'details': {'gender': 'NA', 'status': 'Single', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Dungeon', 'profession': 'Professor', 'details': {'gender': 'M', 'status': 'Married', 'kids': True}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]

[ {'district': 'Mountain', 'profession': 'Baker', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]

但是现在我想使用职业和性别排序,请记住性别在字典中,结果是这样的:


[ {'district': 'Castle', 'profession': 'Professor', 'details': {'gender': 'F', 'status': 'Single', 'kids': False}, 'availability': False, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Castle', 'profession': 'Policeman', 'details': {'gender': 'NA', 'status': 'Single', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Cave', 'profession': 'Secretary', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Cave', 'profession': 'Teacher', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]

[ {'district': 'Dungeon', 'profession': 'Professor', 'details': {'gender': 'M', 'status': 'Married', 'kids': True}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Dungeon', 'profession': 'Policeman', 'details': {'gender': 'NA', 'status': 'Single', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Mountain', 'profession': 'Baker', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]

另一个人只想按状态和孩子排序(同样,这些在字典中):

[ {'district': 'Dungeon', 'profession': 'Professor', 'details': {'gender': 'M', 'status': 'Married', 'kids': True}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Mountain', 'profession': 'Baker', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Cave', 'profession': 'Secretary', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Cave', 'profession': 'Teacher', 'details': {'gender': 'F', 'status': 'Married', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Castle', 'profession': 'Professor', 'details': {'gender': 'F', 'status': 'Single', 'kids': False}, 'availability': False, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]
[ {'district': 'Dungeon', 'profession': 'Policeman', 'details': {'gender': 'NA', 'status': 'Single', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading'] } ]
[ {'district': 'Castle', 'profession': 'Policeman', 'details': {'gender': 'NA', 'status': 'Single', 'kids': False}, 'availability': True, 'preferences': ['Travel', 'Games', 'Food', 'Reading']  }]

等等等等,我不想只用例子来填充。

我知道我可以使用lambda函数对列表进行排序,例如,假设列表称为人,获得第一个结果的代码将是:

people_sorted = sorted(people, key=lambda k: k['district'])

第二个是:

people_sorted = sorted(people, key=lambda k: (k['district'],k['profession']))

对于第三个示例,它将是:

people_sorted = sorted(people, key=lambda k: (['profession'],k['details']['gender']))

最后一个是:

people_sorted = sorted(people, key=lambda k: (['profession'],k['details']['gender']))

当然,我可以根据需要输入任意数量的参数,例如,如果我需要根据性别、职业和地区进行排序,我可以使用:

people_sorted = sorted(people, key=lambda k: (k['details']['gender'], k['profession'], k['district']))

我知道你也可以定义键函数,但是尽管我对任何建议持开放态度,但我试图使这个问题尽可能简短,因此我只包括lambda方法。

您可能在这里认识到的问题是,如何根据用户输入在 lambda 函数中创建元组。我不知道"动态"是否是正确的术语,但问题是。

如果我有给定数量的键 A1、A2、A3...AN ,其中 N 可以更改,我如何正确获取:

people_sorted = sorted(people, key=lambda k: (k['A1'], k['A2'],... k['AN']))

例如,AM可以引用名为"details"的字典,因此实际代码可能如下所示:

people_sorted = sorted(people, key=lambda k: (k['A1'], k['A2'],..., k['details']['AM'] ,..., k['AN']))

再:

为了简洁起见,我使用了lambda示例,由于示例,帖子本身已经很长了。

我们没有触及我包含的列表示例(首选项)以反映实际数据,但在我看来,如果我解决其他情况,我可以解决它。


希望我能解释自己并为帖子的结尾道歉。

谢谢

如果我试图做一个快速的解决方案。 首先,我将从字段名称到提取该字段名称的函数的映射开始:

field_name_mapping = {
"gender": lambda record: record['details']['gender'],
"district": lambda record: record['district']
...
}

然后,当用户输入"gender, district",或者输入在您的系统中完成时,您可以将其分解为令牌,然后创建函数列表

function_list = [field_name_mapping[token] for token in parsed_user_input]

其中,如何解析用户输入的确切细节由您决定。

最后,您返回:

sorted(people, key=lambda record: [f(record) for f in function_list]

我会通过使用几个小函数和类来解决这个问题 首先创建一个自定义词典,该词典从其子词典中返回项目(如果有)。

from collections import UserDict
class custom_dict(UserDict):
def __getitem__(self, key):
if key in self.keys():
return super().__getitem__(key)
else:
for inner_key in self.keys():
if isinstance(self[inner_key],type(self)):
try:
return self[inner_key][key]
except Exception:
pass
raise KeyError(f"{key} is not in dictionary")

然后为 JSON 加载函数做一个钩

def as_custom_dict(dct):
return custom_dict(dct)

然后,当您读取 json 时,将as_custom_dict传递给 object_hook 参数。 这允许您将 JSON Load 或 Load 函数创建的字典转换为类似于类的自定义字典。

json_values = json.loads(values,object_hook=as_custom_dict)

接下来创建一个函数,该函数接收字典和他们想要排序的键列表,并返回这些值的元组

def get_item_from_dict(dct,keys_to_find):
x = []
for key in keys_to_find:
x.append(dct[key])
return tuple(x)

最后创建一个函数,该函数将 JSON 值和参数作为列表并返回排序列表

from functools import partial
def order_by(json_values,args):
arguments = args.split(',')    
lambda_to_find = partial(get_item_from_dict,keys_to_find = arguments)
return sorted(json_values,key=lambda_to_find)

总之,这应该允许你想要的。

相关内容

  • 没有找到相关文章

最新更新