(コメントより)
画像内の「項目1」のAとBをそれぞれ異なるものとして扱い集計をかけたい
項目1がAの場合のステータス比較と、項目1がBの場合のステータス比較は、それぞれ独立しているものとして扱いたいのですが、項目1がAから次のBにいくまでに、画像のA列が空白になっているので、区別して扱うにはどのようにすれば良いか
詳細は下記のコードのmain()を参照いただきたいのですが、下記のように、
・1行読みとる
・A列に空白でないものが現れた場合は、その値を項目1として保存。ブロックの開始行をstartという変数に保存
・次にA列に空白でないものが現れたとき
->項目1の値と一緒にstartから現在行までのblockを別関数に渡し、その別関数の中でステータス最大値を計算し、結果を返す
その後項目1を切り替えて、start行を更新、ループ最初に戻って次の行を読み取る
という流れになります。
※なお、下記のコードが正常に動く前提として
・各行の確認日はすべて処理当日以前の日付であること
・各項目において、同じ担当者で同じ確認日のデータは存在しない
とします。
js
1// ステータス値を表すオブジェクト
2const status = { 'a': 1, 'b': 1, 'c': 3, 'd': 4, 'e': 5, 'f': 6 }
3const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
4// ステータスの最小値
5const MIN_STATUS_VALUE = Object.values(status).sort((a, b) => a - b)[0];
6// ステータスの最大値
7const MAX_STATUS_VALUE = Object.values(status).sort((a, b) => a - b).slice(-1)[0];
8
9function startFunc() {
10 // F列の最終行を取得
11 const lastRow = sheet.getRange(sheet.getMaxRows(), 6).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();
12 // F列(連携日)のデータを取得
13 const renkeibiList = sheet.getRange(2, 6, lastRow - 1, 4).getValues();
14
15 // I列(項目1)をキー、F列(連携日)を値とする連想配列を作成。
16 const renkeibiTable = renkeibiList.reduce((acc, cur) => { acc[cur[3]] = cur[0]; return acc }, {})
17
18 // ステータス最大値を持つレコードを取得
19 const records = getMaxStatusRecords(renkeibiTable);
20
21 // 各項目1ごとのステータスの結果を書き込む。ステータス最大値の場合は、成約額を書き込む。
22 const writeValues = renkeibiList.map(e =>
23 [
24 records[e[3]]?.[2] ?? '', // J列
25 status[records[e[3]]?.[2]] === MAX_STATUS_VALUE ? records[e[3]][4] ?? '' : '' // K列
26 ]
27 );
28 sheet.getRange(2, 10, writeValues.length, writeValues[0].length).setValues(writeValues);
29}
30
31function getMaxStatusRecords(table) {
32 // 列Bの最終行を取得
33 const lastRow = sheet.getRange(sheet.getMaxRows(), 2).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();
34
35 // シートからデータ部分を取得する。(最後の行の判定のため1行余分に取得)
36 const data = sheet.getRange(2, 1, lastRow, 5).getValues()
37
38 // 処理中のブロックの「項目1」を保存する変数
39 let currentKoumoku1 = '';
40
41 // 結果を格納する配列
42 const result = {};
43
44 // 各ブロックの最初の行
45 let start = 0;
46
47 for (let row = 0; row < data.length; row++) {
48 // 現在行のA列(項目1)を読み取る
49 const koumoku1 = data[row][0];
50
51 // 取得した行の「項目1」が空欄かどうか判定
52 if (koumoku1 !== '') {
53 // 現在保存中の「項目1」が空欄かどうか判定
54 if (currentKoumoku1 !== '') {
55 /* 取得した行の「項目1」が空欄ではない&保存中の「項目1」が空欄ではない
56 =最初の行ではない&項目1が切り替わったタイミング ということなので、
57 ブロックを処理。*/
58 const statusValue = checkStatusValues3(data.slice(start, row), table[currentKoumoku1])
59 // 結果用連想配列に項目1をキーとしてステータス値を格納
60 result[currentKoumoku1] = statusValue;
61 }
62 // 切替後の新しい「項目1」をcurrentKoumoku1に代入し、ブロックのstartを現在行に更新する。
63 currentKoumoku1 = koumoku1;
64 start = row;
65 }
66 }
67 // ループを抜けたので残っているブロックを処理する
68 const statusValue = checkStatusValues3(data.slice(start, lastRow - 1), table[currentKoumoku1]);
69
70 // 結果用連想配列に項目1をキーとしてステータス値を格納
71 result[currentKoumoku1] = statusValue;
72
73 return result;
74}
75
76function checkStatusValues3(block, renkeibi) {
77 // 確認日降順に並び替える。
78 return block.sort((a, b) => b[3].getTime() - a[3].getTime())
79 // 確認日が連携日より後のものを抽出
80 .filter(e => e[3] > renkeibi)
81 // 同じ担当者であれば確認日が処理当日に直近のものを抽出
82 .reduce((acc, cur) => acc.some(e => e[1] === cur[1]) ? acc : [...acc, cur], [])
83 // ステータスが最大のものを抽出
84 .reduce((a, b) => status[a[2]] > status[b[2]] ? a : b, MIN_STATUS_VALUE - 1);
85}
86