You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Haskell Lens遍历JSON并按指定Key实现索引?

使用Haskell Lens按指定键索引JSON数组并关联子元素

最近我在用Lens处理JSON文档时遇到了这样一个需求:我有一个指向JSON的Lens,比如doc ^? ((key "body").values),目标JSON结构是这样的:

{"body": [{"key": 23, "data": [{"foo": 1}, {"foo": 2}]}]}

我希望用每个body元素里的"key"字段值作为索引,关联其data数组里的foo值,最终通过类似这样的表达式得到结果:

doc ^? key "body" . values . indexWith (key "key") . key "data" . values . key "foo" . withIndex

期望输出是[(23, 1), (23, 2)]。

下面是我的最小可复现代码(MVCE):

#!/usr/bin/env stack
-- stack --resolver lts-11.7 script
{-# LANGUAGE OverloadedStrings #-}
import Control.Lens
import Data.Aeson
import Data.Aeson.Lens

main :: IO ()
main = do
  let doc = object ["body" .= [object ["key" .= (23 :: Int), "data" .= [object ["foo" .= 1], object ["foo" .= 2]]]]]
  -- 这里需要实现indexWith,让下面的表达式能得到期望结果
  let result = doc ^? key "body" . values . indexWith (key "key") . key "data" . values . key "foo" . withIndex
  print result

实现方案

要实现这个需求,我们需要自定义一个带索引的遍历器(IndexedTraversal)indexWith,它的作用是提取每个元素的指定字段值作为索引,让后续的遍历操作能携带这个索引信息。

直接上代码实现:

import Control.Lens.Indexed (IndexedTraversal, indexed)

-- 接收一个用于提取索引的Lens/Getting,返回带索引的遍历器
indexWith :: Getting a s a -> IndexedTraversal a s s
indexWith idx = indexed $ \f s -> f (s ^. idx) s

把这个函数加到MVCE的导入和主函数之间,运行代码就能得到期望的Just [(23,1),(23,2)]了。

替代方案(无需自定义函数)

如果你不想额外定义函数,也可以用现有Lens组合直接写出逻辑:

let result = doc ^? key "body" . values 
            . (\elem -> indexed (\idx _ -> id) (elem ^. key "key") elem) 
            . key "data" . values . key "foo" . withIndex

不过显然自定义indexWith的方式更清晰易读,也方便复用。


逻辑解释

  • indexWith (key "key"):遍历body数组的每个元素,提取其key字段的值作为索引,把元素包装成带索引的遍历目标。
  • key "data" . values:深入每个元素的data数组,遍历其中的每个子元素。
  • key "foo":提取子元素里的foo值。
  • withIndex:把之前提取的key索引和当前的foo值配对,最终形成我们想要的元组列表。

内容的提问来源于stack exchange,提问作者Reactormonk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:47:21