从头开始实现itertools.groupby

时间:2019-07-19 04:57:51

标签: itertools

我试图实现一个接受

的功能groupify(S)
string S = 'aabbbbccca'

并返回

[('a', 2), ('b', 4), ('c', 3), ('a', 1)]

我知道我们可以使用Python itertools.groupby来做到这一点。但是,我正在尝试从头开始实施。我可以考虑使用deque从左侧弹出一个项目,但不知道如何编写确切的代码来实现(例如,如何获取[('a',2),('b', 4),('c',3),('a',1)],使用双端队列从输入S ='aabbbbccca')。我感谢任何人的帮助。

更新:我自己想出了一个解决方案。我一直在与deque.popleft()挣扎,后来发现我需要使用一个持有人来保存弹出的所有内容(即下面的弹出变量)。

# implement groubpy by scratch
# example: given a string 'aabbbbccca' return [('a', 2), ('b', 4), ('c', 3), ('a', 1)]
from collections import deque
import unittest
from itertools import groupby

class Solution:
    def implement_groupby(self, s):
        pair = []
        m = deque(s)
        char = s[0]
        count = 0
        while m:
            popped = m.popleft()
            if char == popped:
                count += 1
            else:
                pair.append((char, count))
                char = popped
                count = 1

        pair.append((char, count))
        return pair

    def use_built_in_groupby(self, s):
        pair = []
        for key, group in groupby(s):
            pair.append((key, len(list(group))))

        return pair

class Test(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.solution = Solution()
        self.s1 = 'aabbbbcccaa'
        self.s2 = 'abc'

    def expectEqual(self, first, second):
        with self.subTest():
            self.assertEqual(first, second)

    def testImplementGroupby(self):
        self.expectEqual(self.solution.implement_groupby(self.s1), [('a', 2), ('b', 4), ('c', 3), ('a', 2)])
        self.expectEqual(self.solution.implement_groupby(self.s2), [('a', 1), ('b', 1), ('c', 1)])

    def testBuildInGroupby(self):
        self.expectEqual(self.solution.use_built_in_groupby(self.s1), [('a', 2), ('b', 4), ('c', 3), ('a', 2)])
        self.expectEqual(self.solution.use_built_in_groupby(self.s2), [('a', 1), ('b', 1), ('c', 1)])

if __name__ == '__main__':
    unittest.main()   


3 个答案:

答案 0 :(得分:1)

您实际上是在尝试实现压缩字符串的run length encodong

这是不使用collections.deque的纯Python实现。

给出

itertools recipesunique_justseen的变体:

def unique_justseen_(s):
    """Return unique characters as seen from a string.

    Examples
    --------
    >>> s = "aabbbbccca"
    >>> unique_justseen_(s)
    'abca'

    """
    hold, result = "", ""

    for x in s:
        if x == hold:
            continue
        hold = x
        result += x

    return result

代码

def run_length_encode(s):
    """Return a list of compressed strings."""
    result = []

    for x in unique_justseen_(s):
        i = 0
        while (i < len(s)) and (x == s[i]):
            i += 1
            #print(x, s, i)
        s = s[i:]        
        result.append((x, i))

    return result

演示

run_length_encode("aabbbbccca")
# [('a', 2), ('b', 4), ('c', 3), ('a', 1)]

详细信息

我们通过辅助函数"abca"迭代唯一的分组字符unique_justseen_。内部while循环计数观察到的具有唯一字符的匹配项。 s = s[i:]通过消除每组重复的字符来“收缩”初始字符串的副本(您可以通过取消注释print语句来观察这一点)。唯一字符与最终的递增值i打包在一起,并附加到结果列表中。

答案 1 :(得分:0)

您可以按以下方式使用双端队列从列表中弹出元素

from collections import deque
d=deque(['4', '3', '2', '1'])
d.pop()
'1'
d.popleft()
'4'

以您的情况

d=deque([('a', 2), ('b', 4), ('c', 3)])
d.popleft()
print(d)

输出:

deque([('b', 4), ('c', 3)])

答案 2 :(得分:0)

这是我的解决方法。

type LessThan1000 = LessThan<1000> // ⏳
/* type LessThan1000 = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 768 
 | 384 | 640 | 896 | 192 | 320 | 576 | 832 | 448 | 704 | 960 | 96 | 160 | 288 | 544
 | 800 | 416 | 672 | 928 | 224 | 352 | 608 | 864 | 480 | ... 964 more ... | 767 */

sample =“ aabbbbccca”

print(groupify(sample))

输出:{'a':3,'b':4,'c':3}