テーブル内の最大値とそれに付随するデータを取得したい

 まだまだ、前回のアプリを改造して遊んでますw

 今回は、「熱が37.5度以上あった日のうち、最高の体温とその日付」を取得してみることにします。

今回の完成アプリ

 こんな感じ。ついでに「発症日」を入れれるようにして「最も熱が高かった日の発症日からの日数」も計算しちゃいましょう。

どんな仕組みだったらいいんだろう?

  •  まずは体温の最大値の取得だな。確か、配列から最大値を求める関数があったから、それを使うとしよう。

  •  次に、熱が最も高かった日の日付の取得か。前回までのカスタマイズでテーブルは日付順に並んでるから、上から体温を比較していって、該当した行の日付を取得すればいいな。

  •  あ、37.5度以上って条件があったな。37.5度未満のデータを除外してから処理するより、「最高体温が37.5度以上か」を確認するほうが、楽そうなので、これでいってみるか。

  •  発症日からの日数は、標準機能の計算でできるな。あ、ただ、発症日も最も熱が高かった日もnullがあり得るから、IF文使わないとエラーが出て格好悪そう。

では、早速コードを足してみましょう。

作ったコード

 こんな感じになりました。

(() => {
  ('use strict');
  // 正常値との閾値の定数
  const thresholdKt = 37.5;

  // フィールドを編集不可にする
  kintone.events.on(
    [
      'app.record.create.show',
      'app.record.edit.show',
      'app.record.index.edit.show',
    ],
    (event) => {
      const items = ['測定日_最新', '体温_最新', 'SpO2_最新','最高発熱日','最高体温'];
      const record = event.record;
      items.forEach((item) => (record[item].disabled = true));
      return event;
    },
  );

  // 最後尾のデータを最新情報としてコピーする
  kintone.events.on(
    [
      'app.record.create.change.測定履歴',
      'app.record.edit.change.測定履歴',
      'app.record.create.change.測定日',
      'app.record.edit.change.測定日',
      'app.record.create.change.体温',
      'app.record.edit.change.体温',
      'app.record.create.change.SpO2',
      'app.record.edit.change.SpO2',
    ],
    (event) => {
      const record = event.record;
      const table = record['測定履歴'].value;
      const sortedTable = Array.from(table).sort((a, b) => {
        if (
          (!!a.value.測定日.value ? a.value.測定日.value : '1900-01-01') >
          (!!b.value.測定日.value ? b.value.測定日.value : '1900-01-01')
        ) {
          return 1;
        } else {
          return -1;
        }
      });

      if (
        JSON.stringify(table.map((obj) => obj.value.測定日.value)) !==
        JSON.stringify(sortedTable.map((obj) => obj.value.測定日.value))
      ) {
        record['測定履歴'].value = sortedTable;
      }
      const targetRow = sortedTable[sortedTable.length - 1].value;
      record.測定日_最新.value = targetRow.測定日.value;
      record.体温_最新.value = targetRow.体温.value;
      record.SpO2_最新.value = targetRow.SpO2.value;

      return event;
    },
  );

  // 保存前のチェック(エラーがあったら保存できない)
  kintone.events.on(
    ['app.record.create.submit', 'app.record.edit.submit'],
    (event) => {
      const record = event.record;
      const table = record['測定履歴'].value;

      // 療養履歴の測定日がnullのデータがある
      if (typeof table.find((obj) => !obj.value.測定日.value) !== 'undefined') {
        event.error = '「療養履歴」に「測定日」が空白のデータがあります。';
      }

      // 療養履歴の測定日に重複データがある
      if (
        new Set(table.map((obj) => obj.value.測定日.value)).size !==
        table.length
      ) {
        event.error = '「療養履歴」に「測定日」が重複したデータがあります。';
      }

      // 最高体温・最高発熱日の処理
      // 体温の最大値の抽出なので0以下は存在しないため、null,undefindの場合0に変換して比較
      const maxKt = Math.max(
        ...table.map((obj) =>
          !obj.value.体温.value ? 0 : obj.value.体温.value,
        ),
      );

      // 閾値を超える体温が存在する場合、その体温で発熱日を検索する
      // 保存前には、changeイベントで既に日付順に並び変わっている前提
      if (maxKt >= thresholdKt) {
        const order = table.findIndex((obj) => obj.value.体温.value == maxKt);
        record.最高発熱日.value = table[order].value.測定日.value;
        record.最高体温.value = maxKt;
      } else {
        // 閾値を超える体温が無い場合、最高体温、発熱日を空にする
        record.最高発熱日.value = null;
        record.最高体温.value = null;
      }

      return event;
    },
  );
})();

解説です。

  // 正常値との閾値の定数
  const thresholdKt = 37.5;

 正常値との境目の37.5は最初に定数として定義しました。他に機能を追加するときや基準値を帰るときに、変更箇所が少なくて済みそうです。

    (event) => {
      const items = ['測定日_最新', '体温_最新', 'SpO2_最新','最高発熱日','最高体温'];
      const record = event.record;
      items.forEach((item) => (record[item].disabled = true));
      return event;
    },

 増えたフィールドは基本的に手動編集しないので、編集禁止にしておきます。

  // 最後尾のデータを最新情報としてコピーする
  kintone.events.on(
    [
      'app.record.create.change.測定履歴',
      'app.record.edit.change.測定履歴',
      'app.record.create.change.測定日',

 こっそりchangeイベントに測定履歴テーブル全体を足しました。行追加・削除の際も処理が走ります(特に削除のときに最高体温の行が消えると、処理を走らせない場合、整合性が取れなくなる可能性があるため)。

      // 最高体温・最高発熱日の処理
      // 体温の最大値の抽出なので0以下は存在しないため、null,undefindの場合0に変換して比較
      const maxKt = Math.max(
        ...table.map((obj) =>
          !obj.value.体温.value ? 0 : obj.value.体温.value,
        ),
      );

 Mathオブジェクトを使って最大値を求めることにしました。しかし、ここに落とし穴が!Math.max()は、比較する配列にundefinedがあるとNaNになっちゃうんです。なので、三項演算子を使って、数字以外が入っている場合は0として比較しています。体温なんでマイナスは無いでしょうし、0であれば正常値の基準からは十分に離れているはずですから、楽しちゃいましょう。

      // 閾値を超える体温が存在する場合、その体温で発熱日を検索する
      // 保存前には、changeイベントで既に日付順に並び変わっている前提
      if (maxKt >= thresholdKt) {
        const order = table.findIndex((obj) => obj.value.体温.value == maxKt);
        record.最高発熱日.value = table[order].value.測定日.value;
        record.最高体温.value = maxKt;
      } else {
        // 閾値を超える体温が無い場合、最高体温、発熱日を空にする
        record.最高発熱日.value = null;
        record.最高体温.value = null;
      }

 最初に最高温度が基準値以上か比較しています。
 基準値以下のときも、既に最高発熱日・最高体温が入ってる場合もあるので、空にするのを忘れずに!
 基準値以上のときは

  1.  findIndexで最初に合致する行を探す。

  2.  その行の測定日を取得。

とやってます。で、

「等しい」って「=」じゃないの?!
 いや、基本中の基本なんですけどねw
「==」とか「===」とかあるよ。詳しいことは参考書なりネットなりを参照すればいいけど、間違えそうなのを置いときます。
 あ、覚えたつもりでも最初はよくやらかすんで、動かないときはまず演算子を疑ってみること!

https://note.com/ochadukenop/n/n6f32d14cb501

こんなことを過去に言ってるのに、見事にやらかしました。

        const order = table.findIndex((obj) => obj.value.体温.value = maxKt);

 どこが間違ってるか探してください。このせいで意図しないフィールドの数字が置き換えられて1時間悩みましたw

IF(AND(発症日<>"",最高発熱日<>""),(最高発熱日-発症日)/(24*60*60),"")

最後に「発症日からの日数」の計算式にこれを入れれば完成です。


この記事が気に入ったらサポートをしてみませんか?