【问题标题】:Python unflatten list based on second list基于第二个列表的Python展开列表
【发布时间】:2015-12-01 16:57:17
【问题描述】:

我有 2 个 Python 列表:

list_a = [[['Ab'], ['Qr', 'Zr']], [['Gt', 'Mh', 'Nt'], ['Dv', 'Cb']]]
list_b = [['Ab', 'QrB', 'Zr'], ['GtB', 'MhB', 'Nt6B', 'DvB', 'Cb6B5']]

我需要基于list_a 取消展平list_b。我需要:

list_c = [['Ab'], ['QrB', 'Zr'], [['GtB', 'MhB', 'Nt6B'], ['DbB', 'Cb6B5']]]

有没有办法得到这个list_c?

其他信息: 列表将始终被定义为:

  • 来自list_a 的部分字符串将始终在list_b 中找到。例如。对于一个列表中的Gt,第二个列表中将有Gt 或GtB。
  • 每个列表中的条目不能以不同的顺序排列 - 即如果 Qr 在一个列表中位于 Zr 之前,那么它(Qr 或 QrB)必须在第二个列表中位于 Zr 之前。
  • 每个列表最多可以包含 20 个字符串。
  • 每个列表只有唯一的字符串。例如。 Gt 不能在任何列表中出现 2 次或多次。

尝试:

这是我尝试过的:

list_c = [[],[]]
        for ty,iten in enumerate(list_b):
            for q in iten:
                for l_e in list_a:
                    for items in l_e:
                        for t,qr in enumerate(items):
                            if qr in q:
                                list_c[ty].append([q])

这个输出是:

[[['Ab'], ['QrB'], ['Zr']], [['GtB'], ['MhB'], ['Nt6B'], ['DbB'], ['Cb6B5']]]

问题是['QrB'], ['Zr'] 应该组合['QrB','Zr'],就像它们在list_a 中组合一样。

尝试 2:

for ty,iten in enumerate(list_b):
    for q in iten:
        for l_e,m in enumerate(list_a):
            for ss,items in enumerate(m):
                for t,qr in enumerate(items):
                    if qr in q:
                        list_a[l_e][ss][t] = q

这可以工作并产生所需的输出:

[[['Ab'], ['QrB', 'Zr']], [['GtB', 'MhB', 'Nt6B'], ['DvB', 'Cb6B5']]]

但是,它(尝试 2)太长了,我想知道:这似乎不是在 Python 中执行此操作的正确方法。有没有更 Pythonic 的方式来做到这一点?

【问题讨论】:

  • 你能说明 1. 逻辑 2. 你试过什么?
  • 您确定您的示例数据正确吗?不知道如何从[..., 'DvB', 'Cb6B5'] 获得['Mg0', 'Cb605']
  • @achampion,谢谢。修正了问题。
  • 为,为,为,为,永远不是做任何事的方式……
  • 如果在list_b 中找不到来自list_a 的部分字符串怎么办?反之亦然?它们在 2 个列表中的顺序可以不同吗?列表是否像您的示例一样短,或者它们可能很长?是否有一组固定的代码可以从中提取list_a 和list_b 中的字符串?价值观都是独一无二的吗? ... 等等。如果您可以提供更多示例或一些上下文,您可能会得到更好的解决方案。

标签: python list python-2.7


【解决方案1】:

鉴于任务的相当复杂的性质,您的代码看起来不会太长(在列表中的列表中查找列表并根据前两个字母将其与另一个列表中的列表匹配,并将原始值替换为匹配的值保留原始列表的嵌套结构...)

你至少可以消除这样的循环之一:

for sub_a, sub_b in zip(list_a, list_b):
    for inner_a in sub_a:
        for i, a in enumerate(inner_a):
            for b in sub_b:
                if b.startswith(a):
                    inner_a[i] = b

如果您想要一个更通用的解决方案,它可能会像@Tibor 的回答那样涉及递归。

编辑:鉴于您提供的额外信息,您可以递归地处理list_a,用基于list_b 的扁平版本的迭代器的完整版本替换所有短字符串。这利用了字符串在两个列表中以相同顺序出现且没有重复的事实。

def replace_abbreviations(L, full_names):
    for i, item in enumerate(L):
        if isinstance(item, basestring):
            L[i] = full_names.next()
        elif isinstance(item, list):
            replace_abbreviations(item, full_names)

replace_abbreviations(list_a, (item for L in list_b for item in L))

或者,您可以获取两个列表中每个字符串的索引的扁平列表,然后循环遍历这些列表:

def flat_indices(L):
    for i, item in enumerate(L):
        if isinstance(item, list):
            for j, inner_list in flat_indices(item):
                yield (j, inner_list) 
        else:
            yield (i, L)

for (a, i), (b, j) in zip(flat_indices(list_a), flat_indices(list_b)):
    a[i] = b[j]

【讨论】:

  • 看起来 OP 并不关心这些项目实际上是否匹配(例如startswith),只是它被类似地分区(例如list_a 中的子列表的len)。
  • @achampion - 也许吧。需要更清楚地指定问题以提供最佳响应。您的解决方案假定项目已经按正确的顺序排列。
  • 同意!我假设基于 OP 尝试而不是所述问题。
  • 这是你们俩所说的结合:我正在检查来自list_a 的部分字符串是否在来自list_b 的完整字符串中找到 - 如果是这样,那么我需要一个类似的分区。部分字符串出现在开头,因此 startswith() 应该可以工作。但是,根据 septi 在 OP 中的评论,我的主要问题是众多 for 声明。
【解决方案2】:

如果您只关心list_a 中子列表的长度,那么可以将list_a 转换为其子列表长度,然后使用它来分割list_b 的子列表:

# Transform list_a into len of sublists, (generator of generators :)
index_a = ((len(l2) for l2 in l1) for l1 in list_a))  
list_c = []
for flatb, index in zip(list_b, index_a):
    splitb = []
    s = 0
    for i in index:
        splitb.append(flatb[s:s+i])
        s += i
    list_c.append(splitb)

list_c 的值:

[[['Ab'], ['QrB', 'Zr']], [['GtB', 'MhB', 'Nt6B'], ['DvB', 'Cb6B5']]]

【讨论】:

    【解决方案3】:

    这是任意嵌套深度的递归变体。不太漂亮,但应该可以。

    list_a = [[['Ab'], ['Qr', 'Zr']], [['Gt', 'Mh', 'Nt'], ['Dv', 'Cb']]] 
    list_b = [['Ab', 'QrB', 'Zr'], ['GtB', 'MhB', 'Nt6B', 'DvB', 'Cb6B5']]
    
    def flatten(l):
        for el in l:
            if isinstance(el, list):
                for sub in flatten(el):
                    yield sub
            else:
                yield el
    
    def flitten(l1, l2, i):
        result = []
        for j in l1:
            if isinstance(j, list):
                i, res = flitten(j, l2, i)
                result.append(res)
            else:
                result.append(l2[i])
                i += 1
        return i, result
    
    def flutten(l1, l2):
        i, result = flitten(l1, list(flatten(l2)), 0)
        return result
    
    print(flutten(list_a, list_b))
    # prints [[['Ab'], ['QrB', 'Zr']], [['GtB', 'MhB', 'Nt6B'], ['DvB', 'Cb6B5']]]
    

    【讨论】:

      猜你喜欢
      • 2021-04-30
      • 2023-03-23
      • 2015-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多