我是使用Python的初学者,所以请多多包涵。
我有一些像这样的字典:
list = [
{
"name": "Bus 60",
"direction": "City",
"timeLeft": "1",
"timeNext": ""
},
{
"name": "Bus 60",
"direction": "City",
"timeLeft": "3",
"timeNext": ""
},
{
"name": "Bus 1",
"direction": "Some Place",
"timeLeft": "15",
"timeNext": ""
},
{
"name": "Bus 1",
"direction": "Some Place",
"timeLeft": "30",
"timeNext": ""
},
{
"name": "Bus 1",
"direction": "That other place",
"timeLeft": "5",
"timeNext": ""
},
]
我希望将基于“名称”和“方向”的这两个合并,如下所示:
new_list = [
{
"name": "Bus 60",
"direction": "City",
"timeLeft": "1",
"timeNext": "3"
},
{
"name": "Bus 1",
"direction": "Some Place",
"timeLeft": "15",
"timeNext": "30"
},
{
"name": "Bus 1",
"direction": "That other place",
"timeLeft": "5",
"timeNext": ""
},
]
如何实现这一目标,并同时了解其实际工作原理? 我曾经尝试过很多带有循环的解决方案,但是它们最终都会导致大量重复或错误合并。
编辑:每个名称和方向不得超过一个副本。
编辑2:这是我的完整方法:
@APP.route('/api/vasttrafik/departures', methods=['POST'])
def get_departures():
""" Departures """
APP.logger.info('get_departures():')
data = request.get_json()
id_number = data['id']
current_date = date.today().strftime('%Y-%m-%d')
current_time = datetime.now().strftime('%H:%M')
# time_span = data['']
access_token = request.headers['access_token']
url = 'https://api.vasttrafik.se/bin/rest.exe/v2/departureBoard?id='\
+ id_number + '&date=' + current_date + '&time=' + current_time +\
'&format=json&timeSpan=90&maxDeparturesPerLine=2&needJourneyDetail=0'
headers = {'Authorization': 'Bearer ' + access_token}
req = requests.get(url, headers=headers)
json = req.json()
departure_board = json['DepartureBoard']
if 'error' in departure_board:
raise NotFoundException('Did not find anything')
departures = departure_board['Departure']
def departures_model(item):
def get_key_value(key):
return item[key] if key in item else ''
is_live = 'rtTime' in item
if is_live:
current_time = get_key_value('rtTime')
current_date = get_key_value('rtDate')
else:
current_time = get_key_value('time')
current_date = get_key_value('date')
direction = get_key_value('direction')
via = ''
if 'via' in direction:
direction, via = direction.split('via')
time_departure = datetime.strptime(current_date + ' ' + current_time, '%Y-%m-%d %H:%M')
time_now = datetime.now()
diff = time_departure - time_now
if time_now >= time_departure:
minutes_left = 0
else:
minutes_left = math.floor(((diff).seconds) / 60)
clock_left = item['rtTime'] if is_live else item['time']
return dict({
'accessibility': get_key_value('accessibility'),
'bgColor': get_key_value('bgColor'),
'clockLeft': clock_left,
'clockNext': '',
'timeLeft': int(minutes_left),
'timeNext': '',
'direction': direction.strip(),
'via': 'via ' + via.strip() if via != '' else via,
'name': get_key_value('name'),
'sname': get_key_value('sname'),
'type': get_key_value('type'),
'time': get_key_value('time'),
'date': get_key_value('date'),
'journeyid': get_key_value('journeyid'),
'track': get_key_value('track'),
'fgColor': get_key_value('fgColor'),
'isLive': is_live,
'night': 'night' in item,
})
mapped_departures = list(map(departures_model, departures))
def key(bus):
return bus["name"], bus["direction"]
def merge_busses(ls):
for (name, direction), busses in groupby(ls, key):
busses = list(busses)
times = [bus["timeLeft"] for bus in busses]
yield {
**busses[0],
"timeLeft": min(times, key=int),
"timeNext": max(times, key=int),
}
merge_departures = list(merge_busses(mapped_departures))
return jsonify({
'departures': merge_departures,
})
编辑3:我刚刚发现为什么L3viathan和Patrick Artner的解决方案不起作用。仅在事先对公交车列表进行排序的情况下,它们才起作用。因此,我认为groupby需要将命令与之相邻。
答案 0 :(得分:3)
这是我的解决方案:我们使用itertools.groupby
按名称/名称组合对总线进行分组,然后生成字典,其中timeLeft
是这些总线中分钟数最少的字典,而timeNext
是一个空字符串(如果我们只看到了一辆公交车),或者是这些公交车中最多的分钟数。
from itertools import groupby
def key(bus):
return bus["name"], bus["direction"]
def merge_busses(ls):
for (name, direction), busses in groupby(sorted(ls, key=key), key):
busses = list(busses)
times = [bus["timeLeft"] for bus in busses]
yield {
**busses[0],
"timeLeft": min(times, key=int),
"timeNext": "" if len(times) == 1 else max(times, key=int),
}
像这样使用它:
new_list = list(merge_busses(mylist))
在您的示例中使用,将产生:
[
{
"name": "Bus 60",
"direction": "City",
"timeLeft": "1",
"timeNext": "3"
},
{
"name": "Bus 1",
"direction": "Some Place",
"timeLeft": "15",
"timeNext": "30"
},
{
"name": "Bus 1",
"direction": "That other place",
"timeLeft": "5",
"timeNext": ""
}
]
答案 1 :(得分:3)
一种方法是将所有总线按name
&& direction
分组。然后合并数据,确保“较早”时间在'timeLeft'
中,而较晚时间在'timeNext'
中:
Doku:itertools.groupby
busses = [
{
"name": "Bus 60",
"direction": "City",
"timeLeft": "1",
"timeNext": ""
},
{
"name": "Bus 60",
"direction": "City",
"timeLeft": "3",
"timeNext": ""
},
{
"name": "Bus 21",
"direction": "City",
"timeLeft": "5",
"timeNext": ""
},
]
from itertools import groupby
def mergeBusses(listOfBussesDict):
sortList = sorted(listOfBussesDict, key=lambda x: (x["name"],x["direction"]))
# we use name + direction as key for the grouping
merged = groupby(sortList, lambda x: (x["name"],x["direction"]))
# you might consider cleaning up the keys that are used:
# merged = groupby(sortList, lambda x: (x["name"].strip(),x["direction"].strip()))
# if your source data is bad.
for k,g in merged:
sameBus = list(g)
# now we take all times and sort them by their integer value to
# update the correct slots in the dictionary
times = sorted([x["timeLeft"] for x in sameBus],key= lambda y:int(y))
if len(times)>1:
# we only need to do this if the grouping has > 1 bus, in that
# case we use the basedata of the first bus and adjust the times
sameBus[0]["timeLeft"] = times[0]
sameBus[0]["timeNext"] = times[1]
# we just yield the first bus from the group which now has correct times
yield sameBus[0]
# need to make a list from our generator result
mergedOnes = list(mergeBusses(busses))
print(mergedOnes)
输出:
[{'name': 'Bus 60', 'direction': 'City', 'timeLeft': '1', 'timeNext': '3'},
{'name': 'Bus 21', 'direction': 'City', 'timeLeft': '5', 'timeNext': ''}]
您编辑的示例将导致:
[{'name': 'Bus 60', 'direction': 'City', 'timeLeft': '1', 'timeNext': '3'},
{'name': 'Bus 1', 'direction': 'Some Place', 'timeLeft': '15', 'timeNext': '30'},
{'name': 'Bus 1', 'direction': 'That other place', 'timeLeft': '5', 'timeNext': ''}]