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

如何在Elixir中获取两个嵌套结构体的匹配键值对?

提取两个嵌套Elixir结构体的匹配键值对

问题描述

我有两个结构大致相同的嵌套Elixir结构体(示例如下),需要获取二者所有层级中键值完全匹配的部分。此前尝试的for {k, v} <- m1, m2[k] == v, do: {k, v}方法仅适用于普通Map,且只能处理第一层结构,无法适配结构体和嵌套场景。

示例结构体:

%Test.Identities.MetaDetails{
  browser_name: "Chrome",
  browser_version: "108.0.0",
  confidence: %Test.Identities.MetaDetails.Confidence{
    score: 0.9989648033126294
  },
  device: "Other",
  first_seen_at: %Test.Identities.MetaDetails.SeenAt{
    global: ~U[2022-11-25 14:56:09Z],
    subscription: ~U[2022-11-30 14:24:49Z]
  },
  incognito: true,
  ip: "206.84.141.32",
  ip_location: %Test.Identities.MetaDetails.IpLocation{
    accuracy_radius: 5.0,
    city: nil,
    continent: nil,
    country: nil,
    latitude: 33.7233,
    longitude: 73.0435,
    postal_code: "44000",
    subdivisions: [],
    timezone: "Asia/Karachi"
  },
  last_seen_at: %Test.Identities.MetaDetails.SeenAt{
    global: ~U[2022-12-13 11:52:24Z],
    subscription: ~U[2022-12-13 11:52:24Z]
  },
  meta: %Test.Identities.MetaDetails.Meta{
    version: "v1.1.1032+f754139d"
  },
  os: "Linux",
  os_version: nil,
  request_id: "1670933504035.XjTnVT",
  visitor_found: true,
  visitor_id: "nzpkWv3ggKJsgXPx97Z0"
}

解决方案

可以通过递归函数处理结构体的嵌套匹配,同时保留结构体的类型信息:

defmodule StructMatcher do
  @doc """
  递归匹配两个同类型嵌套结构体的键值对,返回包含所有匹配部分的结构体
  """
  def match_structs(s1, s2) when is_struct(s1) and is_struct(s2) and struct(s1) == struct(s2) do
    s1
    |> Map.from_struct()
    |> Map.drop([:__struct__])
    |> Enum.reduce(%{}, fn {k, v}, acc ->
      case {v, Map.get(s2, k)} do
        # 递归处理嵌套的同类型结构体
        {v1, v2} when is_struct(v1) and is_struct(v2) ->
          case match_structs(v1, v2) do
            # 嵌套无匹配结果则跳过
            %{} -> acc
            # 将嵌套匹配结果转为对应结构体类型后加入
            nested -> Map.put(acc, k, struct(struct(v1), nested))
          end
        # 非结构体类型直接判断值是否相等
        {v1, v2} when v1 == v2 ->
          Map.put(acc, k, v1)
        # 不匹配的情况跳过
        _ -> acc
      end
    end)
    # 将匹配结果转为原结构体类型返回
    |> then(fn matched -> struct(struct(s1), matched) end)
  end

  # 非结构体或不同类型结构体返回空Map
  def match_structs(_, _), do: %{}
end

使用示例

假设你有两个待匹配的结构体s1和s2:

# 初始化两个结构体(示例中仅展示部分差异)
s1 = %Test.Identities.MetaDetails{...}
s2 = %Test.Identities.MetaDetails{
  browser_name: "Chrome",
  confidence: %Test.Identities.MetaDetails.Confidence{score: 0.9989648033126294},
  browser_version: "109.0.0", # 与s1不同
  first_seen_at: %Test.Identities.MetaDetails.SeenAt{
    global: ~U[2022-11-25 14:56:09Z], # 与s1相同
    subscription: ~U[2022-12-01 10:00:00Z] # 与s1不同
  }
}

# 获取匹配的键值对
matched_struct = StructMatcher.match_structs(s1, s2)

执行后matched_struct会包含:

  • 顶层的browser_name、confidence
  • first_seen_at下的global字段(因为该子字段值匹配)
  • 其他所有值不匹配的字段会被排除

注意事项

  • 仅处理同类型的结构体,不同模块的结构体不会进行匹配
  • 嵌套结构体如果没有任何匹配的键值对,会被排除在最终结果之外
  • 支持所有Elixir基础类型的直接值匹配(字符串、数字、nil、列表等)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 03:55:17