GASでガントチャートを作成した話の続き

ども、お久しぶりです、ぺんまるです。
前回[GASでガントチャートもどきを作ってみた](https://qiita.com/penmarudayo/items/40bb9a21771183c0820f)という記事を作成し、chatGPTさんに添削をして頂きました。
しばらく放置していましたが、僕は元気です。

最近はプラットフォームという映画や、ぼっちざろっくというアニメを見ていました。
プラットフォームは正直ちんぷんかんでしたが、設定と世界観はとてもおもしろったです。(小並感)
ぼっちざろっくは、とても良い(語彙力）
星座になれたらという曲、歌詞と歌声、楽器隊の演奏がとても心に響きますね。
久しぶりに、ギターを弾きたくなりました。

# 前回までのあらすじと今回のあらすじ
前の記事でchatGPTさんに添削していただき、今回の記事でそれらを元にコードを修正してみました。
ちなみに、どういったところを指摘していただいたのかというと
* コメントの追加
* 変数の宣言
* エラーハンドリングの対策を追加
* マジックナンバーの削減
* 関数のドキュメンテーション
* スクリプトのテスト
といったところを指摘されました。
とりあえずは、コードを先にご覧ください。

# コード
```javascript:コード修正前

function main() {
  var active_sh = SpreadsheetApp.getActiveSheet();   //アクティブシート名取得
  active_sh.getRange("B2:AD2").clearContent()   //タスクオーバーステータスのクリア
  r = task_row(active_sh);  //タスク一覧の行数を取得
  c = month_column(active_sh);    //時間軸の列数を取得
  task_color(active_sh, r, c);    //指定した値が入っているセルをタスクの背景色を参照して背景色を設定
  count_color(active_sh, r, c)    //タスクフィールドの背景色をカウント、タスク数が設定値を超えるとover表示
}

function task_row(active_sh){
  var i = 4;
  while(true){
    var row_value = active_sh.getRange(i, 1)
    if(row_value.isBlank()){
      break;
    }
    i++
  }
  return i;
}

function month_column(active_sh){
  var i = 2;
  while(true){
    var row_value = active_sh.getRange(3, i)
    if(row_value.isBlank()){
      break;
    }
    i++
  }
  return i;
}

function task_color(active_sh, r, c){
  for(var i = 4; i < r; i++){
      var bg_task_values = active_sh.getRange(i, 2, 1, c).getValues();    //タスクごとに全月の値を取得、二次元配列に格納
      var bg_task_flat = bg_task_values.flat();   //1次元配列化、なんか処理しやすそう
      var task_bg_color = active_sh.getRange(i, 1).getBackgrounds();    //タスクごとの背景色を取得、二次元配列に格納
      var bg_color_flat = task_bg_color.flat();  
      values_jadge(active_sh,bg_task_flat, bg_color_flat, i) ;  //タスクの値を格納した二次元配列を走査、1判定箇所にタスクと同じ背景色を設定
    }
}

function values_jadge(active_sh, bg_task_flat, bg_color_flat, i){
  for(var j = 0; j < bg_task_flat.length; j++){
    if(bg_task_flat[j] == 1)
      active_sh.getRange(i, j + 2).setBackground(bg_color_flat[0]);
  }
}

function count_color(active_sh, r, c){
  const over_value = 4;   
  const transpose = a => a[0].map((_, c) => a.map(r => r[c]));
  var target_color = "#ffffff";   //カラーコード取得
  var bg_color = active_sh.getRange(4, 2 , r, c).getBackgrounds();   //指定範囲の背景色取得
  var count = 0;
  var bg_color_trans = transpose(bg_color);   //二次元配列を列ごとに走査するために転置行列にする
  for(var i = 0; i < bg_color_trans.length; i++){
    for(var j = 0; j < bg_color_trans[i].length; j++){
      if(bg_color_trans[i][j].toUpperCase() != target_color.toUpperCase()){   //白い色以外の場合にカウント
        count++ ;
      }
    }
    if(count >= 4){
      active_sh.getRange(2, i + 2).setValue("over");
    }
    count = 0;
  }
}

```

```javascript:コード修正後
function main() {
  //A列のタスク欄スタート位置
  const count_number_task = [4, 1];
  //B列のx行目から始まる時間軸のスタート位置
  const count_number_column = [3, 2];
  //タスクの重複設定値
  const over_value = 4;
  //アクティブシート名取得の処理
  let active_sh = SpreadsheetApp.getActiveSheet();
  //シートの2行目over表示のステータス初期化
  active_sh.getRange("B2:AD2").clearContent()
  //A列のタスクの数とB列3行目以降の時間軸の列数を取得する関数
  let rc_count_task = task_count(active_sh, count_number_task);  
  let rc_count_column = column_count(active_sh, count_number_column)
  //指定した値が入っているセルをタスクの背景色を参照して背景色を設定
  task_set_color(active_sh, rc_count_task, rc_count_column, count_number_task, count_number_column);
  //タスクフィールドの背景色をカウント、タスク数が設定値を超えるとover表示
  count_color(active_sh, rc_count_task, rc_count_column, over_value);
}

function task_count(active_sh, count_number_task){
  let task_number = Array.from(count_number_task);
  while(true){
    let count_rows = active_sh.getRange(task_number[0], task_number[1])
    if(count_rows.isBlank()===false){
      task_number[0]++;
    }
    else{
      break;
    }
  }
  return task_number;
}

function column_count(active_sh, count_number_columns){
  let column_number = Array.from(count_number_columns);
  while(true){
    let count_columns = active_sh.getRange(column_number[0], column_number[1])
    if(count_columns.isBlank()===false){
      column_number[1]++;
    }
    else{
      break;
    }
  }
  return column_number;
}

function task_set_color(active_sh, rc_count_task, rc_count_column, count_number_task, count_number_column){
  for(let i = count_number_task[0] ; i < rc_count_task[0]; i++){
      //タスクごとに全月の値を取得、２次元配列に格納
      let bg_task_values = active_sh.getRange(i, count_number_column[1], 1, rc_count_column[1]).getValues(); 
      //処理しやすくするため、1次元配列に変換
      let bg_task_flat = bg_task_values.flat();
      //タスクごとの背景色を取得、2次元配列に格納
      let task_bg_color = active_sh.getRange(i, 1).getBackgrounds();    
      //同じく、処理しやすいように、1次元配列に変換
      let bg_color_flat = task_bg_color.flat();
      //タスクの値を格納した２次元配列を走査、1判定箇所にタスクと同じ背景色を設定する関数
      values_jadge(active_sh,bg_task_flat, bg_color_flat, i) ;  
    }
}

function values_jadge(active_sh, bg_task_flat, bg_color_flat, i){
  for(var j = 0; j < bg_task_flat.length; j++){
    //入っている値が1の時、そのセルをタスクの背景色と同じにする
    if(bg_task_flat[j] == 1)
      active_sh.getRange(i, j + 2).setBackground(bg_color_flat[0]);
  }
}

function count_color(active_sh, rc_count_task, rc_count_column, over_value){
  //転置行列にするための関数
  const transpose = a => a[0].map((_, c) => a.map(r => r[c]));
  //カラーコード取得
  let target_color = "#ffffff";
  //指定範囲の背景色取得
  let bg_color = active_sh.getRange(4, 2 , rc_count_task[0], rc_count_column[1]).getBackgrounds();
  //二次元配列を列ごとに走査するために転置行列で変換
  let bg_color_trans = transpose(bg_color);
  //重複タスク量の初期値
  let count = 0;
  for(var i = 0; i < bg_color_trans.length; i++){
    for(var j = 0; j < bg_color_trans[i].length; j++){
      //白い色以外の場合にカウント
      if(bg_color_trans[i][j].toUpperCase() != target_color.toUpperCase()){
        count++ ;
      }
    }
    //重複上限設定値を超えるとover表記
    if(count >= over_value){
      active_sh.getRange(2, i + 2).setValue("over");
    }
    //次のタスクの検査をする前に初期化
    count = 0;
  }
}
```

# 修正点
修正点をそれぞれ解説します。

## コメントの追加
まず、コメントの追加ですが、全部の行にコメントはつけず、後で振り返った時に1分くらい考えそうなものにはつけるようにしました。
あとは、行の上にコメントを書くことでスマホでもみやすいように対応しました←？

## 変数の宣言
指摘事項では
>変数の宣言: 変数を宣言する際に、varの代わりにletまたはconstを使用することを検討してください。letは再代入が必要な場合に、constは再代入しない場合に使用できます。
とありましたので、スコープを意識しつつ、varからlet、constに変更しました。
メインで書いてある、最初のタスクのカウント開始の初期値やカラムの位置、タスクの重複部分は後述のマジックナンバー削減でも修正点に挙げられていますが、こちらでもconst宣言をして修正しています。
これらに関して後ほど、また別の問題点や解決策などで解説いたします。

## エラーハンドリング
エラーハンドリングのところですが、そこに関しては修正していません。
理由はめんどくさいからです。
万が一このコードを使う人がいたとしても、ある程度はコードを触っている方だと思うので、エラーログの表示はいらないかと思いました。

## マジックナンバーの削減
次にマジックナンバーの削減ですが、これに関しては多少改善？したつもりです。
```javascript:マジックナンバーの削減
function main() {
    //A列のタスク欄スタート位置
    const count_number_task = [4, 1];
    //B列のx行目から始まる時間軸のスタート位置
    const count_number_column = [3, 2];
    //タスクの重複設定値
    const over_value = 4;

```
上記のようにメイン関数内にタスクのスタート位置、カラム（時間軸)のスタート位置を設定する初期設定用配列と、タスクの重複上限を設定する変数を宣言しています。
これらを使うことにより、シートの仕様が変わった際にも、柔軟にわかりやすく設定変更ができるかなと考えました。

## 関数のドキュメンテーション
あまり、コメントが多すぎるのもどうかなと思いまして(今更ながら)関数の説明部分だけで、戻り値にコメントの追加はしなくてもいいかなとおもい、追記していません。

## スクリプトのテスト
動いてるのでヨシ！！

# 総まとめ
一応、自分なりに調べたり、修正したりしてみましたがいかがでしょうか。
おそらく、あまりにも変わってないだとか、わかりづらくなってると感じる方もいらっしゃると思います。
個人的には配列でタスクのカウント開始位置を指定するのはよくないんじゃないかとコードを書いてて思いました。
理由は、ただ単にタスクのカウント位置を変える時はcount_number_task = [4, 1]の数字を変えればいいんですけど、コードを改修するときに以下、5行目のコメント

```javascript:配列の弊害？
function task_count(active_sh, count_number_task){
  //ここで配列をコピーしてる
  let task_number = Array.from(count_number_task);
  while(true){
    //task_number[0]とtask_number[1]がなんなのかわからない
    let count_rows = active_sh.getRange(task_number[0], task_number[1])
    if(count_rows.isBlank()===false){
      task_number[0]++;
    }
    else{
      break;
    }
  }
  return task_number;
```
といった弊害があります。
まぁ、コメントアウトで詳細を書けばいいのですが、コードを書き換える際に配列を多用した書き方をすると混乱しそうだなって感じました。
実際はどうなんでしょうかね。

次にメインではこの配列をconstで宣言して、このtask_count関数内でtask_numberに配列をコピーしています。
これ、array.fromというメソッドを使わないと、ただ単に代入しただけでは元のcount_number_taskの値も変わってしまいます。
微かな記憶を頼りにすると、C言語でいうポインタだったり、オブジェクトという型であったりといい、つまり何がいいたいかというと、参照渡しと値渡しという違いがあるそうです。
値渡しは変数を新規に作り、その中に値をコピーします。
しかし、配列は参照渡しといい、元となる配列のアドレスを変数に渡すそうです。
言語によって仕様が違うらしいので気をつけないといけないそうです。

そのほかいろいろとありますが、ぶっちゃけこのプログラムにいつまでも固執するのはあまり良くないのではないかなと思いました。
ある程度学ぶことができたら、次に移るのがいいかなと
いえ、決して飽きたわけではありません(嘘)

何はともあれ、記事を見ていただき、ありがとうございました。
次回は未定ですが、おそらくPythonで何か作ろうと考えています。

# 参考リンク
転置行列で参考にさせていただきました。
>[JavaScriptで二次元配列の行列を転置するワンライナー
](https://qiita.com/kznrluk/items/790f1b154d1b6d4de398)

参照渡しや値渡しで参考にさせていただきました。
>[新人「先輩、参照の値渡しについてちゃんと理解してますか？😊」
](https://qiita.com/yoshi_10_11/items/1b70ccb859891b57cb5a)