Emotion Wave Tech Blog

福岡にあるエモーションウェーブ株式会社のエンジニアが書いています。

htmlテーブルをレスポンシブに

目次

画面幅に合わせて配置が変わるテーブル

表を、画面サイズに合わせて狭めたり広げてみせたり、ということを試してみました。 画面幅に合わせて、3列、2列、1列と変化します。

f:id:devew:20200619192111g:plain

これ、IEでは正常に動きませんでした。 Chromeでは動きました。あしからず。

ソースはこんな感じです。

<!doctype html>
    <head>
       <meta charset="utf-8">
       <meta http-equiv="X-UA-Compatible" content="IE=edge">
       <title>ファイルアップロード</title>
       <style type="text/css">
table{
    max-width: 1139px;
    width:100%;
}

table,th,td{
    border-collapse: collapse;
    border:1px solid #333;
    vertical-align: baseline;
    margin:0;
    padding:0;
}


table tr{
    display: inline;
    float: left;
}


table th{
    background-color: #CCCCCC;
}

.department{
    width:80px;
}

.num{
    width:80px;
}

.name{
    width:80px;
}

.position{
    width:120px;
}

.detail.department{
    background-color: #CCCCCC;
}

        </style>
       <script
          src="https://code.jquery.com/jquery-1.12.4.min.js"
          integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="
          crossorigin="anonymous"></script>

       <script>
$(document).ready(function(){

   // 初期表示処理
   tableResize();

   //リサイズされたときの処理
   $(window).resize(function() {

       // ダミー行を消し、隠し列を表示に戻す
       $('#datatable tr[data-department="dummy"]').remove();
       $('#datatable td.department').css('display','table-cell');

       tableResize();
   });


   // リサイズ時イベント
   function tableResize(){
       // テーブル幅をtr幅で割る
       switch (Math.floor($("#datatable").width() / 370)) {
         case 0:
         case 1:
           console.log('1行に1人表示');
           break;
         case 2:
           console.log('1行に2人表示');
           addDummyTr(2);
           break;
         case 3:
         default:
           console.log('1行に3人表示');
           addDummyTr(3);
           break;
       }
   }

   function addDummyTr(humanCnt){

       // ヘッダ部調整用の仮行
       var DUMMY_DETAILHEADER_TMPL ='<tr data-department="dummy"><th class="header department">所属</th><th class="header num">社員番号</th><th class="header name">氏名</th><th class="header position">役職</th></tr>';

       var dummy_detailheader = "";

       // ヘッダに調整用の行を追加する。
       for (var i = 1; i < humanCnt; i++) {
           dummy_detailheader = dummy_detailheader + DUMMY_DETAILHEADER_TMPL;
       }

       $('#datatable thead tr:last').after(dummy_detailheader);

       // ヘッダ2番目以降の所属列を消す
       $('#datatable thead th.department:gt(0)').css('display','none');

       // 明細部調整
       // まず、所属を全て取得し一意にする。
       var departmentList = [];
       $('#datatable tbody tr').each(function(index, element){
           departmentList.push($(element).data('department'));
           
       });
       departmentList = departmentList.filter(function (x, i, self) {
         return self.indexOf(x) === i;
       });

       console.log('departmentList:' + departmentList);


       var DUMMY_DETAILROW_TMPL ='<tr data-department="dummy"><td class="department"></td><td class="num" style="color: transparent;">dummy</td><td class="name"></td><td class="position"></td></tr>';

       // 例として、1行に2人の場合は、所属1人だとスキマができるため、スキマ埋めのためのdummy行を追加する。
       for(let v of departmentList) {

           // スキマ埋めすべきdummy行数を求める。
           let addDummyRowCnt = 0;
           let departmentLength = $('#datatable tbody tr[data-department="' + v + '"]').length
           if(departmentLength <= humanCnt) {
               // 例:所属2人で、1行に3人表示の場合は、1人分のスキマ埋めが必要。
               addDummyRowCnt = humanCnt-departmentLength
           } else {
               // 例:所属4人で、1行に3人表示の場合は、2行になるため、この所属の必要な領域は3×2=6となる。2人分のスキマ埋めが必要。
               addDummyRowCnt = (Math.ceil(departmentLength / humanCnt) * humanCnt) % departmentLength;
           }

           // スキマ分、ダミー行を追加する。
           for(let i = 0; i < addDummyRowCnt; i++) {
               $('#datatable tbody tr[data-department="' + v + '"]:last').after(DUMMY_DETAILROW_TMPL);
           }
       }
           

       // 1行に2人以上の場合、表示上の左から1番目の所属列は残し、後の所属列は消す。
       if(humanCnt > 1){

           // 1行あたりの表示人数の倍数ではない行(見た目上、右側に移った行)は所属列を消す
           $('#datatable tbody tr').filter(function(index) {
               return index % humanCnt >= 1;
           }).find('td.department').css('display','none');

       }
   }
});

       </script>
   </head>
    <body>
        <form action="" method="post">
            <table id="datatable">
                <thead>
                    <tr>
                        <th class="header department">所属</th>
                        <th class="header num">社員番号</th>
                        <th class="header name">氏名</th>
                        <th class="header position">役職</th>
                    </tr>
                </thead>
                <tbody>
                    <tr data-department="tokyo">
                        <td class="detail department">東京</td>
                        <td class="detail num">3</td>
                        <td class="detail name">田中</td>
                        <td class="detail position">課長</td>
                    </tr>
                    <tr data-department="tokyo">
                        <td class="detail department">東京</td>
                        <td class="detail num">11</td>
                        <td class="detail name">鈴木</td>
                        <td class="detail position">主任</td>
                    </tr>
                    <tr data-department="tokyo">
                        <td class="detail department">東京</td>
                        <td class="detail num">16</td>
                        <td class="detail name">内海</td>
                        <td class="detail position">パート</td>
                    </tr>
                    <tr data-department="tokyo">
                        <td class="detail department">東京</td>
                        <td class="detail num">5</td>
                        <td class="detail name">田口</td>
                        <td class="detail position">パート</td>
                    </tr>
                    <tr data-department="nagoya">
                        <td class="detail department">名古屋</td>
                        <td class="detail num">14</td>
                        <td class="detail name">富田</td>
                        <td class="detail position">主任</td>
                    </tr>
                    <tr data-department="osaka">
                        <td class="detail department">大阪</td>
                        <td class="detail num">7</td>
                        <td class="detail name">松永</td>
                        <td class="detail position">主任</td>
                    </tr>
                    <tr data-department="osaka">
                        <td class="detail department">大阪</td>
                        <td class="detail num">14</td>
                        <td class="detail name">井ノ瀬</td>
                        <td class="detail position">パート</td>
                    </tr>
                    <tr data-department="fukuoka">
                        <td class="detail department">福岡</td>
                        <td class="detail num">21</td>
                        <td class="detail name">牧田</td>
                        <td class="detail position">主任</td>
                    </tr>
                    <tr data-department="fukuoka">
                        <td class="detail department">福岡</td>
                        <td class="detail num">6</td>
                        <td class="detail name">水島</td>
                        <td class="detail position">リーダー</td>
                    </tr>
                    <tr data-department="fukuoka">
                        <td class="detail department">福岡</td>
                        <td class="detail num">16</td>
                        <td class="detail name">堀川</td>
                        <td class="detail position">パート</td>
                    </tr>
                    <tr data-department="kagosshima">
                        <td class="detail department">鹿児島</td>
                        <td class="detail num">27</td>
                        <td class="detail name">大迫</td>
                        <td class="detail position">主任</td>
                    </tr>
                </tbody>
            </table>
        </form>
    </body>
</html>

考え方

  • テーブルのtrタグをinlineにし、行を横並びに出来るようにする。
  • 所属ごとに人数が違うので、javascriptに動的に空白行を追加したりして、ちょうどよい見た目にする。

やり方

ポイントは、cssでtrタグをdisplay: inline;にしていること。

table tr{
    display: inline;
    float: left;
}

これでtrが横に並びます。 画面横幅に合わせて勝手に折り返してくれます。

とはいえ、所属によって人数が異なるので、名古屋所属の人が東京の行に表示されたりしておかしくなります。 そこで空白行を埋めることで、調整します。

テーブルの幅を検知して、何列表示になるのか判断します。

   // リサイズ時イベント
    function tableResize(){
        // テーブル幅をtr幅で割る
        switch (Math.floor($("#datatable").width() / 370)) {
          case 0:
          case 1:
            console.log('1行に1人表示');
            break;
          case 2:
            console.log('1行に2人表示');
            addDummyTr(2);
            break;
          case 3:
          default:
            console.log('1行に3人表示');
            addDummyTr(3);
            break;
        }
    }

trにはデータ属性departmentを定義しており、どこの所属かわかるようにしています。 javascriptのaddDummyTr関数内で、所属ごとの人数から、必要な空行を追加します。

f:id:devew:20200619191833p:plain

f:id:devew:20200619191937p:plain

横幅に合わせて、ちょうどよくなるように、空白行が追加されました。

次に、ヘッダ行も表示します。 ヘッダのtrタグの後ろに、ヘッダをさらに追加してしまいます。 1行に2人表示する画面幅なら、ヘッダを1行追加し、 3人表示するなら、ヘッダを2行追加します。

   // ヘッダ部調整用の仮行
    var DUMMY_DETAILHEADER_TMPL ='<tr data-department="dummy"><th class="header department">所属</th><th class="header num">社員番号</th><th class="header name">氏名</th><th class="header position">役職</th></tr>';

    var dummy_detailheader = "";

    // ヘッダに調整用の行を追加する。
    for (var i = 1; i < humanCnt; i++) {
        dummy_detailheader = dummy_detailheader + DUMMY_DETAILHEADER_TMPL;
    }

    $('#datatable thead tr:last').after(dummy_detailheader);

f:id:devew:20200619191958p:plain

f:id:devew:20200619192014p:plain

これでヘッダも付きました。

所属列は要らないので消します。

   // ヘッダ2番目以降の所属列を消す
    $('#datatable thead th.department:gt(0)').css('display','none');

departmentはヘッダの所属列についたクラスです。 gt(0)で、2番目以降が選択対象になります。 それをdisplay:noneにして見えなくします。

これで完成です。

あとがき

同じことを実現するにも、様々なやり方があるでしょうし、外部のモジュールを取り入れたら、あっさり実現できたと思います。

今回の書き方が、きれいな正当なやり方だとは思っていませんが、お勉強兼ねてやってみました。

【javascript】2つの連想配列を比較し、重複するものを返す

目次

javascript連想配列を比較し、重複するものがないか、調べたい

どうも、高島です。

javascriptで2つの配列があります。 両方とも存在するデータを取り出すには、どうすればいいでしょうか?

普通の配列を比較するサンプルは、ネット上に結構あるのですが、連想配列の例があまり無かったので、記事を書きました。

早速ですが、filterの中でfilterを呼べば、可能です。

const ary1 = [
  {"keyno":"A1","room":"101"},
  {"keyno":"A1","room":"102"},
  {"keyno":"B1","room":"201"},
  {"keyno":"C1","room":"301"},
  {"keyno":"D1","room":"401"},
];


const ary2 = [
  {"keyno":"A1","room":"101"}, 
  {"keyno":"C3","room":"301"},
  {"keyno":"D1","room":"401"},
  {"keyno":"E1","room":"501"},
];

// 重複有り
var reList = ary1.filter(ary1row=>ary2.filter(
    ary2row=>
        ary2row.keyno === ary1row.keyno &&
        ary2row.room === ary1row.room).length > 0);
        
console.log("両方とも存在するデータ");
console.log(reList);

結果はこうです。

両方とも存在するデータ
[
  { keyno: 'A1', room: '101' },
  { keyno: 'D1', room: '401' }
]

filter内はどんな動きなのか?

実際はどんな処理がされているのでしょうか? console.logを埋め込んで確認してみます。

// 内部の動作を確認してみる
ary1.filter(ary1row=>{
    console.log("ary1row.room=" + ary1row.room);
    ary2.filter(
        ary2row=>{
            console.log("    ary2row.room=" + ary2row.room);
        })
});

結果はこうなりました。

ary1row.room=101
    ary2row.room=101
    ary2row.room=301
    ary2row.room=401
    ary2row.room=501
ary1row.room=102
    ary2row.room=101
    ary2row.room=301
    ary2row.room=401
    ary2row.room=501
ary1row.room=201
    ary2row.room=101
    ary2row.room=301
    ary2row.room=401
    ary2row.room=501
ary1row.room=301
    ary2row.room=101
    ary2row.room=301
    ary2row.room=401
    ary2row.room=501
ary1row.room=401
    ary2row.room=101
    ary2row.room=301
    ary2row.room=401
    ary2row.room=501

ary1をループして、さらにその中でary2をループしていることがわかります。 filterを使えば、ループを書かなくて済む、というわけです。

冒頭の重複を取り出す処理に話を戻します。

ary2.filter関数が、ary1rowとary2rowを比較し、一致する行を返してくれます。 この部分ですね。

ary2.filter(
    ary2row=>
        ary2row.keyno === ary1row.keyno &&
        ary2row.room === ary1row.room)

ary2.filterは、比較条件がtrueならば、データを返します。 もしfalseなら何も返しません。

データを返ってきたか否かは、lengthで判断します。

もうちょっとわかりやすく、returnを省略せずに、lengthでのtrue/false判定を分離して書いてみます。

var reList = ary1.filter(ary1row=>ary2.filter(
    ary2row=>
        ary2row.keyno === ary1row.keyno &&
        ary2row.room === ary1row.room).length > 0);

↓↓↓↓↓↓

var reList = ary1.filter(ary1row=>{
    var reAry2row = ary2.filter(
        ary2row=>{
            return (ary2row.keyno === ary1row.keyno &&
                    ary2row.room === ary1row.room)
        });

    return reAry2row.length > 0 ;
});

ary1rowとマッチしたreAry2row が存在すれば、length > 0 、ですのでtrueで返します。 ary1.filter()はtrueとなったary1rowをreListに返します。

こうして重複したデータのみが reListにセットされます。

2つの配列を比較し、重複しないものを返す

では、ary1に存在するがary2に存在しないデータは?

これは簡単です。結果が0件のものですね。 length === 0 に変えます。

// 重複しない
reList = ary1.filter(ary1row=>ary2.filter(
    ary2row=>
        ary2row.keyno === ary1row.keyno &&
        ary2row.room === ary1row.room).length === 0);

console.log("ary1に存在するがary2に存在しないデータ");
console.log(reList);
ary1に存在するがary2に存在しないデータ
[
  { keyno: 'A1', room: '102' },
  { keyno: 'B1', room: '201' },
  { keyno: 'C1', room: '301' }
]

(おまけ)自分自身の配列内で重複を取り出す

これまでは、異なる配列同士を比較しましたが、 今度は自分自身と比較すればいいです。

const ary1 = [
  {"keyno":"A1","room":"101"},
  {"keyno":"A1","room":"102"},
  {"keyno":"B1","room":"201"},
  {"keyno":"C1","room":"301"},
  {"keyno":"A1","room":"101"},
  {"keyno":"C1","room":"301"},
  {"keyno":"D1","room":"401"},
];


// 重複有り
var reList = ary1.filter((ary1row,ary1idx,selfary)=>selfary.filter(
    ary2row=>
        ary2row.keyno === ary1row.keyno &&
        ary2row.room === ary1row.room).length > 1);
        
console.log("重複するデータ");
console.log(reList);
重複するデータ
[
  { keyno: 'A1', room: '101' },
  { keyno: 'C1', room: '301' },
  { keyno: 'A1', room: '101' },
  { keyno: 'C1', room: '301' }
]

filter() はcallback関数を引数にとります。 このcallback関数の第3引数は自分自身の配列です。 それと比較すればいいです。

まとめ

filter内でfilterを呼び、そこで比較すれば、二重ループを書く必要はなくなります。

最初にfilter見たときは訳わかりませんでしたが、理解するとスッキリ書けます。

欠点といえば、読むには慣れが必要なことでしょうか。

人が書いたものは、正直ちょっとわかりにくいことが多いです。

var reList = ary1.filter((x,y,z)=>z.filter(
    w=>
        w.keyno === x.keyno &&
        w.room === x.room).length > 1);

アロー関数は、こんな感じで略した変数名で書くことが多いので、変数名からは内容が推測できませんし、returnを省略して書かれると、初心者はコードが読めずに苦しんだりします。

もう慣れるしかないんでしょうけどね。

ファイルのmime type(content type)を確認するためのjavascript

目次

前書き

どうも、高島です。

ファイルの形式やファイルサイズを、ローカルでも簡単に確認できるWebサイトを作ってみました。

  • どうやって?

HTML5のFile APIを利用します。

File APIを使ってみる

1.ソースコードです。 これをコピーして、デスクトップにmimetype_check.htmlという名前で保存してください。

<!doctype html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>ファイルアップロード</title>
        <script
          src="https://code.jquery.com/jquery-1.12.4.min.js"
          integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="
          crossorigin="anonymous"></script>
        <script>
        $(document).ready(function(){
            $("#btn_file_up").click(function () {

                var self = $(this).parents("form");

                //ファイル
                var file = self.find('#file_up').val();

                if (file !== "") {

                    //ファイルタイプ
                    filetype = self.find('#file_up').prop('files')[0].type;
                    //ファイルネーム
                    filename = self.find('#file_up').prop('files')[0].name;
                    //ファイルサイズ
                    filesize = self.find('#file_up').prop('files')[0].size;
                    filesize = filesize / 1000; // 単位をkbにする

                    $("#presult").html("mimetypeは、" + filetype + "です。<br>ファイルサイズは、" + filesize + "KBです。");
                }
            });
        });
        </script>
    </head>
    <body>
        <form action="" method="post">
            <p><input type="file" id="file_up" name="file_up" value=""></p>
            <p><input type="button" id="btn_file_up" name="btn_file_up" value="アップロード"></p>
            <p>ファイルを選んでアップロードボタンを押してください。<br>
               そのファイルのmimetypeとファイルサイズが表示されます。<br>
            </p>
            <p id="presult">
            </p>

        </form>
    </body>
</html>

2. 早速、保存したファイルをブラウザで開きます。

f:id:devew:20200220103106j:plain

3. 「ファイルの選択」から、ファイルを選んでアップロードしましょう。

f:id:devew:20200221162956j:plain

4.mime typeとファイルサイズが表示されました。

f:id:devew:20200220103503j:plain

mime typeはどこに設定されているの?

1.ちなみに、xlsではなく、xlsx形式だと、「application/vnd.openxmlformats-officedocument.spreadsheetml.sheet」と表示されます。これは、どこに設定されているのでしょうか?

2.windowsレジストリエディターを起動してみましょう。レジストリをいじるとOSが正常に起動しなくなります。ここから先は自信がある人だけ。windows検索窓で「regedit」と打って、レジストリエディターを起動します。

3. HKEY_CLASSES_ROOTの配下にたくさん拡張子が並んでいます。  f:id:devew:20200220104505j:plain

4. xlsやxlsxを見てみましょう。そこの「Content type」がそうです。

f:id:devew:20200220104555j:plainf:id:devew:20200220104603j:plain

5.つまり、ここの「Content type」に登録されていないと、HTML5のFile APIではファイル形式の判断が出来ません。 Officeなど、それを使用するソフトが正常にインストールされていないと、登録されていないことがあるようです。

あとがき

File APIは便利です。わざわざサーバに飛ばさなくても、クライアント側の処理で、チェックすることが出来ます。 例えばアップロード前のファイルサイズチェックとか。

ただ、ファイル形式チェックは、拡張子が無難かも知れません。 例えばgoogleスプレッドシートを利用していて、OfficeをインストールしていないPCで、アップロードすることもあるかも知れません。そのようなPCに、「Content type」が登録されているかは正直わかりません。

まさか、「Content type」がレジストリに登録されていたとはね。

C#でAngleSharpを使ってHTMLをパースする

こんにちは。中原です。

今回はC#でAngleSharpというのを利用してHTMLをパースしてみました。

HTMLパースすることなんてあまりないかもしれませんが、もしパースする機会があればAngleSharpを使ってみてください。
AngleSharpはこちら。
github.com

AngleSharpをインストールする

まずは、NuGetからAngleSharpを検索・選択して「インストール」。
f:id:devew:20200110162339p:plain f:id:devew:20200110162359p:plain

参照設定に追加されました。
f:id:devew:20200110162541p:plain

パースするHTMLを用意する

パースするHTMLファイルを用意します。
今回はこんなHTMLを作成しました。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>HTML Parser Sample</title>
  </head>
  <body>
    <p id="id-p">ID=id-pの文字</p>
    <p class="class-p">class=class-pの文字1</p>
    <p class="class-p">class=class-pの文字2</p>
    <p class="class-p">class=class-pの文字3</p>
  </body>
</html>

Program.csに以下のusigを追加して、AngleSharpを使えるようにします。

using AngleSharp.Html.Parser;

それでは簡単なパースをしてみます。

idを指定しタグの値を取得する

// htmlファイルを読み込む
var html = File.ReadAllText("html_parser_sample.html", System.Text.Encoding.UTF8);

// HTMLParserのインスタンス生成
var parser = new HtmlParser();

// htmlをパースする
var doc = parser.ParseDocument(html);

// idを指定してElement取得
var idP = doc.GetElementById("id-p");
Console.WriteLine("GetElementByIdを使って[id-p]の値取得 : {0}", idP.TextContent);

実行結果

タグの値が取得できました。 f:id:devew:20200130163605p:plain

classを指定してタグの値を取得する

// htmlファイルを読み込む
var html = File.ReadAllText("html_parser_sample.html", System.Text.Encoding.UTF8);

// HTMLParserのインスタンス生成
var parser = new HtmlParser();

// htmlをパースする
var doc = parser.ParseDocument(html);

// classを指定してElementを取得
var classpList = doc.GetElementsByClassName("class-p");
foreach (var c in classpList)
{
    Console.WriteLine("GetElementsByClassNameを使って[class-p]の値取得 : {0}", c.TextContent);
}

実行結果

指定したクラスの値が全て取得できました。 f:id:devew:20200130163836p:plain

QuerySelectAllを使ってclassを指定してタグの値を取得する

// htmlファイルを読み込む
var html = File.ReadAllText("html_parser_sample.html", System.Text.Encoding.UTF8);

// HTMLParserのインスタンス生成
var parser = new HtmlParser();

// htmlをパースする
var doc = parser.ParseDocument(html);

// QuerySelectorAllでclassを指定してElementを取得
var elements = doc.Body.QuerySelectorAll("p.class-p");
foreach (var e in elements)
{
    Console.WriteLine("QuerySelectorAllを使って[class-p]の値取得 : {0}", e.TextContent);
}

実行結果

一つ前の実行結果と同じ結果が得られました。 f:id:devew:20200130164134p:plain

上記以外にも沢山機能はありますが、今回は上記3パターンを実装してみました。
また機会あればご紹介したいと思います。

全体のソースコード一式は以下に置きました。
github.com

Gmail自動削除の方法

目次

前書き

どうも、高島です。

gmailって、何もしないとどんどん貯まっていきます。

容量を食うので定期的に削除しようと思います。

googleカレンダーのメールやソーシャルサイトからのメールは、単なる通知メールですので、数日で消してしまっていいです。

これらを自動で消していこうと思います。

  • どうやって?

Google Apps Scriptを使います。 これでgmailのサービスにアクセスして、条件に合ったメールを 定期的にゴミ箱に移動させます。

  • 方法は?Gmail編。

gmailのフィルタ機能を使って、メール受信時に自動でラベルを付けます。

ラベルが付いたメールを毎日0時にゴミ箱に移動します。

Gmailにラベルを付けよう

1.まずは、消したいメールの法則性を掴みます。 googleカレンダーからの通知メールを消したいので、送信元のメアドで判断しましょう。

f:id:devew:20200115163616p:plain

2. gmailの「設定」を開きます。

f:id:devew:20200115161230p:plain

3. 「フィルタとブロック中のアドレス」を選択します。

4.フィルタ条件を作成します。

5. 「ラベルを付ける」から、新しいラベルを作ります。

f:id:devew:20200115163229p:plain

6.ラベルが作成されました。今後受信したメールには、このラベルが自動的に付くようになります。

7. ただし、今まで受信したメールにはラベルが付いていません。ラベルを付けてあげましょう。 受信トレイの検索窓に、フィルタ条件と同じものを打ち込みます。そして、「この検索条件に一致するすべてのスレッドを選択する」をクリックします。

8. 条件に一致したメールにラベルを付けてあげましょう。

スクリプト作成と、タイマートリガー設定

1. Google Apps Scriptを作成します。 今回はgoogleスプレッドシート上に乗っけます。 (こうすることで、削除したメールの一覧などをスプレッドシート上に出力することも可能です)

2.Googleスプレッドシートを開いたら、「ツール」→「スクリプトエディタ」を開きます。

3.スクリプトファイル名は〇〇.gsとなります。今回は「コード.gs」とします。

4. コードは以下のとおりです。

function myFunction() {
 
  //3日より前のGoogleカレンダー通知メールを削除する
  deleteMail(GmailApp.search('label:calendargoogle older_than:3d'));
}

function deleteMail(deleteThreads) {
 
  for (var i = 0; i < deleteThreads.length; i++) {
    deleteThreads[i].moveToTrash();
  }
}

解説

myFunctionは最初に呼び出される関数です(詳しくは後で)。 GmailApp.search('label:calendargoogle older_than:3d') は、ラベルが「calendargoogle」と付いた、3日前のメールを抽出しています。

deleteMail関数で、それらを受け取って、 moveToTrash()でゴミ箱に移動します。簡単ですね。

参考:https://developers.google.com/apps-script/reference/gmail/gmail-app

5. 虫さんのマークからデバッグも出来ますよ。 初回実行時は、プロジェクト名とか決める必要があるので、入力しましょう。

6. gmailにアクセスする許可が必要です。聞かれたら、画面の指示に従います。

7. 実行中。しばらく待ちましょう。

8. gmailのゴミ箱見てみましょう。 おお!見事にゴミ箱に移動しています! ここに入ったメールは30日経てば、完全に削除されます。

9. 動作がうまくいくことを確認したら、バッチ化しましょう。 時計マークをクリックします。

10. トリガーを追加します。

11. トリガーを追加します。 実行する関数を選択→これはmyfunctionで良いです。 イベントのソースを選択→時間主導型を選択します。

時間ベースのトリガーのタイプを選択→日付ベースを選びます。 時刻は午前0時~1時を選びます。

12. トリガーが作成されました。

13. 翌日に実行されていることを確認しましょう。

あとがき

これでメールの自動削除が出来ました。

ほっておいても、ゴミ箱に勝手に移動してくれます。

なお、GmailApp.searchは、いろいろな条件を指定できます。 gmailの検索窓で入力できる方法をそのまま流用できます。

例えば、gmailはカテゴリというものがあり、自動的にソーシャルとかプロモーションとか分類されますが、

GmailApp.search('category:promotions older_than:15d')

とすると、15日前のプロモーションのメールが対象になります。

Google Apps Scriptを初めて使いましたが、応用次第では色々できそうです。