回答編集履歴

6

 

2025/05/27 11:03

投稿

YellowGreen
YellowGreen

スコア868

test CHANGED
@@ -58,7 +58,7 @@
58
58
  const eventMark = '★';
59
59
 
60
60
  // 転記元カレンダーID
61
- const fromCalendarID = 'xxxxxxx@group.calendar.google.com'; // 個人カレンダー(元)
61
+ const fromCalendarID = 'xxxxxxx@group.calendar.google.com';
62
62
 
63
63
  // メンバーの名前とカレンダーIDを記入したシート名
64
64
  const idSheetName = 'IDマスタ';
@@ -67,72 +67,131 @@
67
67
  const startRow = 1;
68
68
 
69
69
  // スプレッドシートを取得
70
- const ss = SpreadsheetApp.getActiveSpreadsheet()
70
+ const ss = SpreadsheetApp.getActiveSpreadsheet();
71
71
 
72
- // メンバーごとの氏名とカレンダーIDをシートから取得して配列membersに保存
72
+ // メンバーごとの氏名とカレンダーIDをシートから取得
73
73
  const idSheet = ss.getSheetByName(idSheetName);
74
+ if (!idSheet) {
74
- const idValues = idSheet.getDataRange().getValues();
75
+ console.log(`${idSheetName}シートが見つかりませんでした`);
75
- const members = idValues.map(v => ({ name: v[0], id: v[1] }));
76
+ return;
77
+ }
76
78
 
79
+ const idValues = idSheet.getDataRange().getDisplayValues();
80
+ const members = idValues
81
+ .filter(v => v[0] && v[1]) // 空の行をフィルタリング
82
+ .map(v => ({ name: v[0].trim(), id: v[1].trim() }));
83
+
84
+ if (members.length === 0) {
85
+ console.log(`${idSheetName}シートに有効なメンバーデータがありません`);
86
+ return;
87
+ }
88
+
77
- // 登録元カレンダーから翌月末日までの予定の内容配列schedulesに保存
89
+ // 登録元カレンダーから翌月末日までの予定を取得
78
90
  const calendar = CalendarApp.getCalendarById(fromCalendarID);
79
91
  const today = new Date();
80
- const nextDay = new Date(today.getFullYear(), today.getMonth() + 2, 0);
92
+ const nextMonthEnd = new Date(today.getFullYear(), today.getMonth() + 2, 0);
93
+
81
- const schedules = calendar.getEvents(today, nextDay)
94
+ const schedules = calendar.getEvents(today, nextMonthEnd)
82
95
  .map(e => ({
83
96
  title: e.getTitle(),
84
97
  start: e.getStartTime(),
85
98
  end: e.getEndTime(),
86
- description: e.getDescription(),
99
+ description: e.getDescription() ?? '',
87
100
  }));
88
101
 
102
+ console.log(`取得した予定数: ${schedules.length}`);
103
+
104
+ // メンバーごとのカレンダーオブジェクトを事前に取得・キャッシュ
105
+ const memberCalendars = new Map();
106
+ for (const member of members) {
107
+ const cal = CalendarApp.getCalendarById(member.id);
108
+ memberCalendars.set(member.id, cal);
109
+ }
110
+
89
- //
111
+ // バッチ処理用の配列
112
+ const sheetOperations = [];
113
+ const calendarOperations = [];
114
+
90
- // メンバー(m)ごとに繰返し処理
115
+ // メンバーごとに処理
91
- //
92
- members.forEach(m => {
116
+ members.forEach(member => {
93
- // メンバー名と同じ名前の転記先シートを取得
117
+ // メンバー名と同じ名前の転記先シートを取得または作成
94
- let sheet = ss.getSheetByName(m.name);
118
+ let sheet = ss.getSheetByName(member.name);
95
119
  if (!sheet) {
96
- sheet = ss.insertSheet(m.name);
120
+ sheet = ss.insertSheet(member.name);
97
- console.log(`${m.name}というシートが見つからなかったので作成しました`);
121
+ console.log(`${member.name}というシート作成しました`);
98
122
  }
99
123
 
100
- // 説明欄にメンバー名がある予定の内容配列eventsに抽出
124
+ // 説明欄にメンバー名がある予定を抽出
101
- const events = schedules.filter(e => e.description.includes(m.name));
125
+ const events = schedules.filter(e =>
126
+ e.description.includes(member.name)
127
+ );
102
128
 
129
+ // シート操作をバッチに追加
130
+ sheetOperations.push({
131
+ sheet: sheet,
132
+ member: member,
133
+ events: events
134
+ });
135
+
103
- // ンバーの予定があったらシート記入
136
+ // カレダー操作をッチ追加
137
+ const memberCalendar = memberCalendars.get(member.id);
138
+ if (memberCalendar && events.length > 0) {
139
+ calendarOperations.push({
140
+ calendar: memberCalendar,
141
+ member: member,
142
+ events: events
143
+ });
144
+ }
145
+
146
+ });
147
+
148
+ // シート操作をバッチ実行
149
+ console.log('シート更新を開始...');
150
+ sheetOperations.forEach(op => {
104
- if (events.length > 0) {
151
+ if (op.events.length > 0) {
105
- // 事前に対象範囲を消去
152
+ // 事前に対象範囲をクリア
106
- const lastRow = sheet.getLastRow();
153
+ const lastRow = op.sheet.getLastRow();
107
154
  if (lastRow >= startRow) {
108
- sheet.getRange(startRow, 1, lastRow, sheet.getLastColumn())
155
+ op.sheet.getRange(startRow, 1, lastRow, op.sheet.getLastColumn())
109
156
  .clearContent();
110
157
  }
111
158
 
112
- // events二次元配列valuesに変換して一括記入[開始日時, 終了日時, 名前を除去した説明]
159
+ // データを一括書き込み
113
- const values = events
160
+ const values = op.events.map(e => [
161
+ e.start,
162
+ e.end,
114
- .map(e => [e.start, e.end, e.description.replace(m.name, '')]);
163
+ e.description.replace(new RegExp(op.member.name, 'g'), '').trim()
115
- sheet.getRange(startRow, 1, values.length, values[0].length)
116
- .setValues(values);
164
+ ]);
117
165
 
118
- // メンバーのカレンダーを取得
166
+ if (values.length > 0) {
119
- const mCalendar = CalendarApp.getCalendarById(m.id);
167
+ op.sheet.getRange(startRow, 1, values.length, values[0].length)
168
+ .setValues(values);
169
+ }
170
+ }
171
+ });
120
172
 
121
- // メンバーのカレンダーから翌月末日までの予定取得
173
+ // カレンダー操作バッチ実行
174
+ console.log('カレンダー更新を開始...');
175
+ calendarOperations.forEach(op => {
176
+ // 既存の★付き予定を一括削除
122
- const mSchedules = mCalendar.getEvents(today, nextDay);
177
+ const existingEvents = op.calendar.getEvents(today, nextMonthEnd);
178
+ const eventsToDelete = existingEvents.filter(e =>
179
+ e.getTitle().startsWith(eventMark)
180
+ );
123
181
 
124
- // 既存の予定の中で目印が付いた予定は削除しておく
182
+ // 削除を並列実行するために配列に集める
125
- mSchedules.forEach(e => {
183
+ eventsToDelete.forEach(e => {
126
- if (e.getTitle().startsWith(eventMark)) {
127
- e.deleteEvent();
184
+ e.deleteEvent();
128
- }
129
- });
185
+ });
130
186
 
131
- // 予定を再登録(タイトルに目印を付ける)
187
+ // 新しい予定を一括作成
132
- events.forEach(e => {
188
+ op.events.forEach(e => {
133
- mCalendar.createEvent(eventMark + e.title, e.start, e.end);
189
+ op.calendar.createEvent(eventMark + e.title, e.start, e.end);
134
- });
190
+ });
135
- };
191
+
136
192
  });
193
+
194
+ console.log('処理完了');
195
+
137
196
  }
138
197
  ```

5

2025/05/23 13:01

投稿

YellowGreen
YellowGreen

スコア868

test CHANGED
@@ -58,7 +58,7 @@
58
58
  const eventMark = '★';
59
59
 
60
60
  // 転記元カレンダーID
61
- const fromCalendarID = 'xxxxxxx@gmail.com'; // 個人カレンダー(元)
61
+ const fromCalendarID = 'xxxxxxx@group.calendar.google.com'; // 個人カレンダー(元)
62
62
 
63
63
  // メンバーの名前とカレンダーIDを記入したシート名
64
64
  const idSheetName = 'IDマスタ';

4

2025/05/23 12:50

投稿

YellowGreen
YellowGreen

スコア868

test CHANGED
@@ -58,7 +58,7 @@
58
58
  const eventMark = '★';
59
59
 
60
60
  // 転記元カレンダーID
61
- const fromCalendarID = 'yellow.green.pointer@gmail.com'; // 個人カレンダー(元)
61
+ const fromCalendarID = 'xxxxxxx@gmail.com'; // 個人カレンダー(元)
62
62
 
63
63
  // メンバーの名前とカレンダーIDを記入したシート名
64
64
  const idSheetName = 'IDマスタ';

3

複数のメンバーの予定の転記に対応したコードの例を追記しました

2025/05/23 12:47

投稿

YellowGreen
YellowGreen

スコア868

test CHANGED
@@ -47,3 +47,92 @@
47
47
  ```
48
48
 
49
49
  今回は、エラーについてのみ回答しました
50
+
51
+ 【以下は、参考までに複数のメンバーの予定の転記に対応したコードの例です】
52
+
53
+ ```JavaScript
54
+ // IDマスタというシートに各メンバーの名前(A列) カレンダーID(B列)を記入しておく
55
+ // この関数を時間主導型のトリガーで深夜帯に実行するように設定しておく
56
+ function myFunction() {
57
+ // このスクリプトで転記した予定につける目印
58
+ const eventMark = '★';
59
+
60
+ // 転記元カレンダーID
61
+ const fromCalendarID = 'yellow.green.pointer@gmail.com'; // 個人カレンダー(元)
62
+
63
+ // メンバーの名前とカレンダーIDを記入したシート名
64
+ const idSheetName = 'IDマスタ';
65
+
66
+ // メンバーのシートへの記入開始位置
67
+ const startRow = 1;
68
+
69
+ // スプレッドシートを取得
70
+ const ss = SpreadsheetApp.getActiveSpreadsheet()
71
+
72
+ // メンバーごとの氏名とカレンダーIDをシートから取得して配列membersに保存
73
+ const idSheet = ss.getSheetByName(idSheetName);
74
+ const idValues = idSheet.getDataRange().getValues();
75
+ const members = idValues.map(v => ({ name: v[0], id: v[1] }));
76
+
77
+ // 登録元カレンダーから翌月末日までの予定の内容を配列schedulesに保存
78
+ const calendar = CalendarApp.getCalendarById(fromCalendarID);
79
+ const today = new Date();
80
+ const nextDay = new Date(today.getFullYear(), today.getMonth() + 2, 0);
81
+ const schedules = calendar.getEvents(today, nextDay)
82
+ .map(e => ({
83
+ title: e.getTitle(),
84
+ start: e.getStartTime(),
85
+ end: e.getEndTime(),
86
+ description: e.getDescription(),
87
+ }));
88
+
89
+ //
90
+ // メンバー(m)ごとに繰返し処理
91
+ //
92
+ members.forEach(m => {
93
+ // メンバー名と同じ名前の転記先シートを取得
94
+ let sheet = ss.getSheetByName(m.name);
95
+ if (!sheet) {
96
+ sheet = ss.insertSheet(m.name);
97
+ console.log(`${m.name}というシートが見つからなかったので作成しました`);
98
+ }
99
+
100
+ // 説明欄にメンバー名がある予定の内容を配列eventsに抽出
101
+ const events = schedules.filter(e => e.description.includes(m.name));
102
+
103
+ // メンバーの予定があったらシートに記入
104
+ if (events.length > 0) {
105
+ // 事前に対象範囲を消去
106
+ const lastRow = sheet.getLastRow();
107
+ if (lastRow >= startRow) {
108
+ sheet.getRange(startRow, 1, lastRow, sheet.getLastColumn())
109
+ .clearContent();
110
+ }
111
+
112
+ // eventsを二次元配列valuesに変換して一括記入[開始日時, 終了日時, 名前を除去した説明]
113
+ const values = events
114
+ .map(e => [e.start, e.end, e.description.replace(m.name, '')]);
115
+ sheet.getRange(startRow, 1, values.length, values[0].length)
116
+ .setValues(values);
117
+
118
+ // メンバーのカレンダーを取得
119
+ const mCalendar = CalendarApp.getCalendarById(m.id);
120
+
121
+ // メンバーのカレンダーから翌月末日までの予定を取得
122
+ const mSchedules = mCalendar.getEvents(today, nextDay);
123
+
124
+ // 既存の予定の中で目印が付いた予定は削除しておく
125
+ mSchedules.forEach(e => {
126
+ if (e.getTitle().startsWith(eventMark)) {
127
+ e.deleteEvent();
128
+ }
129
+ });
130
+
131
+ // 予定を再登録(タイトルに目印を付ける)
132
+ events.forEach(e => {
133
+ mCalendar.createEvent(eventMark + e.title, e.start, e.end);
134
+ });
135
+ };
136
+ });
137
+ }
138
+ ```

2

補足

2025/05/22 08:28

投稿

YellowGreen
YellowGreen

スコア868

test CHANGED
@@ -45,3 +45,5 @@
45
45
  // 予定を再登録(タイトルに★を付ける)
46
46
  for (var i = RANGE; i <= sheet.getLastRow(); i++) {
47
47
  ```
48
+
49
+ 今回は、エラーについてのみ回答しました

1

説明を修正

2025/05/22 08:23

投稿

YellowGreen
YellowGreen

スコア868

test CHANGED
@@ -1,4 +1,4 @@
1
- スプレッドシートへの出力する際に
1
+ スプレッドシートへの出力する際に
2
2
  range(出力する行番号)を予定ごとに更新しているので、
3
3
  説明に「佐藤」が含まれない予定があるとその行が空行になります。
4
4
 
@@ -7,8 +7,9 @@
7
7
 
8
8
  回避方法としては、
9
9
  例えば次のコードのように
10
+ 繰返し処理の前にrange(出力する行番号)の初期値をRANGE(記入開始行)に設定しておき、
10
- スプレッドシートへの登録部分を
11
+ 繰返し処理の中でスプレッドシートに出力する際には
11
- 説明に「佐藤」が含まれる予定の時だけrange(行番号)の更新をするようにします
12
+ 説明に「佐藤」が含まれる予定の時だけrangeの更新をするようにします
12
13
 
13
14
 
14
15
  ```JavaScript
@@ -29,7 +30,7 @@
29
30
  ```
30
31
 
31
32
  なお、
32
- 予定の再登録の際に
33
+ 予定のスプレッドシートへ出力の際に
33
34
  開始行をRANGEで指定していたのですから
34
35
  ```JavaScript
35
36
  // 予定を再登録(タイトルに★を付ける)
@@ -38,7 +39,7 @@
38
39
 
39
40
  のところは i = 1ではなく
40
41
  次のように i = RANGEとしておかないと
41
- RANGEを1以外にしたときにも1行目から再登録しようとしてエラーが発生します
42
+ RANGE(記入開始行)を1以外にしたときにも1行目から再登録しようとしてエラーが発生します
42
43
 
43
44
  ```JavaScript
44
45
  // 予定を再登録(タイトルに★を付ける)