使用函数式编程方法对字典按值分组



给定regex.findall()的组字典列表:

[{'short_name': 'f1', 'long_name': 'ae123'}, {'short_name': 'f2', 'long_name': 'ae123'}, {'short_name': 'f3', 'long_name': 'x89zy'}, {'short_name': 'f4', 'long_name': 'bc00'}, {'short_name': 'f5', 'long_name': 'bc00'}]

我想通过'short_name'键对这些字典进行分组,但是我很好奇如何在Python中使用函数式编程技术来做到这一点。

目前为止我的代码是:

def parse_fonts(style):
    sheet = parse_stylesheet(style, skip_comments=True, skip_whitespace=True)
    values_by_long_name = {}
    fonts = map(
        lambda x: FONT_REGEX.search(x.serialize()).groupdict(),
        sheet[1:]
    )
    values_by_long_name = {}
    for font in fonts:
        values_by_long_name.setdefault(
            font['long_name'], []).append(font['short_name'])
    return values_by_long_name
上面的代码返回如下内容:
[{'long_name': 'ae123', 'short_name': ['f1', 'f2']}, {'long_name': 'x89zy', 'short_name': ['f3']}, {'long_name': 'bc00', 'short_name': ['f4', 'f5']}]

如果不使用for循环/列表推导,如果可能的话,在一个函数调用链中,如a(b(c(d(x)))),我如何实现相同的目标?

你的实现可能是我在Python中使用的,它本身并不是非函数的——从某种意义上说,纯函数实现仍然将遵循相同的逻辑:

  • 折叠输入列表,使用字典/映射累加器
  • 对于每个(键,值)对,检查键是否存在于映射
  • 中。
  • 更新映射(即返回一个新映射),如果键存在,则附加值,否则为键
  • 插入一个新的单例[value]

我猜你可以在Pyton中使用itertools.groupby,但它不是很令人满意,因为你仍然需要一个助手来获得相同的输出形状:

import itertools
lst =[{'short_name': 'f1', 'long_name': 'ae123'},
      {'short_name': 'f2', 'long_name': 'ae123'},
      {'short_name': 'f3', 'long_name': 'x89zy'},
      {'short_name': 'f4', 'long_name': 'bc00'},
      {'short_name': 'f5', 'long_name': 'bc00'}]
def regroup(groups):
    """Regroup groupby result."""
    return [{'long_name': key, 'short_name': [g['short_name'] for g in group]}
            for key, group in groups]
g = regroup(itertools.groupby(lst, lambda x: x['long_name']))
print(g)

…outpus:

[{'long_name': 'ae123', 'short_name': ['f1', 'f2']}, {'long_name': 'x89zy', 'short_name': ['f3']}, {'long_name': 'bc00', 'short_name': ['f4', 'f5']}]

在语法层面上,使用函数式语言确实可以更简洁,就像Haskell中的这个例子:

import qualified Data.Map as M

pairs = [("f1", "ae123"), ("f2", "ae123"), ("f3", "x89zy"), ("f4", "bc00"), ("f5", "bc00")]
group :: Ord b => [(a, b)] -> M.Map b [a]
group = M.fromListWith (++) . map swap
  where
    swap(a, b) = (b, [a])

…交互运行时会产生:

*Main M> group pairs
fromList [("ae123",["f2","f1"]),("bc00",["f5","f4"]),("x89zy",["f3"])]

如果您对Python中的函数式编程感兴趣,我将重点讨论使用mypy对类型进行推理并限制副作用的使用,因为语法通常会更冗长。(请注意,列表推导式是非常实用的——相当于map函数,它对列表中的每个值应用一些函数,而不改变其形状。)

最新更新