Найти - Пользователи
Полная версия: поиск и сравнение в списке
Начало » Python для новичков » поиск и сравнение в списке
1 2
bystander
Здравствуйте, только начал осваивать Питон, написал скрипт, изменяющий табличку, но он работает не совсем корректно.
Дано: список d вида
[[ФИО1, [год, дата]], 
[ФИО2, [год, дата]], 
[ФИО1, [год, дата]], 
[ФИО3, [год, дата]]]

Нужно найти элементы списка с уникальными ФИО и добавить к ним все даты из элементов с такими же ФИО, а потом удалить те элементы, из которых бралась дата, чтобы получилось что-то вроде:
[[ФИО1, [год, дата, дата]], 
[ФИО2, [год, дата]], 
[ФИО3, [год, дата]]]

Код:
def diff():
    i = 0
    q = 0
    while i < len(d):    #крутим цикл для каждого ФИО
        while q < len(d):    #крутим цикл для каждой даты
            if d[i][0] == d[i+q][0] and d[i][1][1] != d[i+q][1][1]:    #если одинаковые имена, но разные даты
                d[i][1].append(d[i+q][1][1])    # добавляем дату в подсписок элемента
                del(d[i+q])    #удаляем строку, из которой была взята дата
            q = q+1
        i = i+1
    return d[j]    #возвращаем в список обновлённую стоку
 
j = 0
while j < len(d):
    d[j] = diff()
    j = j+1
 
print(d)
Добавление дат и удаление дубликатов работают для первой строки, ко второй добавляется всего пара значений, с остальными вообще ничего не происходит. Возможно изменяется длинна списка и потому не работает len(d)? Подскажите, что не так с этим кодом, и как сделать лучше (подозреваю, три цикла while - это не самый лучший выбор). В подсознании крутится range()…

Знаю, что есть более простые способы, возможно через словарь, но я хотел бы разобраться именно со сравнением элементов списка.
В ветку пролистал, похожих тем не нашел, по работе и образоанию гуманитарий, так что пожалуйста не кидайтесь тапками
4kpt
Правильно крутится. Самый простой способ - использование xrange (аналог range, но возвращает итератор) и цикла for.

data = [2, 3, 4, 5, 6]
for pos in xrange(len(data)):
    print "data in pos %s = %s" % (pos, data[pos])

В этом случае len() определяется один раз для создания итератора, но если вы меняете список внутри цикла, то в этом случае лучше использовать явный проход по списку, так как len() для базового списка будет неверен через одно удаление, т.е. длина списка станет меньше начальной и когда дело дойдет до позиции последнего элемента (точнее элемента позиция которого больше фактического размера нового списка), то она не будет совпадать с фактической и программа вернет ошибку IndexError, т.е. ошибка индекса (например, вы пытаетесь вызвать 10 элемент в списке из 9).

Для этого случая по списку лучше проходить явно, т.е.

data = [2, 3, 4, 5, 6]
for line in data:
    print line

Такой подход позволяет выбирать элементы из уже существующего списка на каждом шаге. Если элементы будут удалятся в цикле, то просто будет меньше итераций (или шагов).
bystander
Спасибо, с
for line in data:
    line = diff()
заработало быстрее, но всё равно даты нормально собираются только для первой строки. Не подскажете, из-за чего это?
dimy44
Выведите на экран с помощью print значения переменных в нужных вам местах и вы увидете что с ними происходит с каждым шагом. Так и быстрей вникнете что к чему.
4kpt
Или дайте свой код. А то как-то гадать сложновато :) Нужно посмотреть.
bystander
В стартовом посте фактически весь код, не было только чтения файла и создания списка. Вот выкладываю целиком. Всё больше убеждаюсь, что загвоздка именно в длинне цикла.

x = open("D:\\python\\test table.txt", "r")
f = x.read().split('\n')
d = []
for line in f:#тут табличка приводится в вид, описанный выше
    element = line.split('\t') 
    x = element[0]
    y = element[1].split(':') 
    t = [x, y]
    d.append(t)
def diff():
    i = 0 
   #в этом месте print(line) выводит строки по порядку
    q = 0
    while i < len(d):
        while q < len(d):
            #в этом месте print(line) выводит каждую строкустолько раз, сколько задано в обеих циклах
            if d[i][0] == d[i+q][0] and d[i][1][1] != d[i+q][1][1]: 
                line[1].append(d[i+q][1][1])
                del(d[i+q])
                #в этом месте print(line) несколько раз выводит первую строку, каждый раз добавляя к ней новую найденную дату и пару раз раз вторую строку добавляя к ней новую найденную дату
            q = q+1
        i = i+1        
    return line
for line in d:
    line = diff()
print(d)

print(d) выводит список, в котором для первого элемента выполнены условия, описанные в стартовом посте, для воторго элемента собрано несколько дат и дальше в списке встречаются элементы с таким же ФИО, остальные элементы не тронуты.
Ещё одна небольшая проблема: строка почему-то не хочет разделяться по пробелу, при этом split(':') работает.
4kpt
Использование while крайне нежелательно. Я же Вам написал. Напилил код с циклом for. Постарался сделать попроще. Еще проще сложновато. Попытайтесь вникнуть в идею. Первая строка # coding: utf-8 нужна для питона версии 2.Х. Если Вы работаете с 3.Х, то можно ее удалить.

# coding: utf-8
data = [[u"Иванов", [1999, u"Дата 1 - Иванов"]],
        [u"Петров", [1999, u"Дата 1 - Петров"]],
        [u"Клачин", [1999, u"Дата 1 - Клачин"]],
        [u"Клачин", [1999, u"Дата 2 - Клачин"]],
        [u"Петров", [1999, u"Дата 2 - Петров"]],
        [u"Петров", [1999, u"Дата 3 - Петров"]],
        [u"Иванов", [1999, u"Дата 2 - Иванов"]],
        [u"Клачин", [1999, u"Дата 3 - Клачин"]],
        [u"Иванов", [1999, u"Дата 3 - Иванов"]],
        [u"Петров", [1999, u"Дата 4 - Петров"]],
        [u"Иванов", [1999, u"Дата 4 - Иванов"]],
        [u"Петров", [1999, u"Дата 5 - Петров"]],
        [u"Шудрин", [1999, u"Дата 1 - Шудрин"]],
        [u"Клачин", [1999, u"Дата 4 - Клачин"]]]
for fline in data:
    for nline in data[data.index(fline) + 1:]:
        if fline[0] == nline[0]:
            fline[1].append(nline[1][1])
            # Если могут быть полностью одинаковые данные по одному человеку
            #data.pop(data.index(nline))
            # Если полностью идентичных данных быть не может
            data.remove(nline)           
for line in data:
    print repr(line).decode("unicode_escape") # Для 2.Х
    #print(line) # Для 3.Х

По разбору файла не знаю. Нужно увидеть сам файл. Выложите…
sp3
Ежели порядок данных неважен и их много предлагаю померятся п…ми
data = [[u"Иванов", [1999, u"Дата 1 - Иванов"]],
        [u"Клачин", [1999, u"Дата 4 - Клачин"]]]
for x in range(200000):
    name = "ivanov_%s"%(x%30)
    date =  "ivanov_%s"%(x%10)
    data.append([name,[1999,date]]) 
 
from time import time
 
def timer(foo):
    def wrapped(*w):
        t = time()
        out = foo(*w)
        print foo.__name__, time() - t
        return out
    return wrapped
 
 
@timer
def lists(data):
    for fline in data:
        for nline in data[data.index(fline) + 1:]:
            if fline[0] == nline[0]:
                fline[1].append(nline[1][1])
                # Если могут быть полностью одинаковые данные по одному человеку
                #data.pop(data.index(nline))
                # Если полностью идентичных данных быть не может
                data.remove(nline)
    return data
 
 
@timer             
def dicts(data):
    out = {}
    for name,(year,date) in data:
        all_date = out.get(name,[year])
        # только уникальные значения
        if date not in all_date:
            out[name] = all_date
    return out
 
 
newdata = dicts(data)
newdata = lists(data)
4kpt
sp3
Что-то Вы натворили не то :) Посмотрите Ваш и мой результаты. Нужно, чтобы по идентичным фамилиям добавлялись даты во внутренний список, т.е. количество дат должно увеличиваться. Хотя подход интересен…

bystander
Дано: список d вида [[ФИО1, ],
[ФИО2, ],
[ФИО1, ],
[ФИО3, ]]
Нужно найти элементы списка с уникальными ФИО и добавить к ним все даты из элементов с такими же ФИО, а потом удалить те элементы, из которых бралась дата, чтобы получилось что-то вроде:
[[ФИО1, ],
[ФИО2, ],
[ФИО3, ]]
bismigalis
словарь рулит
from collections import defaultdict
d = defaultdict(lambda: ([],[]))
for name, (year, date) in lst:
    d[name][0].append(year)
    d[name][1].append(date)
out = [[name, [year[0]] + date] for name, (year, date) in d.items()]
print(out)
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB