LINQのデータ列なし例外発生を回避する方法

こんばんは。
UiPath Studio 2024.10.0 Community editionのユーザです。

1.やりたいこと

扱うDataTableはi_dt_Datatable2、dt_Datatable2_work1の2つで、前者からデータを抽出、計算して後者を作成しています。

io_dt_Datatable2
[Date,Start,End,TimeSpan,Status
2024/06/06,06:58:14,06:59:54,00:01:40,〇
2024/06/06,12:14:53,12:16:16,00:01:23,〇
2024/06/06,13:00:01,13:01:21,00:01:20,〇
2024/06/06,15:01:55,15:03:24,00:01:29,〇
2024/06/06,15:49:52,15:51:10,00:01:18,〇
2024/06/06,16:01:02,16:02:21,00:01:19,〇
2024/06/06,16:25:49,16:27:04,00:01:15,〇
]

dt_Datatable2_work1
例外発生のため値は確認できず。

具体的には、i_dt_Datatable2の(“Date”)でグループ単位にして、(“Start”)が9:00~12:00までの(“TimeSpan”)平均値を計算してdt_Datatable2_work1の(“Date”)に年月日(2024/06/06)を、
dt_Datatable2_work1の(“AVG TimeSpan”)にTimeSpan平均値を書き込みます。

構文は下記のとおりです。


dt_Datatable2_work1=i_dt_Datatable2.AsEnumerable.Where(Function(x) Timespan.Parse(x("Start").ToString) >= TimeSpan.FromHours(9) AndAlso TimeSpan.Parse(x("Start").ToString) < TimeSpan.FromHours(12)).groupBy(Function(x) x("Date").ToString).Select(Function(x) dt_Datatable2_work1.LoadDataRow({x.Key, x.Sum(Function(y) TimeSpan.Parse(y("TimeSpan").ToString).TotalSeconds)/ x.Count},False)).CopyToDataTable

2.相談したいこと

i_dt_Datatable2の(“Date”)でグループ単位にして、(“Start”)が9:00~12:00までのデータが無い場合は、 The source contains no DataRows.という例外が発生します。
i_dt_Datatable2の(“Date”)でグループ単位にして、(“Start”)が9:00~12:00までのデータが無い場合は、dt_Datatable2_work1(“AVG TimeSpan”)列に0を書き込みたいが、前述の構文をどう修正すれば良いでしょうか。

※同じ質問をCopilotにしたところ、下記のコードが回答として返ってきました。
invoke codeアクティビティで下記のコードが使えそうであれば、下記のコードをinvoke codeアクティビティに貼った場合の事例を回答としていただいても構いません。


dt_Datatable2_work1 = i_dt_Datatable2.AsEnumerable()
    .GroupBy(Function(x) x("Date").ToString)
    .Select(Function(g)
        Dim rows = g.Where(Function(x) TimeSpan.Parse(x("Start").ToString) >= TimeSpan.FromHours(9) AndAlso TimeSpan.Parse(x("Start").ToString) < TimeSpan.FromHours(12)).ToList()
        If rows.Any() Then
            Return dt_Datatable2_work1.LoadDataRow({g.Key, rows.Sum(Function(y) TimeSpan.Parse(y("TimeSpan").ToString).TotalSeconds) / rows.Count}, False)
        Else
            Return dt_Datatable2_work1.LoadDataRow({g.Key, 0}, False)
        End If
    End Function)
    .Where(Function(x) x IsNot Nothing)
    .CopyToDataTable()

こんにちは

例えば以下のpost のようにCopyToDataTableの代わりにToArrayでDataRow配列を出力し、それの内容有無で処理を分ければ良いかと思います

ご指導に従い、WF修正しましたが、2か所エラーが出ました。再度ご指導ください。
いちばん上の代入文は下記になります。

arr_DataRow=i_dt_Datatable2.AsEnumerable.Where(Function(x) Timespan.Parse(x("Start").ToString) >= TimeSpan.FromHours(9) AndAlso TimeSpan.Parse(x("Start").ToString) < TimeSpan.FromHours(12)).groupBy(Function(x) x("Date").ToString).Select(Function(x) dt_Datatable2_work1.LoadDataRow({x.Key, x.Sum(Function(y) TimeSpan.Parse(y("TimeSpan").ToString).TotalSeconds)/ x.Count},False)).ToArray

いちばん下のエラーが出ているElseセクションの構文は下記になります。

arr_DataRow("AVG TimeSpan")=0


上記の参照先の処理と異なるようですが…

評価すべきは

arr_DataRow.Any

です。

arr_DataRowに中身があれば、CopyToDataTable で出DataTable に変換してください。これはもとの式と同じです。
中身が空の場合は、そもそも行がありませんのでdt_Datatable2_work1の内容をどの様にするか、明確にする必要があると思います

中身が空の場合は、"Date"だけ値を残して、”AVG TimeSpan”に0を設定したいです。
イメージは
{“Date”,“AVG Timespan”}={“2024/06/06”,0}です。
代入文をどう書けば良いでしょうか?

この評価結果がFalseになるのは、

  • arr_DataRow 配列に要素が存在しない場合。言い換えると指定した時間範囲(9:00~12:00)に該当するデータが存在しない場合だと思われますが、
    *{”Date”,“AVG Timespan”}={“2024/06/06”,Null}の場合もデータが存在しないとみなされるのでしょうか?

前提によりますが、この時点でdt_Datatable2_work1の中身はどのようになていますでしょうか?
例えばスキーマが定義されていて、行が無い状態でしたら、以下の様に行追加が必要です。

*{”Date”,“AVG Timespan”}={“2024/06/06”,Null}の場合もデータが存在しないとみなされるのでしょうか?

このケースは式中のSumで例外となると思います。

回答をお待ちしておりました。ありがとうございます。これからやってみます。さすがUiPath難解です。

こんにちは。
隠れて見えない代入文は
dt_Datatable2_work1=arrDr.CopyToDataTable
でしょうか?

Thenの中でしたら、そうです。直前の式でCopyToDataTableの代わりにToArrayで出力したものをCopyToDataTableでDataTableに出力しています。

こんばんは。ご教示ありがとうございます。
2点さらにご教示ください。

1.作成いただいたIfアクティビティのElseセクションをご教示どおり打ち込みましたが、
添付のエラーが出ます。何故でしょうか?

2.作成いただいたIfアクティビティのElseセクションのAddDataRowアクティビティに
New Object(){“2024/06/06”,“00:00:00”}
と書いてありますが、i_dt_Datatable2の(“Date”)でグループ単位にして、(“Start”)が9:00~12:00までの(“TimeSpan”)平均値を計算してdt_Datatable2_work1の(“Date”)に年月日(2014/06/06)を、
dt_Datatable2_work1の(“AVG TimeSpan”)に平均値を書き込む仕様のため、"2024/06/06"はリテラルではなく変数にしないとまずいです。どのような構文になりますでしょうか。

<ご参考>

io_dt_Datatable2
[Date,Start,End,TimeSpan,Status
2024/06/06,06:58:14,06:59:54,00:01:40,〇
2024/06/06,12:14:53,12:16:16,00:01:23,〇
2024/06/06,13:00:01,13:01:21,00:01:20,〇
2024/06/06,15:01:55,15:03:24,00:01:29,〇
2024/06/06,15:49:52,15:51:10,00:01:18,〇
2024/06/06,16:01:02,16:02:21,00:01:19,〇
2024/06/06,16:25:49,16:27:04,00:01:15,〇
2024/06/07,06:58:14,06:59:54,00:01:40,〇
2024/06/07,12:14:53,12:16:16,00:01:23,〇
2024/06/07,13:00:01,13:01:21,00:01:20,〇
2024/06/07,15:01:55,15:03:24,00:01:29,〇
2024/06/07,15:49:52,15:51:10,00:01:18,〇
2024/06/07,16:01:02,16:02:21,00:01:19,〇
2024/06/07,16:25:49,16:27:04,00:01:15,〇
]

dt_Datatable2_work1
例外発生のため値は調査できず。

今回質問させていただいているアルゴリズムは、Main.xamlの最後でInvoke WorkFlow Fileアクティビティで呼び出されている"LogAnalysis_Sheet2.xaml"の中の処理になります。

上記では6/7分もnullになると思いますが...
最低限の要件を満たした入力データを準備できませんか?

UploadしたWFの中の、Main.xamlが配置されたフォルダからの相対パス

\OUTPUT\OUTPUT.xlsx

の"Sheet2"をRead Rangeしたのが、dt_datatable2であり、これをMain.xamlから呼び出したLogAnalysis_Sheet2.xamlの中で下記代入文
を実行すると、dt_Datatable2_work1が取得できるはずでした。しかし、今回(“TimeSpan”)の平均値を計算するためのデータが存在しなかったため、dt_Datatable2_work1が取得できませんでした。
(“TimeSpan”)の平均値を計算するためのデータが存在すれば、dt_Datatable2_work1は作成できたはずです。
従いまして、小職としましては、既にテストデータは提供済みという認識です。
もし追加で欲しいデータがあれば"Sheet2"に任意のデータを手作業で行追加いただけばと思います。
(“Start”)を9:00~12:00までにしておくのがMUSTですが、EndはStartより後の時間であれば、どんな時間でも良いし、TimeSpan列はEnd-Startで計算できます。今回Status列は無視していただいて構いません。

何か他に欲しい情報があれば、何なりとお申し付けください。


dt_Datatable2_work1=i_dt_Datatable2.AsEnumerable.Where(Function(x) Timespan.Parse(x("Start").ToString) >= TimeSpan.FromHours(9) AndAlso TimeSpan.Parse(x("Start").ToString) < TimeSpan.FromHours(12)).groupBy(Function(x) x("Date").ToString).Select(Function(x) dt_Datatable2_work1.LoadDataRow({x.Key, x.Sum(Function(y) TimeSpan.Parse(y("TimeSpan").ToString).TotalSeconds)/ x.Count},False)).CopyToDataTable

上記の6/7のデータですと、結果として示されている

2024/06/07,06:58:14

にはならないのでは?ということです。

おっしゃる通りです。だからダミーだと追記しています。正確なデータが欲しい場合は、”Sheet2”からWFでdt_Datatable2_work1を生成してください。ただし、TimeSpanの平均値を計算するデータがないとdt_Datatable2_work1が作成できないのはご存じの通りです。

意図が通じていないようですが、平均の問題ではなく

2024/06/07,null

になるのでは?ということです。

私も概念的にはそうだろうと思いますが、何分ローカルパネルで値を確認する前に例外が発生してデバッグ終了して、dt_Datatable2_work1の値を確認できなかったので、ダミーデータで表現しておきました。私はYoichi様のように”心の眼”を持っていないため、見ていないものを見たようには書けません。もし気になるようでしたら、このスレッドの該当部分は正直に「値を調査できませんでした」に修正しておきます。(既に修正しました)それではElseセクションのリテラルを変数に修正する箇所の回答をお待ちします。

例えば以下の様になると思います。

dict =dt.AsEnumerable.GroupBy(Function(r) r("Date").ToString).ToDictionary(Function(g) g.Key, Function(g) g.ToArray)

arrDr = kv.Value.Where(Function(v) TimeSpan.Parse(v("Start").ToString) >=TimeSpan.FromHours(9) AndAlso TimeSpan.Parse(v("Start").ToString)<TimeSpan.FromHours(12) ).ToArray()

AddDataRowアクティビティのArrayRow

New Object(){kv.Key,TimeSpan.FromSeconds(arrDr.Average(Function(r) TimeSpan.Parse(r("TimeSpan").ToString).TotalSeconds)).ToString}

Sample
Sample20240723-1.zip (3.9 KB)

ワークフローで使用されているkvは暗黙変数のようですが、辞書変数をFor Eachアクティビティで回すときに使用される特殊な項目名という認識でよろしいでしょうか。