テーブル内の最大値とそれに付随するデータを取得したい
まだまだ、前回のアプリを改造して遊んでますw
今回は、「熱が37.5度以上あった日のうち、最高の体温とその日付」を取得してみることにします。
![](https://assets.st-note.com/img/1678790624735-aTVyaIGAvg.jpg)
こんな感じ。ついでに「発症日」を入れれるようにして「最も熱が高かった日の発症日からの日数」も計算しちゃいましょう。
どんな仕組みだったらいいんだろう?
まずは体温の最大値の取得だな。確か、配列から最大値を求める関数があったから、それを使うとしよう。
次に、熱が最も高かった日の日付の取得か。前回までのカスタマイズでテーブルは日付順に並んでるから、上から体温を比較していって、該当した行の日付を取得すればいいな。
あ、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;
}
最初に最高温度が基準値以上か比較しています。
基準値以下のときも、既に最高発熱日・最高体温が入ってる場合もあるので、空にするのを忘れずに!
基準値以上のときは
findIndexで最初に合致する行を探す。
その行の測定日を取得。
とやってます。で、
「等しい」って「=」じゃないの?!
いや、基本中の基本なんですけどねw
「==」とか「===」とかあるよ。詳しいことは参考書なりネットなりを参照すればいいけど、間違えそうなのを置いときます。
あ、覚えたつもりでも最初はよくやらかすんで、動かないときはまず演算子を疑ってみること!
こんなことを過去に言ってるのに、見事にやらかしました。
const order = table.findIndex((obj) => obj.value.体温.value = maxKt);
どこが間違ってるか探してください。このせいで意図しないフィールドの数字が置き換えられて1時間悩みましたw
IF(AND(発症日<>"",最高発熱日<>""),(最高発熱日-発症日)/(24*60*60),"")
最後に「発症日からの日数」の計算式にこれを入れれば完成です。