用最Pythonic的方法按重复元素拆分数组

14 投票
11 回答
2503 浏览
提问于 2025-04-17 08:28

我有一个物品列表,想要根据一个分隔符来拆分这个列表。我希望所有的分隔符都被去掉,并且在分隔符出现两次的时候进行拆分。例如,如果分隔符是'X',那么下面这个列表:

['a', 'b', 'X', 'X', 'c', 'd', 'X', 'X', 'f', 'X', 'g']

会变成:

[['a', 'b'], ['c', 'd'], ['f', 'g']]

注意最后一组没有被拆分。

我写了一些不太好看的代码来实现这个功能,但我相信一定有更好的方法。如果你能设置一个任意长度的分隔符(也就是说,在看到N个分隔符后进行拆分),那就更棒了。

11 个回答

4

使用生成器函数来保持你在列表中迭代器的状态,以及到目前为止看到的分隔符字符的数量:

l = ['a', 'b', 'X', 'X', 'c', 'd', 'X', 'X', 'f', 'X', 'g'] 

def splitOn(ll, x, n):
    cur = []
    splitcount = 0
    for c in ll:
        if c == x:
            splitcount += 1
            if splitcount == n:
                yield cur
                cur = []
                splitcount = 0
        else:
            cur.append(c)
            splitcount = 0
    yield cur

print list(splitOn(l, 'X', 2))
print list(splitOn(l, 'X', 1))
print list(splitOn(l, 'X', 3))

l += ['X','X']
print list(splitOn(l, 'X', 2))
print list(splitOn(l, 'X', 1))
print list(splitOn(l, 'X', 3))

输出结果是:

[['a', 'b'], ['c', 'd'], ['f', 'g']]
[['a', 'b'], [], ['c', 'd'], [], ['f'], ['g']]
[['a', 'b', 'c', 'd', 'f', 'g']]
[['a', 'b'], ['c', 'd'], ['f', 'g'], []]
[['a', 'b'], [], ['c', 'd'], [], ['f'], ['g'], [], []]
[['a', 'b', 'c', 'd', 'f', 'g']]

编辑:我也很喜欢使用groupby,这里是我的尝试:

from itertools import groupby
def splitOn(ll, x, n):
    cur = []
    for isdelim,grp in groupby(ll, key=lambda c:c==x):
        if isdelim:
            nn = sum(1 for c in grp)
            while nn >= n:
                yield cur
                cur = []
                nn -= n
        else:
            cur.extend(grp)
    yield cur

这和我之前的回答差不多,只是让groupby来处理输入列表的迭代,创建匹配分隔符和不匹配分隔符的字符组。那些不匹配的字符会被添加到当前元素中,而匹配的字符组则负责分隔出新的元素。对于很长的列表来说,这种方法可能更高效,因为groupby的处理是在C语言中完成的,并且只需遍历列表一次。

4

这里有一种方法可以使用 itertools.groupby() 来实现:

import itertools

class MultiDelimiterKeyCallable(object):
    def __init__(self, delimiter, num_wanted=1):
        self.delimiter = delimiter
        self.num_wanted = num_wanted

        self.num_found = 0

    def __call__(self, value):
        if value == self.delimiter:
            self.num_found += 1
            if self.num_found >= self.num_wanted:
                self.num_found = 0
                return True
        else:
            self.num_found = 0

def split_multi_delimiter(items, delimiter, num_wanted):
    keyfunc = MultiDelimiterKeyCallable(delimiter, num_wanted)

    return (list(item
                 for item in group
                 if item != delimiter)
            for key, group in itertools.groupby(items, keyfunc)
            if not key)

items = ['a', 'b', 'X', 'X', 'c', 'd', 'X', 'X', 'f', 'X', 'g']

print list(split_multi_delimiter(items, "X", 2))

我得说,cobbal 的解决方案要简单得多,但结果是一样的。

13

我觉得这个问题可能没有什么特别优雅的解决办法(当然我希望我错了),所以我建议用一种简单直接的方法:

def nSplit(lst, delim, count=2):
    output = [[]]
    delimCount = 0
    for item in lst:
        if item == delim:
            delimCount += 1
        elif delimCount >= count:
            output.append([item])
            delimCount = 0
        else:
            output[-1].append(item)
            delimCount = 0
    return output

 

>>> nSplit(['a', 'b', 'X', 'X', 'c', 'd', 'X', 'X', 'f', 'X', 'g'], 'X', 2)
[['a', 'b'], ['c', 'd'], ['f', 'g']]

撰写回答