【问题标题】:erlang : get the value for the set of keys from proplistserlang : 从 proplists 中获取键集的值
【发布时间】:2015-12-08 23:43:14
【问题描述】:

我有“困难”的道具列表。我需要到达 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).

欢迎提出建议

【问题讨论】:

  • 请说明问题;你是说你想用一个键&lt;&lt;"serviceCatalog"&gt;&gt; 来查找这个数据中的所有元组,它的值在其中的某个位置包含一个值为{&lt;&lt;"name"&gt;&gt;, &lt;&lt;"me"&gt;&gt;} 的元组,如果{&lt;&lt;"name"&gt;&gt;, &lt;&lt;"me"&gt;&gt;} 是,你是否希望返回整个&lt;&lt;"serviceCatalog"&gt;&gt; 元组成立?顺便说一句,如果这是您想要的,请注意您的示例数据不匹配,因为 {&lt;&lt;"name"&gt;&gt;, &lt;&lt;"me"&gt;&gt;} 仅存在于 {&lt;&lt;"user"&gt;&gt;, ...} 元组中。

标签: list loops search erlang


【解决方案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/3extract/3 的子句如下所述:

  • 第一个子句检查数据是否为空列表,如果是,则返回累加器。
  • 第二个子句匹配所需的 Key 作为 2 元组的第一个元素,并将匹配的元组添加到累加器中。
  • 第三个子句匹配所需的Key 作为列表头部的2 元组的第一个元素。它将匹配的元组添加到累加器中,并继续从列表的其他元素中提取。
  • 第四个子句处理列表头部没有匹配键的 2 元组的情况,其中元组中的值是列表或元组;如果是,它会尝试从值中提取,然后继续从列表的其余部分中提取。
  • Data 是一个与之前的子句已经匹配的列表不匹配的列表时,第五个子句匹配。它折叠列表的所有值,尝试从所有嵌套列表和元组值中提取并忽略所有其他值。
  • Data 是一个元组时,第六个也是最后一个子句匹配;它只是将元组转换为列表并递归调用extract/3

鉴于问题中定义的数据J,我们可以找到一个带有&lt;&lt;"serviceCatalog"&gt;&gt; 键的二元组,如下所示:

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 的进一步调用,以查看我们找到的元素是否包含我们正在寻找的&lt;&lt;"name"&gt;&gt; 元素。我们将使用返回元组列表的列表推导,每个元组都包含一个 &lt;&lt;"serviceCatalog"&gt;&gt; 元素和一个布尔值,指示它是否包含所需的 &lt;&lt;"name"&gt;&gt; 元素:

2> [{SC,lists:any(fun({_, V}) -> V == <<"me">> end, t:extract(<<"name">>, SC))} || SC <- SCs].

列表解析遍历所有 &lt;&lt;"serviceCatalog"&gt;&gt; 元素,从每个元素中提取所有 &lt;&lt;"name"&gt;&gt; 元组,并检查任何值为 &lt;&lt;"me"&gt;&gt; 的元组。

不幸的是,示例数据中的&lt;&lt;"serviceCatalog"&gt;&gt; 元素没有问题中描述的{&lt;&lt;"name"&gt;&gt;, &lt;&lt;"me"&gt;&gt;} 元组,因此上述列表推导结果中的布尔值(结果未显示)为false。但无论哪种方式,要遍历列表推导的结果,只保留 true 值,但去掉布尔值,因此结果列表只包含匹配的 &lt;&lt;"serviceCatalog"&gt;&gt; 元素,我们可以折叠列表。首先,让我们做一个折叠函数:

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).
[]

同样,由于数据中没有 &lt;&lt;"serviceCatalog"&gt;&gt; 元素包含 {&lt;&lt;"name"&gt;&gt;, &lt;&lt;"me"&gt;&gt;} 元组,因此这里的结果列表是空的,但如果有的话,我们将从 lists:foldl/3 返回的列表中获取它们的值。

【讨论】:

    猜你喜欢
    • 2010-10-26
    • 2022-01-20
    • 2015-09-12
    • 2019-04-21
    • 1970-01-01
    • 1970-01-01
    • 2017-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多