将dict的一部分与dupli合并

2024-03-28 10:11:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我是一个完整的初学者与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:我刚刚发现为什么维亚坦和帕特里克·阿特纳的解决方案不起作用。只有在事先对公交车列表进行排序的情况下,它们才起作用。所以我猜groupby需要dicts与之相邻。你知道吗


Tags: keynameingetdateiftimevalue
2条回答

一种方法是按它们的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': ''}]

我的解决方案是:我们使用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": ""
    }
]

相关问题 更多 >