簡體   English   中英

JSONata,如何轉換缺少元素的數組?

[英]JSONata, how to transform array with elements missing?

我正在嘗試將二維數組(比如行)轉換為相同行的數組,但對於某些行,缺少一個屬性(省略),需要從填充了屬性的前一行中獲取。

不幸的是,我找不到訪問前幾行或將屬性存儲在持久變量中的方法。

我希望有人能給我一個提示,如何最好地實現這一點。

假設數據如下所示:

{
  "payload": [
    {
      "Name": "Name1",
      "Values": "MsgId1.3 / Type1 / "
    },
    {
      "Values": "MsgId1.1 / Type3 / COMP"
    },
    {
      "Name": "Name2",
      "Values": "MsgId2.6 / Type1 / COMP"
    },
    {
      "Values": "MsgId2.5 / Type4 / COMP"
    },
    {
      "Values": "MsgId2.4 / Type4 / REJT"
    },
    {
      "Name": "Name3",
      "Values": "MsgId3.2 / Type7 / "
    }
  ]
}

預期結果如下所示:

{
  "list": [
    {
      "NAME": "Name1",
      "MSG_ID": "MsgId1.3",
      "MSG_TYPE": "Type1",
      "MSG_STATUS": ""
    },
    {
      "NAME": "Name1",   /* <-- this line is missing in my results */
      "MSG_ID": "MsgId1.1",
      "MSG_TYPE": "Type3",
      "MSG_STATUS": "COMP"
    },
    {
      "NAME": "Name2",
      "MSG_ID": "MsgId2.6",
      "MSG_TYPE": "Type1",
      "MSG_STATUS": "COMP"
    },
    {
      "NAME": "Name2",   /* <-- this line is missing in my results */
      "MSG_ID": "MsgId2.5",
      "MSG_TYPE": "Type4",
      "MSG_STATUS": "COMP"
    },
    {
      "NAME": "Name2",   /* <-- this line is missing in my results */
      "MSG_ID": "MsgId2.4",
      "MSG_TYPE": "Type4",
      "MSG_STATUS": "REJT"
    },
    {
      "NAME": "Name3",
      "MSG_ID": "MsgId3.2",
      "MSG_TYPE": "Type7",
      "MSG_STATUS": ""
    }
  ]
}

我的最后一個 JSONata 是這樣的,但它不起作用。

{
  "list": [
    $.payload
      .(
        $values := $.Values ~> $split(" / ");
        $name := ( ($.Name) ? ($.Name) : $name );
        {
          "NAME": $name,
          "MSG_ID": $values[0],
          "MSG_TYPE": $values[1],
          "MSG_STATUS": $values[2]
        }
      )
  ]
}

我也嘗試使用 $each() function,但無濟於事。

我沒有測量此解決方案的性能,但一個想法是在$reduce function 的累加器中累積最后使用的名稱,以在填充列表時填寫空白: https://stedi.link/Uhi76Yq

類似的想法,但基於$index參數傳遞給$reduce function: https://stedi.link/952AyV9的回調參數(也許運行起來會更快)。

我發現一個非常低效的解決方案似乎可以通過使用$filter() function 來工作。

編輯:不幸的是,所有行數超過幾行(我將有 n 千行)的運行時間是幾分鍾。 到目前為止,它給了我正確的結果,但那樣我就無法使用它。 保留它,因為它至少是一個解決方案。

$names:= ([$.payload#$i.({ "pos": $i, "name": $.Name })[name]])^(<pos); 過濾所有具有名稱的行的原始數據並將 index = "pos" = $i與它一起存儲。

$prevname:= $filter($names, function($v, $j, $a) { $v.pos <= $i })[-1].name; 過濾具有索引 <= 當前索引 ( $v.pos <= $i ) 的所有行的數組$names並且只返回最后一行的.name

(
  $names := ([$.payload#$i.({ "pos": $i, "name": $.Name })[name]])^(<pos);
  {
    "list": [
      $
        .payload#$i
        .(
          $values := $.Values ~> $split(" / ");
          $prevname := $filter($names, function($v, $j, $a) { $v.pos <= $i })[-1].name;
          {
            "NAME": ($.Name) ? ($.Name) : $prevname,
            "MSG_ID": $values[0],
            "MSG_TYPE": $values[1],
            "MSG_STATUS": $values[2]
          }
        )
    ]
  }
)

可以在此處找到解決方案示例。

您需要編寫一個遞歸的 function 來命令式地遍歷每一行,而不是使用沒有定義執行順序的映射運算符 ( . )。 然后,您可以傳入前一行的$name以用作下一行的默認名稱。 以下表達式執行此操作:

(
    $row := function($name, $payload) {(
        $first := $payload[0];
        $rest := $payload[[1..$count($payload)-1]];
        $values := $first.Values ~> $split(" / ");
        $name := ( ($first.Name) ? ($first.Name) : $name );
        [
            {
            "NAME": $name,
            "MSG_ID": $values[0],
            "MSG_TYPE": $values[1],
            "MSG_STATUS": $values[2]
            },
            $rest ? $row($name, $rest)
        ]
    )};
    {
        "list": $row("", payload)
    }
)

https://try.jsonata.org/g7bcZKHKl

如果您的數據集足夠大,那么這最終將超過堆棧限制。 如果發生這種情況,您需要稍微重寫 function 以進行尾遞歸。 https://docs.jsonata.org/programming#tail-call-optimization-tail-recursion

安德魯科爾曼的回答讓我朝着正確的方向前進。

我的最終解決方案可以處理大型數據集。 即使負載數組中有數千個對象,它也能快速運行。

可以在此處找到解決方案示例。

(
  $previous := function($index) {
    ($index > 0) ?
      ($.payload[$index - 1].Name ?
        $.payload[$index - 1].Name :
        $previous($index - 1)) : 
      "" };

  {
    "list": $
      .payload#$i
      .(
        $values := $.Values ~> $split(" / ");
        {
          "pos": (0) ? $i,
          "NAME": ($.Name) ? ($.Name) : $previous($i),
          "MSG_ID": $values[0],
          "MSG_TYPE": $values[1],
          "MSG_STATUS": $values[2]
        }
      )
  }
)

.payload#$i使用位置變量綁定( #$i ) 為我提供原始數組payload的行的索引。

"NAME": ($.Name)? ($.Name): $previous($i), "NAME": ($.Name)? ($.Name): $previous($i),檢查當前行是否填寫了Name ,如果沒有,則通過 function $previous搜索先前的名稱。

Function $previous檢查上一行的Name是否已填寫並返回。 否則,它會遞歸調用自身,直到找到上面填充了Name的行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM