【问题标题】:Merging a list of lists such that unique values overwrite nonunique ones?合并列表列表以使唯一值覆盖非唯一值?
【发布时间】:2012-01-15 16:09:51
【问题描述】:

在搜索了很长一段时间并尝试使用列表推导自己想出一个解决方案后,我觉得我完全不知道如何解决以下问题:

给定一个列表:

crazy_list = [a, b]

地点:

a = [['*'], ['*', '34', '*', '*', '*', '*', '*', '*', '*'], ['*', '*', '*', '102', '*', '*', '*', '*', '*'], ['*', '*', '*', '*', '*', '170', '*', '*', '*'], ['*', '*', '*', '*', '*', '*', '*', '238', '*'], ['*']]
b = [['*'], ['*', '*', '*', '102', '*', '*', '*', '*', '*'], ['*', '*', '*', '*', '*', '170', '*', '*', '*'], ['*', '*', '*', '*', '*', '*', '*', '238', '*'], ['*', '34', '*', '*', '*', '*', '*', '*', '*'], ['*']]

我如何优雅地获得一个新的列表列表:

answer = [['*'], ['*', '34', '*', '102', '*', '*', '*', '*', '*'], ['*', '*', '*', '102', '*', '170', '*', '*', '*'], ['*', '*', '*', '*', '*', '170', '*', '238', '*'], ['*', '34', '*', '*', '*', '*', '*', '238', '*'], ['*']]

我将如何概括这种合并来做同样的事情:

[a, b, c, etc...]

(c 及以后的元素和子元素的数量与 'a' 和 'b' 相同,但在不同位置可能具有不同的唯一值)

====================v 到目前为止我能做的一点点 v============= ========

我可以想出一个列表理解,它可以为单个列表做我想做的事情,例如:

c = ['*', '34', '*', '*', '*', '*', '*', '*', '*']
d = ['*', '*', '*', '102', '*', '*', '*', '*', '*']

通过做:

[c[item_indx] if item != '*' else d[item_indx] for item_indx, item in enumerate(c)]

但即使试图概括我到目前为止所拥有的东西也让我非常头疼...... 我可能错误地处理了这个问题。关于如何解决/更好地解决这个问题的一些帮助和/或想法将不胜感激。此外,我不能仅仅放弃'*',因为它们编码重要的时间信息。感谢你的宝贵时间!

【问题讨论】:

  • 合并时同一位置发生冲突怎么办?
  • 如果在合并后已经存在唯一值 aka(数字字符串),则在连续合并中出现在相同位置的任何其他数字不应覆盖。只有 '*' 应该被覆盖。

标签: python list merge multidimensional-array


【解决方案1】:

假设您目前仅在两个列表上取得进展,您可以试试这个。

def merge(a, b):
    return [a[item_indx] if item != '*' else b[item_indx] for item_indx, item in enumerate(a)]

reduce(merge, crazy_list)

reduce 在这种情况下所做的是获取最后一个合并的结果列表,然后对下一个元素执行此操作。所以对于一个疯狂的 [a, b, c, d] 列表,a 和 b 将合并,结果列表将与 c 合并,结果将与 d 合并。等等。一般来说,reduce 以类似的方式应用作为参数提供的函数。

编辑:好的,我明白你现在在说什么了。所以我定义了另一种方法来处理列表列表。我知道您已经接受了答案,但我将把它作为替代方案。

# a and b are lists of lists.
def merge_lists(a, b):
    for i, v in enumerate(a):
        a[i] = merge(a[i], b[i])
    return a

reduce(merge_lists, (c, d))

【讨论】:

  • 嗯,这很有趣,也许如果我再试验一下,我可能会想出一些东西......你发布的解决方案不起作用,因为我只发布了列表理解适用于单个列表,而不适用于列表列表,并且绝对不适用于包含列表列表的列表(哦天哪,这是一种尴尬的说法)
  • 它适用于我的列表列表。你能解释为什么它不起作用吗?对于列表列表,您期望的行为是什么?当我添加另一个列表a(除了您的示例中的cd)时,a = ['69', '*', '*', '*', '*', '*', '*', '*', '*']reduce(merge, [c,d,a]) 产生 ['69', '34', '*', '102', '*', '*', '*', '*', '*']。有错吗?
  • hmm... 所以对于列表列表,如果我执行以下操作:对于 indx,enumerate(a) 中的值:merge(a[indx],b[indx]) 对我有用.
  • 之前我什至不知道reduce,所以感谢您引起我的注意。
【解决方案2】:

首先,让我们弄清楚如何将来自a 的子列表与来自b 的子列表合并:

[x if x != '*' else y for (x, y) in zip(a, b)]

我们从两个列表中获取成对的项目,并为每一对产生一个或另一个元素,并制作一个结果列表。

将其推广到多个列表:好吧,zip 接受 *args,所以这没有问题 - 我们可以从任意数量的列表中创建项目元组。但是我们需要从那个元组中获取“非'*'元素,如果它存在,否则为'*'”。

一种方法是创建元素的set,删除'*',并使用结果中的任何元素(如果非空),否则使用'*'。我们可以从set.pop() 中获取“任何元素”,如果集合为空,则会引发 KeyError。所以我们做一个包装函数然后使用它:

def non_star_if_possible(items):
    try: return set(items).difference('*').pop()
    except KeyError: return '*'

def merge(lists):
    return [non_star_if_possible(items) for items in zip(*lists)]

最后,我们实际上有一个列表列表的列表,我们希望从列表列表中获取列表,逐元素。所以我们应用相同的zip技巧:

def merge_all(data):
    return [merge(lists) for lists in zip(*data)]

如果我们愿意,我们可以将最后两个折叠成一个嵌套列表推导式,但您可能会发现这样更清晰。 :)

【讨论】:

  • 是的,这正是我所需要的。我花了几分钟来理解你在做什么,但现在我已经消化了它是有道理的。我应该补充一点,我也非常感谢您的解释。再次感谢!
【解决方案3】:

你可以试试这个。另一个版本

reduce(lambda a,b:[[x[0] if x[1] == '*' else x[1] for x in p] 
   for p in (zip(x,y) for x,y in zip(a,b)) ],(a,b))

对于多个列表

reduce(lambda a,b:[[x[0] if x[1] == '*' else x[1] for x in p] 
   for p in (zip(x,y) for x,y in zip(a,b)) ],(a,b,c.....))

或者可能是这个

reduce(lambda a,b:[[x[x[1] != '*'] for x in p]
   for p in (zip(x,y) for x,y in zip(a,b)) ],(a,b,c,....))

注意:reduce 将两个 lambda 函数应用于迭代器,并将结果作为下一次调用的第一个参数传递。

lambda 函数zips 对列表和列表中的每个项目都选择非“*”项目,以便在发生冲突时优先考虑第一个列表。

【讨论】:

    【解决方案4】:

    任意数量列表的解决方案:

    from functools import reduce
    
    a = ['a','*','*','*']
    b = ['*','b','*','*']
    c = ['*','*','*','c']
    
    ls = (a, b, c)
    
    def merge(*ls):
        return [f(x) for x in zip(*ls)]
    
    def f(x):
        for item in x:
            if item != '*':
                return item
        return '*'
    
    print(reduce(merge, ls))
    

    这给出了(py3.2,idleX!)

    >>> ================================ RESTART ================================
    >>> 
    ['a', 'b', '*', 'c']
    >>> 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-11
      • 2019-04-03
      • 2017-12-29
      • 2017-08-30
      相关资源
      最近更新 更多