【スプレッドシート】おじさん専用!予算管理用日付関数

こんにちは。
Googleスプレッドシートで予算管理をしようと企むオジサンです。
予算管理では様々な日付でグルーピンが必要になります。
たとえば年度ごとに集計とか、四半期ごとに集計とか...
そのために「2019/01/01」のような日付を「FY2018」みたいな値に変換したいです。
具体的には↓これくらいのケースに変換したい。

2019/01/01に対しての変換

ケース
年度 FY2018
半期 FY2018下期
四半期 FY2018_4Q
年月 201901

で、これができるカスタム関数作った。
その名もcomDate()。なんとなく会社の日付だから。
もっと良い名前あったら教えてください。

利用例

使い方について細かい話をしてもしょうがないので具体的な利用例で説明します。
まずこんな感じの家計簿があったとします。

f:id:naosim:20191123160659p:plain

支出日と支出額が書いてあるテーブルです。

これに対してC1のセルにこんな風に記述します。

=comDate(A:A,"年度,半期,四半期,年月")

第1引数は支出日の列、第2引数は変換タイプです。
これを実行するとこうなる!

f:id:naosim:20191123160828p:plain

日付がいろんな値に変換されてる!

この表を使って、年度別に集計してみる。
こうゆうときはquery関数が便利。

=query(B:C,"select C, sum(B) where C != '' group by C")

これを実行すると

f:id:naosim:20191123160852p:plain

キターーー!
オジサンの効率化完了。

ソース

ソースはここに置いておきます。
まだまだ改良の余地があるので参考までに。

ではまた。

【スプレッドシート】カスタム関数を作る

GoogleスプレッドシートはGAS側に定義した関数(カスタム関数)を呼び出すことができます。
これを使いこなせたら最強だなと思ったのでいろいろ調べました。

GASに定義したカスタム関数を呼ぶ

GAS側に

function hello() {
  return 'hello';
}

という関数があるとしたら、シートには

=hello()

と書けば呼び出せます。
上記の結果としてhelloと表示されます。

f:id:naosim:20191123150855p:plain

簡単すぎる!

カスタム関数に引数を渡す

カスタム関数には引数も渡せます。

続きを読む

GASのDateコノヤロー!

GASのDateに腹が立ったのでメモ。

var date = new Date('2020-01-01')

これは想定通り2020年1月1日になります。
これに時刻を追加したこれは?

var date = new Date('2020-01-01 09:00')

なぜか1970年になります。 一方、同じgoogle製のchromeで同じコードを動かすと2020年になります。
GASめ...
ちなみに日付をハイフン区切りではなくスラッシュ区切りにするとちゃんと2020年になります。

var date = new Date('2020/01/01 09:00')

GASめ...
同じgoogleだろ...

まとめ

入力 GAS出力
new Date('2020-01-01') Wed Jan 01 09:00:00 GMT+09:00 2020
new Date('2020-01-01 09:00') Thu Jan 01 09:00:00 GMT+09:00 1970
new Date('2020/01/01') Wed Jan 01 00:00:00 GMT+09:00 2020
new Date('2020/01/01 09:00') Wed Jan 01 09:00:00 GMT+09:00 2020

chromeでは全て2020年になります。

GASめ...

2DゲームエンジンDEFOLDの紹介

2DゲームエンジンのDEFOLDを使いはじめました。
調べた内容を少しずつまとめます。
今日は紹介だけ。

img

完全無料

「完全」の意味はよくわからないけど、やったぜ!

言語はlua

javascriptに似たゆるい言語。プログラミング初心者でも手を出しやすいと思います。
Lua - Wikipediaより

Hello World

print("Hello World")

挿入ソート

local a = {5, 3, 1, 4, 2} -- `{´と`}´はテーブルコンストラクタ
for i = 2, #a do -- `#´は長さ演算子であり、`#a´はテーブルaのサイズ(ここでは5)を返す
    for j = i, 2, -1 do
        if a[j - 1] <= a[j] then break end
        a[j], a[j - 1] = a[j - 1], a[j]
    end
end

特に癖もなく普通の言語です。

マルチプラットフォーム対応

対応プラットフォームいっぱい。 - HTML5 - Android - iOS - Steam - FB Instant Games - Windows - macOS - Linux

セットアップ不要

エディタをダウンロードしてインストールするだけで環境構築完了です。 編集画面のUIもシンプルでよさそう。 img

Message Passingが良い

ちょっと細かい話ですが、DEFOLDでは メソッド呼び出しなどのオブジェクト同士のコミュニケーション時に、オブジェクトのメソッドを直接呼ぶことができません。
その代わりMessage Passingを使い、間接的にメッセージを送る方式を使います。
こんなイメージ
通常のメソッド呼び出しDEFOLDのMessagePassingObjectAObjectBObjectAObjectB事務局動け依存なしObjectBに動いてほしいObjectAが「動いてほしい」て言ってるよ。

これによりオブジェクト同士の依存が強制的になくなります。
依存がないとオブジェクトの再利用などの最適化ができるようになります。
そこらへんの最適化はDEFOLD側で全部いい感じにやってくれます。
なので開発者はゲームのことだけ考えれば良い。
ありがたやー。

とりあえずシューティングゲームでも作ってみます。

ツリー構造の問題

プログラミングの問題です。
親子関係のあるツリー構造の問題です。
親が子を持つか、子が親を持つか、相互に持つか、考え始めるといつも悶々としてしまうんですよねー。特に答えはないですが悶々としてみてください。
以下はjavascriptで書いてますが言語はなんでも良いと思います。

問題

f:id:naosim:20191022165557p:plain

図のようなツリー構造を作るTreeFactoryを作成したい。
下記の問題テンプレの// 実装してくださいの部分を埋めて、全体がエラーなく実行される状態にしてください。 // 実装してください以外の場所にメソッド等を実装しても良いです。

ツリー構造の説明

要素

  • 要素はIDと値を持っている
  • 要素にはノードとリーフの種類がある

リーフ

  • 子供の要素を持たない
  • idはツリー全体でユニークであれば良い
  • 値は1(固定)

ノード

  • 子供の要素を1つ以上持つ
  • idはツリー全体でユニークであれば良い
  • 値は配下にあるリーフの総数

問題テンプレ

<!DOCTYPE html>
<script>
class TreeFactory {
  
  add(id, parentOption) {
    // 実装してください
  }

  find(id) {
    // 実装してください
    return {id: '-1', value: -1}
  }

  update(id, parent) {
    // 実装してください
  }
}

// 期待する挙動

// (1) ツリー構造の構築
const treeFactory = new TreeFactory();
treeFactory.add('A')
treeFactory.add('E', 'B')
treeFactory.add('B', 'A')
treeFactory.add('C', 'A')
treeFactory.add('D', 'B')

const a = treeFactory.find('A')
assert('(1)-1', `id:${a.id}, value:${a.value}`, 'id:A, value:3')

const b = treeFactory.find('B')
assert('(1)-2', `id:${b.id}, value:${b.value}`, 'id:B, value:2')

// -- ここまでで図と同じ状態


// (2) ツリーへの追加
treeFactory.add('F', 'B')
treeFactory.add('G', 'C')
const a2 = treeFactory.find('A')
assert('(2)-1', `id:${a2.id}, value:${a2.value}`, 'id:A, value:4')

// (3)ツリーの変更
treeFactory.update('D', 'A')// DをA直下にぶら下げる
const a3 = treeFactory.find('A')
assert('(3)-1', `id:${a3.id}, value:${a3.value}`, 'id:A, value:4')

const b3 = treeFactory.find('B')
assert('(3)-2', `id:${b3.id}, value:${b3.value}`, 'id:B, value:2')

// 確認用
function assert(message, actual, expect) {
  if(actual !== expect) {
    console.error(message, 'エラー', `${actual} != ${expect}`)
    return;
  }
  console.log(message, 'OK');
}
</script>

【GAS】Gmailからメールを検索する

Gmailからメールを検索する方法はググるとたくさんヒットするけど、どれも微妙だから自作した。

諸悪の根源はThread

メールの検索にはGmailApp.search()てメソッドを呼ぶけど、こいつがよくわからん。
なぜか戻り値がスレッドのリスト。。
メールのリストを返してくれれば良いのに。。

完成品

POINT 1: スレッドを全部展開する

2次元のfor文で中身を掘る。

POINT 2: 同一IDの除去

スレッドの仕様がよくわからないけど、展開しただけだと同じメールが複数件ある可能性がある。 なので同一IDを除去する。

POINT 3: ソート

検索結果はどうゆう順に取れるのかよくわからないので、日付でソートする。

とりあえずこれで完成。 ただし、メールが取得できたからと言って安心できない。

つらみ

メールの日付とは何か

getDate()で日付が取れるけど、これもよくわからない。
仕様書には

The date and time of this message.

と書いてあるけど、ざっくりしすぎ。。 受信したシステム日付であることを祈る。

ちなみに日付が送信者の言い値だと辛い。
メールサーバが滞留して1日遅れで届いた場合に、昨日の日付のメールが急に届く。例えば「1時間毎に試着メールをチェックする」みたいな処理を仕掛けると、昨日の日付のメールが急に届いてもメールのチェック対象から外れてロストする。。

本文が取れない

本文はgetPlainBody()で取れるが、本文サイズが大きすぎると取得できない。(仕様にないけど...) なので例えばシステムから飛んでくるメールに対して

「エラー」って文字列があったらslackに通知する

みたいな処理を書いていて本文サイズが大きくて取得できなかった場合、これも処理されずにロストする。

まとめ

GASは手軽で便利だけど、危険がいっぱいだから慎重に使おう。

webサイトのタイトルとURLを取得するブックマークレット

ネットで見つけた記事をSNSやチャットで共有したい。
今時共有機能なんてどんなアプリにも付いてるだろと思うけど、実際はURLしか共有されない。タイトルも共有してほしいんだけど。。
表示するアプリによっていはOGP(Open Graph Protcol)とかで補完されてタイトルも表示されるけど、対応してない場合もあって微妙。
ってことで、ブックマークレット作った。

共有ブックマークレット

chrome

javascript:prompt('', `${document.title}\n${location.href}`)

chromeはalertの内容をコピペできないのでpromtを使う。

firefox

javascript:alert(`${document.title}\n${location.href}`)

firefoxのpromptはテキストの改行が効かないからalertを使う。

チャットワーク用

ちなみにチャットワークに共有するならinfoタグを使うと良い。

chrome

javascript:prompt('', `[info][title]${document.title}[/title]${location.href}\n[/info]`)

chromeはalertの内容をコピペできないのでpromtを使う。

firefox

javascript:alert(`[info][title]${document.title}[/title]${location.href}\n[/info]`)