erlang :从 proplists 中获取键集的值
erlang : get the value for the set of keys from proplists
我有 "difficult" proplists。我需要到达 serviceCatalog
并遍历它的所有值,直到我得到一个具有 name=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).
欢迎大家指教
考虑到数据嵌套的深度,一种方法是编写一组递归函数子句,无论其在数据中的级别如何,都可以找到具有给定键的二元组。这是一个例子:
-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
的子句如下所述工作:
- 第一个子句检查数据是否为空列表,如果是,returns 累加器。
- 第二个子句将所需的
Key
作为二元组的第一个元素进行匹配,并将该匹配的元组添加到累加器中。
- 第三个子句匹配所需的
Key
作为列表头部的二元组的第一个元素。它将匹配的元组添加到累加器并继续从列表的其他元素中提取。
- 第四个子句处理列表头部没有匹配键的二元组的情况,其中元组中的值是列表或元组;如果是这样,它会尝试从值中提取,然后继续从列表的其余部分中提取。
- 当
Data
是一个与前面的子句已经匹配的列表不匹配的列表时,第五个子句匹配。它折叠列表的所有值,尝试从所有嵌套列表和元组值中提取并忽略所有其他值。
- 当
Data
是元组时,第六个也是最后一个子句匹配;它只是将元组转换为列表并递归调用 extract/3
。
给定问题中定义的数据 J
,我们可以找到一个带有 <<"serviceCatalog">>
键的二元组,如下所示:
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">>
元素。我们将使用一个列表理解,即 returns 一个元组列表,每个元组包含一个 <<"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>
我们的折叠函数获取每个 {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
.
我有 "difficult" proplists。我需要到达 serviceCatalog
并遍历它的所有值,直到我得到一个具有 name=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).
欢迎大家指教
考虑到数据嵌套的深度,一种方法是编写一组递归函数子句,无论其在数据中的级别如何,都可以找到具有给定键的二元组。这是一个例子:
-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
的子句如下所述工作:
- 第一个子句检查数据是否为空列表,如果是,returns 累加器。
- 第二个子句将所需的
Key
作为二元组的第一个元素进行匹配,并将该匹配的元组添加到累加器中。 - 第三个子句匹配所需的
Key
作为列表头部的二元组的第一个元素。它将匹配的元组添加到累加器并继续从列表的其他元素中提取。 - 第四个子句处理列表头部没有匹配键的二元组的情况,其中元组中的值是列表或元组;如果是这样,它会尝试从值中提取,然后继续从列表的其余部分中提取。
- 当
Data
是一个与前面的子句已经匹配的列表不匹配的列表时,第五个子句匹配。它折叠列表的所有值,尝试从所有嵌套列表和元组值中提取并忽略所有其他值。 - 当
Data
是元组时,第六个也是最后一个子句匹配;它只是将元组转换为列表并递归调用extract/3
。
给定问题中定义的数据 J
,我们可以找到一个带有 <<"serviceCatalog">>
键的二元组,如下所示:
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">>
元素。我们将使用一个列表理解,即 returns 一个元组列表,每个元组包含一个 <<"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>
我们的折叠函数获取每个 {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
.