見出し画像

【GAS】Google Apps Script 活用事例 シートを増やさず、GASでSUMIF関数を再現する方法

noteのコンテンツで、何が読まれているのかを把握するために、アクセス数をシートに書き出しています。その結果をEvernoteに定期的に飛ばしたいなぁと思って、SUMIF関数的な動きをスクリプトを書きました。まぁ、大したPV数ではないのですが.....。

スクリーンショット 2021-03-13 12.55.15

書き出したままで、集計をしていない状態でした。既にシートが複数枚あるので、あまり増やしたくないと考えていました。

実行すると、こんな感じでEvernoteに送られます。

スクリーンショット 2021-03-14 8.00.00

SlackAppでも、検索可能

スクリーンショット 2021-03-14 8.02.23

一番最初に書いたコードはこちら。

for文が2重。文字列を入れる変数、カウントする変数などなど.....。冗長な感じがするなーと感じていました。

function generateArray_(values, column){
 return values.map(record => record[column]).filter(value => value);
}

//シートを増やさず、GASでSUMIF関数を再現する方法
function generateMessage_() {
 const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
 const sheet       = spreadsheet.getSheetByName('コンテンツ分析');
 const values      = sheet.getDataRange().getValues();
 values.shift();
 
 //ブログの記事名を取得し、重複を削除する
 const originalArray = generateArray_(values, 1);
 const uniqueArray   = Array.from(new Set(originalArray));
 uniqueArray.splice(5);

 console.log('重複なしの配列の要素数',uniqueArray.length);//22
 console.log('元々の配列', originalArray.length);//36

 let amount  = 0;   //合計のPV数
 let count   = 0;   //ヒット回数をカウント
 let message = '';  //Slack等で送るためのメッセージ

 for(let i = 0; i < uniqueArray.length; i++){
   let entry = uniqueArray[i];

   for(let j = 0; j < values.length; j++){
     const title = values[j][1];
     const pv    = values[j][2];

     if(entry === title){
       count  += 1;
       amount += pv;

       console.log(pv, values[j][1], count + '件');
     }//if
   }//for_j

   //タイトルから不要な文言を削除する
   entry = entry
   .replace('【GAS】Google Apps Script 活用事例 ','')
   .replace(/【.*】/, '')
   .replace(/\s/, '')
   .replace(/(.*)/, '')
   .slice(0, 25);

   //メッセージを生成
   message += `${entry} PV: ${amount}\n`;

   //初期化
   title  = '';
   amount = 0;
   count  = 0;

 }//for_i
 console.log(message);
 return message
}//end

上記のコードは、SUMIF関数を実際に組んで、どんなロジックかを確認しながら書きました。

スクリーンショット 2021-03-13 13.00.31

UNIQUE関数

=UNIQUE('コンテンツ分析'!B2:B)

SUMIF関数

=SUMIF('コンテンツ分析'!$B$2:$C,A1,'コンテンツ分析'!$C$2:$C)

やはり、同じ事がやりたい人っているのね

reduceが使えるのではないか?と目星をつけていて、reduceを使ったスクリプトでリライトする事にしました。

エトーさん、はけたさん、ありがとうございます。

.....はけたさんのコードがめちゃくちゃ難しいと感じるのは気のせいでしょうか?一部、スプレッド構文で書かれているのだけは分かる。?の部分は、おそらく三項演算子かな。

NaNになる場合を再現してみる

function myFunction(){
 const a = undefined;
 const b = 1;
 console.log( a + b );//NaN
}



function myFunction2(){
 const a = false;
 const b = 1;
 console.log( a + b );//1
}

undefined以外は、1になりました。うーむ、よく分からないですね。

参考にして、書けたスクリプトがこちら

/**
* メール経由で、Evernoteに送信する。 SlackAppで、 /find note で検索できる 
*
*/

function mailNotify(){
 const greet    = 'note 2021/02/22 からの合計PV数をお知らせします。\n\n';  
 const message  = generateMessage_();
 const to       = '***************@m.evernote.com';

 const date     = Utilities.formatDate(new Date(), 'JST', 'yyyy/MM/dd');
 const subject  = `note アクセス数 ${date}`;
 const sheetUrl = 'https://docs.google.com/spreadsheets/d/*****************/edit';
 
 const body    = greet + message + '\n' + sheetUrl;
 GmailApp.sendEmail(to, subject, body);

}



/**
* 2次元配列を1次元配列にする 
*
* @param {object}  2次元配列
* @param {number}  列 数字で指定
* @return {object} 1次元配列
*
*/

function generateArray_(values, column){
 return values.map(record => record[column]).filter(value => value);
}




/**
* シートから重複を削除して、SUMIF関数的な動きで集計 Evernoteに送信するメッセージを作成
*
*/

function generateMessage() {
 const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
 const sheet       = spreadsheet.getSheetByName('コンテンツ分析');
 const values      = sheet.getDataRange().getValues();
 values.shift();
 
 //ブログの記事名を取得し、重複を削除する
 const entriesArray = generateArray_(values, 1);
 const pvArray      = generateArray_(values, 2);

 //console.log(entriesArray);
 //console.log(pvArray);


 //テキストから余計な文字列を抽出する
 const formatTitle = (text) => (text
 .replace('【GAS】Google Apps Script 活用事例 ','')
 .replace(/【.*】/, '')
 .replace(/\s/, '')
 .replace(/(.*)/, '')
 .slice(0, 25));

 //オブジェクトに変換し、重複を省く ショートサーキット
 const sumObject = pvArray.reduce((acc, current, i) => {
   
   const title = formatTitle(entriesArray[i]);
   acc[title]  = (acc[title] || 0) + current;

   console.log(`title: ${title}, pv: ${current}`);

   return acc
 }, {})//reduce

 console.log(sumObject);
 
 //2次元配列に変換
 const newValues = Object.entries(sumObject);

 //[['ブログタイトル', 'PV数']] indexの1番目 PV数でSORT
 const compareFunc = (a, b) => (b[1] - a[1]);
 newValues.sort(compareFunc);

 console.log(`オブジェクトから変換後の配列: ${newValues}`);
 

 //2次元配列を文字列に変換する
 const message = newValues.map(array => `${array[0]} PV ${array[1]}\n`).join('');
 console.log(message);

 return message
}

2次元配列のソート

ソートの挙動を確かめるためのミニテスト

function myFunction3456(){
 const values = [ [ '入力制限をかけて、綺麗なデータを集めよう', 910 ],
 [ 'スプレッドシートのキーワードからGoogle検索の', 174 ],
 [ '差し込み文書の作成を自動化する', 446 ],
 [ ' VLOOKUP関数的な処理をGASで書く方法', 318 ],
 [ 'noteのアクセスログを定期的に取得するスクリプト', 121 ] ];

 const compareFunc = (a, b) => (b[1] - a[1]);
 values.sort(compareFunc);
 console.log(values);

}

引数のa,bには、1次元配列が入り、それぞれのPV数を比較しながら、並び替える事が出来ます。

こんな記事も書いています。


この記事が参加している募集

#最近の学び

181,334件

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