Работая со словарями, как бы вы нашли / отследили дубликаты GUID в этом списке? - PullRequest
0 голосов
/ 10 декабря 2011

Я много работаю с XML-файлами WiX, и почти каждый объект в WiX требует GUID.Чтобы избежать ошибок копирования и вставки, я начал сортировать и отображать все повторяющиеся идентификаторы GUID с помощью такого списка (созданного с помощью find и egrep):

./A2.Spam.EggsMgrSvc/__A2.Spam.EggsMgrSvc.wixproj:3A206536-FBCC-4911-AF2B-CBCD76E2C23E
./A2.Spam.TrojanBunnies/Files/Files.wxs:1F372E8A-95B9-49AC-84A6-998E7F5B0689
./A2.Spam.TrojanBunnies/Files/Files.wxs:4BB4FBAD-032A-4FBA-8B81-8AA2876E6765
./A2.Spam.TrojanBunnies/Files/File1.wxs:E289D834-4421-4DCE-B0A8-94C09978058A
./A2.Spam.TrojanBunnies/Files/Files.wxs:083863F1-70DE-11D0-BD40-00A0C911CE86
./A2.Spam.TrojanBunnies/Files/File1.wxs:E289D834-4421-4DCE-B0A8-94C09978058A
./A2.Spam.TrojanBunnies/Files/Files.wxs:083863F1-70DE-11D0-BD40-00A0C911CE86
./A2.Spam.TrojanBunnies/Files/File2.wxs:E289D834-4421-4DCE-B0A8-94C09978058A

вформат выглядит следующим образом:

  3 E289D834-4421-4DCE-B0A8-94C09978058A
       2 ./A2.Spam.TrojanBunnies/Files/File1.wxs
       1 ./A2.Spam.TrojanBunnies/Files/File2.wxs
  2 083863F1-70DE-11D0-BD40-00A0C911CE86
       2 ./A2.Spam.TrojanBunnies/Files/Files.wxs

Общее количество вхождений GUID подсчитывается рядом с GUID, затем количество вхождений этого GUID подсчитывается в каждом файле.

I 'Мы придумаем следующий скрипт (который выдает вышеуказанный вывод).Я все еще новичок в Python и действительно пытаюсь понять словари и их практическое использование.Правильно ли было использовать вложенные словари?Я выбрал словари, потому что думал, что это самый простой способ добавлять / отслеживать уникальные записи.Хотя использование синтаксиса, подобного parent_dict['child_dict_key']['value_key'], кажется немного странным, как будто я мог бы использовать items() или другие итерируемые методы / методы:

#!/usr/bin/env python

guids = {}
f_and_g = open( 'files-and-guids.txt', 'r')

for fg in f_and_g.readlines():
    fname, guid = map( str.strip, fg.split(':') )

    if guid not in guids:
        guids[guid] = { 'count': 1, 'files': {} }
    else:
        guids[guid]['count'] += 1

    ## Count how many times a GUID was used in a given file
    if fname not in guids[guid]['files']:
        guids[guid]['files'][fname]  = 1
    else:
        guids[guid]['files'][fname] += 1

## Sort by total count for a given GUID
for guid in sorted( guids, key=lambda x:guids[x]['count'], reverse=True):
    ## Skip printing if count is below threshold
    if guids[guid]['count'] < 2:
        continue
    guid_dict = guids[guid]
    print '{:>3} {}'.format( guid_dict['count'], guid )
    ## Sort by filename counts
    for fname in sorted( guid_dict['files'],
                         key=lambda x: guid_dict['files'][x], reverse=True ):
        fname_cnt = guid_dict['files'][fname]
        print '{:>8} {}'.format( fname_cnt, fname)

Ответы [ 3 ]

2 голосов
/ 10 декабря 2011

Еще один вариант:

#!/usr/bin/env python
import fileinput
from collections import defaultdict, Counter

# count guids
perfile = defaultdict(Counter)
total = Counter()
for line in fileinput.input():
    fname, guid = map(str.strip, line.split(':'))
    perfile[guid][fname] += 1
    total[guid] += 1

# print most common guid first
for guid, count in total.most_common():
    if count < 2: continue # skip printing if count is below threshold
    print '{:>3} {}'.format(count, guid)
    # sorting by filename counts
    for fname, fname_cnt in perfile[guid].most_common():
        print '{:>8} {}'.format(fname_cnt, fname)

Пример

$ python2.7 count-guid.py  input 
  3 E289D834-4421-4DCE-B0A8-94C09978058A
       2 ./A2.Spam.TrojanBunnies/Files/File1.wxs
       1 ./A2.Spam.TrojanBunnies/Files/File2.wxs
  2 083863F1-70DE-11D0-BD40-00A0C911CE86
       2 ./A2.Spam.TrojanBunnies/Files/Files.wxs

Не задумывайтесь над этим, если сценарий понятен и он работает для вас.

2 голосов
/ 10 декабря 2011

Я бы сделал это примерно так, хотя на самом деле я не тестировал этот код:

#!/usr/bin/env python

import collections
import operator

guids = collections.defaultdict(collections.Counter)
f_and_g = open('files-and-guids.txt', 'r')

for fg in f_and_g:
    fname, guid = map(str.strip, fg.split(':'))

    guids[guid][fname] += 1

## Sort by total count for a given GUID

guids_counts_totals = [(guids, counts, sum(counts.itervalues()))
                       for guids, counts
                       in guids.iteritems()]

guids_counts_totals_sorted = sorted(guids_counts_totals,
                                    key=operator.itemgetter(2),
                                    reverse=True)

for guid, counts, total in guids_counts_totals_sorted:
    ## Skip printing if count is below threshold
    if total < 2:
        continue

    print '{:>3} {}'.format(total, guid)

    ## Sorting by filename counts
    fnames_counts_sorted = sorted(counts.iteritems(),
                                  key=operator.itemgetter(1), reverse=True)
    for fname, count in fnames_counts_sorted:
        print '{:>8} {}'.format(count, fname)

Некоторые изменения здесь:

  • Использование collections.defaultdict иcollections.Counter вместо неоднократной проверки наличия ключа и установки его в 1, если его там нет
  • Не дублировать данные, сохраняя счет для каждого GUID и для каждого имени файла.Вы можете просто суммировать все значения для каждого имени файла GUID
  • Сортировка и итерация по dict.itervalues() вместо простого использования ключей и последующего поиска их значений
  • Использование operator.itemgetter() вместоlambda выражений
  • Интервалы в соответствии с PEP 8
0 голосов
/ 15 декабря 2011

Основываясь на некоторых ответах, которые я попробовал еще раз, уклоняясь от любых других библиотек ради простой попытки усложнить мою жизнь:

def MyCounter(l):
    d = dict()
    for i in l:
        if i not in d:
            d[i] = 1
        else:
            d[i] += 1
    return d

def main():
    guids = dict()
    f_and_g = open('files-and-guids.txt', 'r')
    for fg in f_and_g.readlines():
        fname, guid = map(str.strip, fg.split(':'))
        if guid not in guids:
            guids[guid] = [fname]
        else:
            guids[guid] += [fname]

    ## Sort by total count for a given GUID
    for guid in sorted(guids, key=lambda guid: len(guids[guid]), reverse=True):
        ## Skip printing if count is below threshold
        if len(guids[guid]) < 2: continue
        guid_list = guids[guid]
        print '{:>3} {}'.format( len(guid_list), guid )
        ## Sort by filename counts
        counts = MyCounter(guid_list)
        for fname, fname_cnt in sorted(counts.iteritems(), key=lambda x:x[1],
                                   reverse=True):
            print '{:>8} {}'.format(fname_cnt, fname)
...