Преобразовать в список dict / type, список строк, содержащий списки строк? - PullRequest
1 голос
/ 01 марта 2020
a = ['foo', 'bar', ['can', 'haz']]

Нужно применить функцию к каждой паре строк, заменив их, в том числе внутри списков. Например,

f = lambda k,v: {'key': k, 'val': v}

Так что f(a) станет:

[{'key': 'foo', 'val': 'bar'}, [{'key': 'can', 'val': 'haz'}]]

Выше a только 2 измерения, но мне будет интересно k Габаритные размеры. Начал взламывать что-то вместе с boltons.iterutils.remap, прежде чем стало ясно, что замена всех элементов, не включенных в список на каждом уровне иерархии, на dict или другой f не подходит для этого ...

РЕДАКТИРОВАТЬ: Другой пример

# create some random variables, alternative: `locals().update({c: (round(…`
a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z = tuple(
    (round(abs(random()) ** 10, 4)
     if randint(0, 1) % 2 == 0
     else randint(20, 50)
     if randint(0, 1) % 2 == 0
     else ('foo', 'bar', 'can', 'haz', 'bzr')[randint(0, 4)])
           for c in ascii_lowercase)

l1 = [a, b, c, d, e, f, g, [h, i, j, k],
      l, m, n, [o, p, q, r, [s, t, u, v, w, x, y, z], a, b], c, d, e]

g = lambda k,v: {'{}_key'.format(k): k, '{}_val'.format(k): v}

Когда есть пары смежных друг с другом, он должен применить к нему конструктор типа T и присоединиться (у меня есть функция add_to поддержка диктов, списков и т. д.) любого предыдущего, непосредственно смежного, без списка inbetwixt, иначе необработанный скаляр должен быть объединен в список в той же иерархии, в которой он находился ранее. Вот ожидаемый результат g(l1), исключая оценку переменных:

[
    {'a_key': a, 'a_val': b,
     'c_key': c, 'c_val': d,
     'e_key': e, 'e_val': f},
    g,
    [
        {'h_key': h, 'h_val': i,
         'j_key': j, 'j_val': k}
    ],
    {'l_key': l, 'l_val': m},
    n,
    [
        {'o_key': o, 'o_val': p,
         'q_key': q, 'q_val': r},
        [
            {'s_key': s, 's_val': t,
             'u_key': u, 'u_val': v,
             'w_key': w, 'w_val': x,
             'y_key': y, 'y_val': z}
        ],
        {'a_key': a, 'a_val': b}
    ],
    {'c_key': c, 'c_val': d},
    e
]

Ответы [ 3 ]

1 голос
/ 03 марта 2020

Ниже куча беспорядка, но основной алгоритм в solution() не выглядит таким уж плохим. Не могу сказать, что мне нравится sentinel, но ... все остальное было аккуратно.

https://repl.it/@altendky / ChartreuseWeighty Root -10

import functools
import itertools
import random
import string

import attr
import toolz


@attr.s(frozen=True)
class Example:
    source = attr.ib()
    target = attr.ib()
    group_handler = attr.ib()


def random_example():
    a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z = tuple(
        (round(abs(random.random()) ** 10, 4)
        if random.randint(0, 1) % 2 == 0
        else random.randint(20, 50)
        if random.randint(0, 1) % 2 == 0
        else ('foo', 'bar', 'can', 'haz', 'bzr')[random.randint(0, 4)])
            for c in string.ascii_lowercase)

    l1 = [a, b, c, d, e, f, g, [h, i, j, k],
        l, m, n, [o, p, q, r, [s, t, u, v, w, x, y, z], a, b], c, d, e]

    auauughhghhhh = [
        {f'{a}_key': a, f'{a}_val': b,
        f'{c}_key': c, f'{c}_val': d,
        f'{e}_key': e, f'{e}_val': f},
        g,
        [
            {f'{h}_key': h, f'{h}_val': i,
            f'{j}_key': j, f'{j}_val': k}
        ],
        {f'{l}_key': l, f'{l}_val': m},
        n,
        [
            {f'{o}_key': o, f'{o}_val': p,
            f'{q}_key': q, f'{q}_val': r},
            [
                {f'{s}_key': s, f'{s}_val': t,
                f'{u}_key': u, f'{u}_val': v,
                f'{w}_key': w, f'{w}_val': x,
                f'{y}_key': y, f'{y}_val': z}
            ],
            {f'{a}_key': a, f'{a}_val': b}
        ],
        {f'{c}_key': c, f'{c}_val': d},
        e
    ]

    g = lambda k,v: {'{}_key'.format(k): k, '{}_val'.format(k): v}

    return Example(
        source=l1,
        target=auauughhghhhh,
        group_handler=functools.partial(process_group, paired_sequence_handler=lambda s: build_dict_by_update(s, g)),
    )


def process_group(group, paired_sequence_handler):
    processed_group = []

    if len(group) == 0:
        return processed_group

    odd = (len(group) % 2) != 0
    raw_pairs = group[:-1] if odd else group
    pairs = toolz.partition_all(2, raw_pairs)
    result = paired_sequence_handler(pairs)

    processed_group.append(result)

    if odd:
        processed_group.append(group[-1])

    return processed_group


def build_dict_by_update(sequence, pair_handler):
    result = {}
    for pair in sequence:
        result.update(pair_handler(*pair))

    return result


examples = [
    Example(
        source=['foo', 'bar', ['can', 'haz']],
        target=[{'key': 'foo', 'val': 'bar'}, [{'key': 'can', 'val': 'haz'}]],
        group_handler=functools.partial(process_group, paired_sequence_handler=lambda s: build_dict_by_update(s, lambda k,v: {'key': k, 'val': v})),
    ),
    random_example(),
]


def solution(source, group_handler):
    built = []
    group = []

    sentinel = object()

    for value in itertools.chain(source, [sentinel]):
        if not isinstance(value, list) and value is not sentinel:
            group.append(value)
            continue

        built.extend(group_handler(group))
        group = []

        if value is sentinel:
            break

        result = solution(
            source=value,
            group_handler=group_handler,
        )
        built.append(result)

    return built



for example in examples:
    result = solution(
        source=example.source,
        group_handler=example.group_handler,
    )

    succeeded = result == example.target

    print('?', succeeded)

    if not succeeded:
        print('?  ', example.target)
        print('?  ', result)
0 голосов
/ 01 марта 2020

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

a = ['foo', 'bar', ['can', 'haz']]

def f(a):
  itr = iter(a)
  return [f(a) if isinstance(x, list) else {'key': x, 'val': next(itr)} for x in itr]

# >>> f(a)
# [{'key': 'foo', 'val': 'bar'}, [{'key': 'can', 'val': 'haz'}]]

Обновление:

Хорошо, я должен сказать, что это доставило мне небольшую головную боль, но поскольку это интересная проблема и отличная возможность узнать что-то новое, вот оно.
Функция, которая выполняет точно 1 что вы хотели, и так как у вас есть некоторые проблемы с рекурсивными операциями:

С вашей точной функцией и вводом я получаю RecursionError: maximum recursion depth exceeded while calling a Python object

Это делает без рекурсии . Я уверен, что это не окончательное решение, но оно работает:

from functools import reduce
import operator

def traverse(lst):
  stack = [lst]
  res = [[]]

  substack = stack[0]
  subres = res[0]

  depth = 0

  while stack:
    if substack:
      if isinstance(substack[0], list):
        substack = substack[0]
        subres.append([])
        subres = subres[-1]
        depth += 1
      else:
        if len(substack) > 1 and not isinstance(substack[1], list):
            subres.append({'key': substack.pop(0), 'val': substack.pop(0)})
        else:
          subres.append(substack.pop(0))
    else:
        substack = reduce(operator.getitem, [0] * depth, stack)
        subres = reduce(operator.getitem, [-1] * depth, res)
        depth -= 1
        substack.pop(0)

  return res[0]

Live Demo

1 , Я не совсем уверен, как вы должны знать точные предварительные имена для ключей dict в вашем выходном примере, если вы передаете функции только списки списков, поэтому я оставлю это на ваше усмотрение.

0 голосов
/ 01 марта 2020

Вам понадобится рекурсия для поддержки неопределенного количества уровней.

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

a = ['foo', 'bar', ['can', 'haz']]

def f(a):
    k,v,b,*_ = a+[None]
    result = [{"key":k,"val":v}]
    if b: result.append(f(b))
    return result

вывод:

print(f(a))

# [{'key': 'foo', 'val': 'bar'}, [{'key': 'can', 'val': 'haz'}]]


b = ['foo', 'bar', ['can', 'haz', ['boo','yah',['dino','dog']]]]
print(f(b))
[
  {'key': 'foo', 'val': 'bar'},
  [
     {'key': 'can', 'val': 'haz'},
     [
        {'key': 'boo', 'val': 'yah'},
        [
          {'key': 'dino', 'val': 'dog'}
        ]
     ]
  ]
]
...