我有"困难" proplists。我需要到达serviceCatalog
并遍历其所有值,直到我得到一个名字= me
J =[{<<"access">>,
{[{<<"token">>,
{[{<<"issued_at">>,<<"2015-09-12T13:27:38.789879">>},
{<<"expires">>,<<"2015-09-12T14:27:38Z">>},
{<<"id">>,<<"fe">>},
{<<"tenant">>,
{[{<<"description">>,null},
{<<"enabled">>,true},
{<<"id">>,<<"01">>},
{<<"name">>,<<"service">>}]}},
{<<"audit_ids">>,[<<"f">>]}]}},
{<<"serviceCatalog">>,
[{[{<<"endpoints">>,
[{[{<<"adminURL">>,<<"http://8.198.99.999:8080">>},
{<<"region">>,<<"RegionOne">>},
{<<"internalURL">>,
<<"http://8.198.99.999:8080/v1/AUTH_01a"...>>},
{<<"id">>,<<"30">>},
{<<"publicURL">>,<<"8.198.99.999:8080/v1/"...>>}]}]},
{<<"endpoints_links">>,[]},
{<<"type">>,<<"object-store">>},
{<<"name">>,<<"my">>}]},
{[{<<"endpoints">>,
[{[{<<"adminURL">>,<<"8.198.99.999:8080/v2.0">>},
{<<"region">>,<<"RegionOne">>},
{<<"internalURL">>,<<"8.198.99.999:8080/v2.0">>},
{<<"id">>,<<"4b3f44a5c64b4bd8b10c376c858b"...>>},
{<<"publicURL">>,<<"8.198.99.999:8080"...>>}]}]},
{<<"endpoints_links">>,[]},
{<<"type">>,<<"identity">>},
{<<"name">>,<<"other">>}]}]},
{<<"user">>,
{[{<<"username">>,<<"my">>},
{<<"roles_links">>,[]},
{<<"id">>,<<"8">>},
{<<"roles">>,[{[{<<"name">>,<<"admin">>}]}]},
{<<"name">>,<<"me">>}]}},
{<<"metadata">>,
{[{<<"is_admin">>,0},
{<<"roles">>,
[<<"e">>]}]}}]}}]
我正在寻找这样做的好方法。 我开始这样的实现(但看起来有更优雅的解决方案):
A1=proplists:get_value(<<"access">>,J).
A=element(1,A1).
B=proplists:get_value(<<"token">>, A).
C=element(1,B).
D=proplists:get_value(<<"serviceCatalog">>, C).
任何建议都很好
答案 0 :(得分:1)
考虑到数据的嵌套程度如何,一种方法是编写一组递归函数子句,这些子句可以找到具有给定键的2元组,而不管其在数据中的级别如何。这是一个例子:
-module(t).
-export([extract/2]).
extract(Key, Data) ->
extract(Key, Data, []).
extract(_Key, [], Acc) ->
Acc;
extract(Key, {Key,_}=KV, Acc) ->
[KV|Acc];
extract(Key, [{Key,_}=KV|Fields], Acc) ->
extract(Key, Fields, [KV|Acc]);
extract(Key, [{_,V}|Fields], Acc) when is_tuple(V); is_list(V) ->
extract(Key, Fields, extract(Key, V, Acc));
extract(Key, Data, Acc) when is_list(Data) ->
lists:foldl(fun(V, FoldAcc) when is_tuple(V); is_list(V) ->
extract(Key, V, FoldAcc);
(_, FoldAcc) ->
FoldAcc
end, Acc, Data);
extract(Key, Data, Acc) when is_tuple(Data) ->
extract(Key, tuple_to_list(Data), Acc).
extract/2
函数只将其参数和空累加器列表传递给extract/3
。 extract/3
的条款如下所述:
Key
作为2元组的第一个元素,并将匹配的元组添加到累加器。Key
作为列表头部的2元组的第一个元素。它补充说,将元组与累加器匹配,并继续从列表的其他元素中提取。Data
是一个与之前的子句已经匹配的列表不匹配的列表时,第五个子句匹配。它折叠列表的所有值,尝试从所有嵌套列表和元组值中提取并忽略所有其他值。Data
是元组时,第六个和最后一个子句匹配;它只是将元组转换为列表并递归调用extract/3
。根据问题中定义的数据J
,我们可以找到一个带有<<"serviceCatalog">>
键的2元组,如下所示:
1> SCs = t:extract(<<"serviceCatalog">>, J).
[{<<"serviceCatalog">>,
[{[{<<"endpoints">>,
[{[{<<"adminURL">>,<<"http://8.198.99.999:8080">>},
{<<"region">>,<<"RegionOne">>},
{<<"internalURL">>,
<<"http://8.198.99.999:8080/v1/AUTH_01a">>},
{<<"id">>,<<"30">>},
{<<"publicURL">>,<<"8.198.99.999:8080/v1/">>}]}]},
{<<"endpoints_links">>,[]},
{<<"type">>,<<"object-store">>},
{<<"name">>,<<"my">>}]},
{[{<<"endpoints">>,
[{[{<<"adminURL">>,<<"8.198.99.999:8080/v2.0">>},
{<<"region">>,<<"RegionOne">>},
{<<"internalURL">>,<<"8.198.99.999:8080/v2.0">>},
{<<"id">>,<<"4b3f44a5c64b4bd8b10c376c858b">>},
{<<"publicURL">>,<<"8.198.99.999:8080">>}]}]},
{<<"endpoints_links">>,[]},
{<<"type">>,<<"identity">>},
{<<"name">>,<<"other">>}]}]}]
提取物找到了我们期望的元素。然后,我们可以将其传递给t:extract/2
,以查看我们找到的元素是否包含我们正在寻找的<<"name">>
元素。我们将使用列表推导来返回元组列表,每个元组包含一个<<"serviceCatalog">>
元素和一个布尔值,指示它是否包含所需的<<"name">>
元素:
2> [{SC,lists:any(fun({_, V}) -> V == <<"me">> end, t:extract(<<"name">>, SC))} || SC <- SCs].
列表推导遍历所有<<"serviceCatalog">>
元素,从每个元素中提取所有<<"name">>
元组并检查值为<<"me">>
的任何元组。
不幸的是,示例数据中的<<"serviceCatalog">>
元素没有问题中描述的{<<"name">>, <<"me">>}
元组,因此上面列表理解结果中的布尔值(结果未显示)是false
。但无论如何,为了遍历列表理解的结果,只保留true
值,但删除布尔值,以便结果列表仅包含匹配的<<"serviceCatalog">>
元素,我们可以折叠列表。首先,让我们做一个折叠函数:
3> Fold = fun({SC, true}, Acc) -> [SC|Acc]; (_, Acc) -> Acc end.
#Fun<erl_eval.12.54118792>
我们的fold函数接受每个{SC, boolean}
元组,将具有true
值的元素添加到累加器列表中,并删除具有false
值的元组。下面我们从前面捕获列表推导的结果,然后将它和折叠函数传递给lists:foldl/3
:
4> SCBools = [{SC,lists:any(fun({_, V}) -> V == <<"me">> end, t:extract(<<"name">>, SC))} || SC <- SCs].
...
5> lists:foldl(Fold, [], SCBools).
[]
同样,由于数据中没有<<"serviceCatalog">>
个元素包含{<<"name">>, <<"me">>}
元组,因此此处的结果列表为空,但如果有的话,我们会在返回的列表中获取它们的值lists:foldl/3
。