2015-06-25 11 views
2

Я ищу способ присоединиться к итераторам python, например itertools.izip_longest(), но я хотел бы присоединиться к элементам, которые имеют один и тот же «ключ» (как определено параметром) и вывод None, когда ключ не существует на всех итераторах. Я предполагаю, что итераторы отсортированы по возрастанию на «ключ».Присоединиться к итераторам python по ключу

Пример:

iter1 = iter((1, 3, 4, 9)) 
iter2 = iter((3, 5, 6)) 
iter3 = iter((1, 3, 10)) 

zipjoiner(iter1, iter2, iter3) 

должны дать:

iter(((1, None, 1), (3, 3, 3), (4, None, None), (None, 5, None), (None, 6, None), (9, None, None), (None, None, 10))) 

(в данном случае ключ является удостоверением по умолчанию lambda x: x)

Я попытался изменить izip_longest() реализации, которые содержатся в python documentation и он работает (по крайней мере, на моем примере), но я ищу более элегантное решение. Любая идея?

Это мой код:

def zipjoiner(*args, **kwds): 
    # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- 
    fillvalue = kwds.get('fillvalue') 
    key = kwds.get('key', lambda x: x) 
    counter = [len(args) - 1] 
    def sentinel(): 
     if not counter[0]: 
      raise ZipExhausted 
     counter[0] -= 1 
     yield fillvalue 
    fillers = itertools.repeat(fillvalue) 
    iterators = [itertools.chain(it, sentinel(), fillers) for it in args] 

    def getkey(x): 
     return None if x is None else key(x) 

    try: 
     while iterators: 
      elements = tuple(map(next, iterators)) 
      keys = tuple(map(getkey, elements)) 
      minkey = min(_ for _ in keys if not _ is None) 
      while not all(k == minkey for k in keys): 
       yield tuple(map(lambda (k, v): v if k == minkey else None, zip(keys, elements))) 
       elements = tuple(map(lambda (k, it, v): it.next() if k == minkey else v, zip(keys, iterators, elements))) 
       keys = tuple(map(getkey, elements)) 
       minkey = min(_ for _ in keys if not _ is None) 
      yield elements 

    except ZipExhausted: 
     pass 

ответ

0

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

def join_iterators(*iterators): 
    sets = [] 
    for iterator in iterators: 
     sets.append(set(iterator)) 

    values = set(itertools.chain(*iterators)) 
    get_value_or_none = lambda value, s: value if value in s else None 
    for value in sorted(values): 
     yield tuple(get_value_or_none(value, s) for s in sets) 

Это не решает ваши ключевые функции, но я думаю, что вы понять, как применить это;)

0

Хотя вы могли бы сделать что-то более читаемое, я не думаю, что вы могли бы сделать что-то более эффективное.

С точки зрения читабельности я хотел бы изменить заголовок и первые несколько строк:

def zipjoiner(*iters, fillvalue=None, key=lambda x: x): 
    # drop first two lines dealing with fillvalue and key 

, поскольку нет никаких оснований, чтобы иметь дело с **kwds.