Извлечение уникальных элементов из списка сопоставлений

StackOverflow https://stackoverflow.com/questions/186131

  •  06-07-2019
  •  | 
  •  

Вопрос

Это интересная проблема, которая ищет наиболее питоническое решение.Предположим, у меня есть список отображений {'id': id, 'url': url}.Некоторый idВ списке есть дубликаты, и я хочу создать новый список, удалив все дубликаты.Я придумал следующую функцию:

def unique_mapping(map):
    d = {}
    for res in map:
        d[res['id']] = res['url']

    return [{'id': id, 'url': d[id]} for id in d]

Полагаю, это довольно эффективно.Но есть ли «более питонический» способ?Или, возможно, более эффективный способ?

Это было полезно?

Решение

Ваш пример можно немного переписать, чтобы построить первый словарь с использованием выражения-генератора и исключить необходимость построения других отображений.Просто повторно используйте старые:

def unique_mapping(mappings):
    return dict((m['id'], m) for m in mappings).values()

Несмотря на то, что это получилось в виде однострочника, я все же думаю, что это вполне читабельно.

При использовании вашего и моего оригинального решения следует учитывать две вещи:

  • товары не всегда будут возвращены в том же порядке, в котором они были изначально
  • более поздняя запись перезапишет предыдущие записи с тем же идентификатором

Если вы не против, то предлагаю решение выше.В другом случае эта функция сохраняет порядок и обрабатывает первые обнаруженные идентификаторы с приоритетом:

def unique_mapping(mappings):
    addedIds = set()
    for m in mappings:
        mId = m['id']
        if mId not in addedIds:
            addedIds.add(mId)
            yield m

Возможно, вам придется позвонить с помощью list(unique_mappings(mappings)) если вам нужен список, а не генератор.

Другие советы

Есть несколько вещей, которые вы могли бы улучшить.

  • Вы выполняете два цикла: один над исходным словарем, а затем снова над результирующим словарем.Вместо этого вы можете добиться результатов за один шаг.

  • Вы можете перейти на использование генератора, чтобы не создавать весь список заранее.(Используйте list(unique_mapping(items)) для преобразования в полный список, если вам это нужно)

  • Нет необходимости сохранять значение при проверке дубликатов, вместо этого вы можете использовать набор.

  • Вы воссоздаете словарь для каждого элемента, а не возвращаете оригинал.Это действительно может быть необходимо (например.вы их изменяете и не хотите трогать оригинал), но если нет, то эффективнее использовать уже созданные словари.

Вот реализация:

def unique_mapping(items):
    s = set()
    for res in items:
        if res['id'] not in s:
            yield res
            s.add(res['id'])

Я думаю, что это можно сделать еще проще. Словари не допускают дублирования ключей. Сделайте ваш список отображений в словарь отображений. Это удалит дубликаты.

>>> someListOfDicts= [
    {'url': 'http://a', 'id': 'a'}, 
    {'url': 'http://b', 'id': 'b'}, 
    {'url': 'http://c', 'id': 'a'}]

>>> dict( [(x['id'],x) for x in someListOfDicts ] ).values()

[{'url': 'http://c', 'id': 'a'}, {'url': 'http://b', 'id': 'b'}]
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top