Apps Scriptリファレンス: Apps Script Reference |障害・課題追跡: IssueTracker |Google Workspace: Status Dashboard - Summary

2018年5月31日木曜日

気になった設定やぶつかった壁の越え方を書き残して行くページ


題名の通り個人的な備忘録
+同じ壁にぶつかっている人の役に立つかも


Google フォトと Google ドライブの連携
 デスクトップ > Google ドライブ > 設定 > 「Google フォト」フォルダを作成する] をクリック
 Android端末 > Google フォト > 設定 > バックアップと同期 > モバイル通信でのバックアップ > 写真をON


ダウンロードを保留しています(Android Google Play)
アップデート保留中で更新中のアプリがあるのが原因の場合
 Android端末 > Google Playホーム画面の左上の三本線メニュー > マイアプリ&ゲーム > アップデート
 →アップデート保留中の右にある「停止」ボタンをクリックして一度停止する
 →ダウンロードしたいアプリを再度ダウンロードしてみる

2018年5月27日日曜日

opacityで要素を透明にする


要素をこのように半透明にしてみる






コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
  <head>
    <style>
    .opacity_05 {
      opacity: 0.5;
    }
    </style>
  </head>
  <body>
    <button>通常ボタン</button>
    <button class="opacity_05">半透明ボタン</button>
  </body>
</html>
意訳
 



opacity_05のスタイル
半透明にする




通常ボタン
半透明ボタン




classListでスタイルをadd, remove, toggle, containsする


classListで要素のスタイルを操作してみる


デモ
(Apps ScriptのWebアプリで作ったもの)



コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
  <head>
    <style>
    .bg_pink {
      background-color: pink;
    }
    </style>
  </head>
  <body>
    <textarea id="ta"></textarea><br>
    <button id="add_bt">add</button>
    <button id="remove_bt">remove</button>
    <button id="toggle_bt">toggle</button>
    <button id="contains_bt">contains</button>
    
    <script>
    document.getElementById("add_bt").onclick = add_bt_clicked;
    document.getElementById("remove_bt").onclick = remove_bt_clicked;
    document.getElementById("toggle_bt").onclick = toggle_bt_clicked;
    document.getElementById("contains_bt").onclick = contains_bt_clicked;
    var ta = document.getElementById("ta");
    
    function add_bt_clicked(){
      ta.classList.add("bg_pink");
    }
    
    function remove_bt_clicked(){
      ta.classList.remove("bg_pink");
    }
    
    function toggle_bt_clicked(){
      ta.classList.toggle("bg_pink");
    }
    
    function contains_bt_clicked(){
      ta.value = ta.classList.contains("bg_pink");
    }
    </script>
  </body>
</html>
意訳
 



bg_pinkのスタイル
背景色をpinkにする




テキストエリア
addボタン
removeボタン
toggleボタン
containsボタン


addボタンをクリックしたらadd_bt_clickedを実行する
removeボタンをクリックしたらremove_bt_clickedを実行する
toggleボタンをクリックしたらtoggle_bt_clickedを実行する
containsボタンをクリックしたらcontains_bt_clickedを実行する
idがtaの要素を取得する

この機能がやること
idがtaのテキストエリアにbg_pinkのスタイルを追加する


この機能がやること
idがtaのテキストエリアからbg_pinkのスタイルを削除する


この機能がやること
idがtaのテキストエリアにbg_pinkのスタイルがなければaddしてあればremoveする


この機能がやること
idがtaのテキストエリアにbg_pinkのスタイルがあればtrueしてなければfalseを返す






2018年5月26日土曜日

指定した要素の子要素の最後の要素を取得したい


  1. elem.childrenで指定した要素の子要素を取得
  2. elem.children.lengthでその数を取得
  3. elem.children[len - 1]でその最後の要素を取得

という感じでinnerHTMLまで取得してみる



コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
  <body>
    <div id="main_div">
      <div>あめ</div>
      <div>ちょこ</div>
      <div>がむ</div>
    </div>
  </body>
  <script>
  get_last_elem("main_div")
  function get_last_elem(id){
    var elem = document.getElementById(id);
    var len = elem.children.length;
    var last_elem = elem.children[len - 1];
    alert(last_elem.innerHTML);
  }
  </script>
</html>
意訳
 


main_divの要素
あめ div
ちょこ div
がむ div



get_last_elem()でmain_divの最後の要素を取得する
この機能がやること
受け取ったidから一致する要素を取得して
その要素の子要素の数を取得して
その要素の最後の子要素を取得して
innerHTMLをアラートに出す





.getElementsByTagNameで取得する


index.html
<!DOCTYPE html>
<html>
  <body>
    <div id="main_div">
      <div>あめ</div>
      <div>ちょこ</div>
      <div>がむ</div>
    </div>
  </body>
  <script>
  get_last_elem("main_div")
  function get_last_elem(id){
    var elem = document.getElementById(id);
    var divs = elem.getElementsByTagName("div");
    var len = divs.length;
    var last_elem = divs[len - 1];
    alert(last_elem.innerHTML);
  }
  </script>
</html>


参考

ParentNode.children
https://developer.mozilla.org/ja/docs/Web/API/ParentNode/children

document.getElementsByTagName
https://developer.mozilla.org/ja/docs/Web/API/Document/getElementsByTagName

スタイルを複数設定するときの備忘録


今回設定したスタイル


.blue が設定されていたら青文字
.bt が設定されていたら背景色は灰色
.blueか.btどちらかが設定されていたら太文字
.blueと.btの両方が設定されていたら背景色はピンク


.blue.bt
と書くとblueとbtの両方が設定されているときのスタイル

.blue, .bt
と書くとblueまたはbtのどちらかが設定されているときのスタイル(共通のスタイル)



コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
<head>
  <style>
  .blue {
    color: blue;
  }
  
  .bt {
    width: 60px;
    height: 30px;
    background-color: lightgrey;
    border: none;
  }
  
  .blue.bt {
    background-color: lightblue;
  }

  .blue, .bt {
    font-weight: bold;
  }
</style>
</head>
<body>
  <label class="blue">ラベル</label>
  <button class="bt">ボタン</button>
  <button class="blue bt">ボタン</button> 
</body>
</html>
意訳
 



blueのスタイル



btのスタイル





  
blueとbt両方が設定されているときのスタイル



blueとbtに共通のスタイル





ラベルにblueのスタイルを設定
ボタンにbtのスタイルを設定
ボタンにblueとbtのスタイルを設定





2018年5月25日金曜日

A,B列の値を5行ずつ処理してC列に入れる


良いタイトルが浮かばなかったのでそのままですが
たまにこういう処理をしたくなって。

トリガーに登録したら6分の壁を越える時にも使える。



コード.gs
/************************************
A列とB列のデータをもとに何か処理を施して
C列に結果を入れる
これを一つずつやるとgetValue()とsetValue()が多すぎてアラートが出る
かといって一気にあるだけ全部をgetLastRow()とgetValues()で取得してsetValues()でやるには多すぎたり
そんなときにとりあえず5行ずつやってみようというコード

この例ではA列とB列を足してC列に入れるだけの処理を書いています
************************************/
var ss_url = "URL";
function get_step_by_step(){
  var ss = SpreadsheetApp.openByUrl(SS_URL);
  var sh = ss.getSheets()[0];
  var last_row = get_last_row(sh) + 1;
  var step = 4;
  var valueA = sh.getRange("A" + last_row + ":A" + (last_row + step)).getValues();
  var valueB = sh.getRange("B" + last_row + ":B" + (last_row + step)).getValues();
  Logger.log([last_row, valueA.length, valueA, valueB])
  for(var i = 0; i < valueA.length; i++){
    var valueC = valueA[i] + valueB[i];
     sh.getRange("C" + (last_row + i)).setValue(valueC);
  }
}

function get_last_row(sh) {
  var values = sh.getRange("C:C").getValues();
  for (var i = values.length - 1; i >= 0; i--) {
    if (values[i] != "") {
      break;
    }
  }
  var last_row = i + 1;
  return last_row;
}



A, B列に1~9まで入力しておいて
get_step_by_step() を実行すると
C列には結合した値が入力される



参考

JIRA APIでステータスをtransitionで更新する


JIRAのstatusをAPIで更新するにはtransitionでやるらしい
https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-transitions-post

  1. transitoin idを確認する
    https://SITENAME.atlassian.net/rest/api/2/issue/ISSUE_KEY/?expand=transitions.fields
  2. transition idをPOSTで更新する



コード.gs
function get_jira_token() {
  var id = "LOGIN_EMAIL";
  var pw = "LOGIN_PASSWORD";
  var jira_token = Utilities.base64Encode(id + ":" + pw);
  return jira_token;
}

function get_base_url() {
  var base_url = "https://SITENAME.atlassian.net/rest/api/2/issue/";
  return base_url;
}

/************************************
まずはtransitionsを取得してログに出して変更したいステータスのidを見つける
...{"id":"31","name":"完了"...
などと取得できる
************************************/
function get_issue() {
  var token = get_jira_token();
  var key = "KEY-1";
  var options = {
    contentType: "application/json",
    headers: {"Authorization": " Basic " + token}
  };
  var url = get_base_url() + key + "?expand=transitions.fields";
  var response = UrlFetchApp.fetch(url, options);
  Logger.log(response);
}

/************************************
transitionを変更する
var transitions = {"transition": {"id": "31"}}
などと書いて変更する
************************************/
function change_transition() {
  var token = get_jira_token();
  var payload = get_payload();
  var options = get_options(token, payload);
  var url = get_url();
  UrlFetchApp.fetch(url, options);
}

function get_url(){
  var key = "KEY-1";
  var url = get_base_url() + key + "/transitions";
  return url;
}

function get_payload(values) {
  var transitions = {"transition": {"id": "31"}}
  var payload = JSON.stringify(transitions);
  return payload;
}

function get_options(token, payload) {
  var options = {
    method: "post",
    payload: payload,
    contentType: "application/json",
    headers: {"Authorization": " Basic " + token}
  }
  return options;
}


参考

Do transition
https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-transitions-post


JIRA APIでカスタムフィールドの値を更新する


JIRAのAPIで特定のカスタムフィールドの値を変更したくて書いたコードです。



コード.gs
function get_jira_token() {
  var id = "LOGIN_EMAIL";
  var pw = "LOGIN_PASSWORD";
  var token = Utilities.base64Encode(id + ":" + pw);
  return token;
}

function get_base_url() {
  var base_url = "https://SITENAME.atlassian.net/rest/api/2/issue/";
  return base_url;
}

function update() {
  var token = get_jira_token();
  var payload = get_payload();
  var options = get_options(token, payload);
  var url = get_url();
  UrlFetchApp.fetch(url, options);
}

function get_url(){
  var key = "KEY-1";
  var url = get_base_url() + key;
  return url;
}

function get_payload(values) {
  var email = Session.getActiveUser().getEmail();
  var data = {
    customfield_10025: "対象のカスタムフィールドに入れる値"
  };
  var fields = {fields: data};
  var payload = JSON.stringify(fields);
  return payload;
}

function get_options(token, payload) {
  var options = {
    method: "PUT",
    payload: payload,
    contentType: "application/json",
    headers: {"Authorization": " Basic " + token}
  }
  return options;
}



上記コード内の以下の値を設定してから

  • LOGIN_EMAIL
  • LOGIN_PASSWORD
  • SITENAME

update() を実行すると以下のようにカスタムフィールドの値が更新できる



参考

JIRA REST API examples
https://developer.atlassian.com/server/jira/platform/jira-rest-api-examples/

2018年5月17日木曜日

画面遷移前にアラートを出す


今いるページで何か操作をしてから画面遷移する前に以下のようなダイアログを出したい




デモ


上のテキストエリアに何か文字を入力してから
このページを閉じたり更新したり別のページへ移動したり
しようとすると確認ダイアログが表示されます



コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
<body>
  <textarea></textarea>
  <script>
  window.onbeforeunload = function(e) {
    return e.returnValue;
  }
  </script>
</body>
</html>

意訳
 


テキストエリア

画面遷移前に
確認ダイアログを返す







補足


参考にしたサイトではダイアログにメッセージを表示できそうにかかれていましたが
手元で試したところメッセージは渡せずに

行った変更が保存されない可能性があります。

という固定の文字列しか表示できなかった。


こういうhtmlを試してみた


index.html
<!DOCTYPE html>
<html>
<body>
  <textarea></textarea>
  <script>
  window.onbeforeunload = function(e) {
    var dialogText = '本当にこのページを離れていいですか?';
    e.returnValue = dialogText;
    return dialogText;
  };
  </script>
</body>
</html>

本当にこのページを離れていいですか?

はダイアログに表示されず

行った変更が保存されない可能性があります。

が表示された。


2018年5月13日日曜日

claspで.clasp.jsonが作成されずScript IDが紐付かない時にやったこと


やったこと

  • なぜか一つ上の階層に.clasp.jsonが存在していたのでそれを消した
  • .clasp.jsonが作成されていなかったディレクトリの中身を空にしてcloneし直す
    • すると.clasp.jsonが作成されてscript IDも保存された




2018年5月12日土曜日

macOS High Sierraで隠しファイルを表示する


キーボードショートカット

commad + shift + .
(コマンド+シフト+ピリオド)


コマンドライン

フォルダに移動して
$ ls -a


参考


「macOS High Sierraで隠しファイルを表示する」
でググるとたくさんヒットしましたが
Mac のキーボードショートカットには載っていませんでした

情報源はどこなんだろう。。
Communitiesで質問と回答は見つかった↓
https://discussions.apple.com/thread/7581737

claspを使ってみる


claspをMacにインストールして使ったときの手順

公式の手順に沿ってやってみる
https://codelabs.developers.google.com/codelabs/clasp/#0

$ sudo npm install n -g
$ sudo n latest
$ npm i @google/clasp -g
→npm update check failed                       │
│                 Try running with sudo or get access                 │
│                to the local update config store via                 │
│ sudo chown -R $USER:$(id -gn $USER) /Users/NAME/.config

と言われたので
$ sudo npm i @google/clasp -g
$ clasp
$ clasp login

→ブラウザが起動してログインすると
Logged in! You may close this page.と出る

フォルダを作ってそこに新規スクリプトファイルを作成する
$ mkdir clasp_codelab && cd clasp_codelab
$ clasp create "clasp Codelab"
→Error: Permission denied. Enable the Apps Script API:

と言われるので
https://script.google.com/home/usersettings
でAPIをオンにしてリトライ
$ clasp create "clasp Codelab"

これで新規スクリプトファイルは作成できた


ドライブ内のファイルを開くには
$ clasp open


オンラインのプロジェクトをローカルに持ってくるときは
$ clasp clone SCRIPT ID


オンラインのプロジェクトを編集してからローカルに反映するときは
$ clasp pull


ローカルのプロジェクトを編集してオンラインに反映するときは
$ clasp push

このあとオンラインのプロジェクトを再読込すると反映を確認できる


バージョンを保存するときは
$ clasp version "説明"


Webアプリをデプロイする場合
$ clasp deploy 2 "Second deployment"

一度もデプロイされていないとこのコマンドは使えないらしい
(スクリプトエディタ上で事前に一度アプリをデプロイしておく必要がある)





参考

Command Line Interface using clasp
https://developers.google.com/apps-script/guides/clasp

clasp - The Apps Script CLI
https://codelabs.developers.google.com/codelabs/clasp/#0

GitHub google/clasp
https://github.com/google/clasp

G Suite Developers Blog
https://gsuite-developers.googleblog.com/2018/01/three-new-tools-to-help-improve-your.html?m=1

2018年5月7日月曜日

改行で区切った文字列からJSON形式の文字列を作成する


テキストを入力して
JSON作成ボタンをクリックしたら
JSON文字列が作成される




というアプリケーション







コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
<head>
  <style>
    .ta {
      width: 360px;
      height: 120px;
    }
  </style>
</head>
<body>
  <textarea id="input_text" class="ta"></textarea><br>
  <button id="create_json_bt">JSON作成</button><br>
  <textarea id="json_text" class="ta"></textarea><br>
  <script>
    var input_text = document.getElementById("input_text");
    var json_text = document.getElementById("json_text");
    var create_json_bt = document.getElementById("create_json_bt");
    create_json_bt.onclick = create_json_bt_clicked;

    function create_json_bt_clicked() {
      var text = input_text.value;
      var text_split = text.split("\n");
      var array = [];
      for (var i = 0; i < text_split.length; i++) {
        var obj = {}
        obj["text"] = text_split[i];
        array.push(obj);
      }
      var json = JSON.stringify(array);
      json_text.value = json;
    }
  </script>
</body>
</html>
意訳
 



taのスタイル
幅
高さ




入力エリア
JSON作成ボタン
JSON出力エリア

idがinput_textの要素を取得する
idがjson_textの要素を取得する
idがcreate_json_btの要素を取得する
create_json_btがクリックされたらcreate_json_bt_clickedを実行する

この機能がやること
input_textの値を取得して
改行で区切って
結果を入れる配列を用意して
区切った値の数だけ以下を繰り返す
オブジェクトの入れ物を用意して
{"text": text_split[i]の値}を作って
arrayに追加する

arrayをJSONに変換して
JSONの出力エリアに入れる







2018年5月6日日曜日

オブジェクトを二次元配列にして返す


このようなオブジェクトを
var obj = [
  {"en": "Aries", "ja": "おひつじ座"},
  {"en": "Taurus", "ja": "おうし座"},
  {"en": "Gemini", "ja": "ふたご座"},
  {"en": "Cancer", "ja": "かに座"},
  {"en": "Leo", "ja": "しし座"},
  {"en": "Virgo", "ja": "おとめ座"},
  {"en": "Libra", "ja": "てんびん座"},
  {"en": "Scorpius", "ja": "さそり座"},
  {"en": "Sagittarius", "ja": "いて座"},
  {"en": "Capriconus", "ja": "やぎ座"},
  {"en": "Aquarius", "ja": "みずがめ座"},
  {"en": "Pisces", "ja": "うお座"}
]


このような配列にして返したり
[[Aries, おひつじ座], [Taurus, おうし座], [Gemini, ふたご座], [Cancer, かに座], [Leo, しし座], [Virgo, おとめ座], [Libra, てんびん座], [Scorpius, さそり座], [Sagittarius, いて座], [Capriconus, やぎ座], [Aquarius, みずがめ座], [Pisces, うお座]]


このように改行を入れて返したり
Aries,おひつじ座
Taurus,おうし座
Gemini,ふたご座
Cancer,かに座
Leo,しし座
Virgo,おとめ座
Libra,てんびん座
Scorpius,さそり座
Sagittarius,いて座
Capriconus,やぎ座
Aquarius,みずがめ座
Pisces,うお座

したくて書いたコードです



コード.gs
var obj = [
  {"en": "Aries", "ja": "おひつじ座"},
  {"en": "Taurus", "ja": "おうし座"},
  {"en": "Gemini", "ja": "ふたご座"},
  {"en": "Cancer", "ja": "かに座"},
  {"en": "Leo", "ja": "しし座"},
  {"en": "Virgo", "ja": "おとめ座"},
  {"en": "Libra", "ja": "てんびん座"},
  {"en": "Scorpius", "ja": "さそり座"},
  {"en": "Sagittarius", "ja": "いて座"},
  {"en": "Capriconus", "ja": "やぎ座"},
  {"en": "Aquarius", "ja": "みずがめ座"},
  {"en": "Pisces", "ja": "うお座"}
]

function make_arrays(){
  var keys = Object.keys(obj[0]);
  var arrays = [];
  for(var i = 0; i < obj.length; i++){
    var array = [];
    for(var j = 0; j < keys.length; j++){
      var key = keys[j];
      array.push(obj[i][key]);
    }
    arrays.push(array);
  }
  Logger.log(arrays);
  var rows = make_rows(arrays);
  Logger.log(rows);
}

function make_rows(arrays){
  var rows = "";
  for(var i = 0; i < arrays.length; i++){
     rows += arrays[i] + "\n";
  }
  return rows;
}
意訳
JSONを用意する














この機能がやること
objのkeysを取得して(例の場合はenとjaがkey)
結果を入れる配列を用意して
objの数だけ以下を繰り返す
array配列を用意して
keysの数だけ以下を繰り返す(例の場合はenとjaなのでkeysの数は2)
keyにkeysの値を1つずつ入れて
array配列に追加する

arrays配列にarrayを追加する

arraysをログに出す
arraysをmake_rowsに渡して
結果をログに出す


この機能がやること
rowsという入れ物を用意して
arraysの数だけ繰り返す
rowsにarraysの値をひとつずつ改行を付けて足して

rowsを返す



実行結果


テキストエリアの値をローカルにダウンロードする


テキストエリアに入力したテキストをローカルにダウンロードしたい







コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
  <body>
    <textarea id="ta"></textarea><br>
    <input type="text" id="file_name" value="ファイル名">
    <input type="text" id="content_type" value="text/plain"><br>
    <button id="button">Download</button>
    <script>
  var download_button = document.getElementById("button");
  download_button.onclick = download;

  function download() {
    var data = document.getElementById("ta").value;
    var file_name = document.getElementById("file_name").value;
    var content_type = document.getElementById("content_type").value;
    var blob = new Blob([data], {"type":content_type});
    
    var link = document.createElement("a");
    link.download = file_name;
    link.href = window.URL.createObjectURL(blob);
    link.click();
  }
    </script>
  </body>
</html>
意訳
 


テキスト入力エリア
ファイル名を入れるテキストボックス
ContentTypeを指定するテキストボックス
ダウンロードボタン

idがbuttonの要素を取得して
クリックされたらdownloadを実行する

この機能がやること
テキストエリアの値を取得して
ファイル名を取得して
content_typeを取得して
Blobに入れて

a要素を作成して
ダウンロードするファイル名に指定して
URLを作成して
クリックしてダウンロードする






2018年5月5日土曜日

RawGitでgithubに置いた.jsを読み込む


RawGitでgithubに置いた.jsを読み込んでみたときの備忘録

https://rawgit.com/
で対象のファイルのURLを貼り付けると

production と developmentの欄にURLが表示される


試してみたこと

テストで何度もコードを更新する場合はdevelopmentのURLを使うと更新されたコードが読み込まれる
→トラフィックが多すぎると制限される


productionはトラフィックの制限はないが、コードを更新しても更新されたコードは読み込まれない
→更新されたコードを読み込む場合は更新後に再度対象のgithubのURLを貼り付けると新しいURLが表示される(URL内にあるcommit hashが更新される)







コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
<head>
  <script type="text/javascript" src="https://cdn.rawgit.com/prepractice/javascript/a2763ed6/library/array_unique_count.js"></script>
</head>
<body>
  <script>
    unique_count();
    function unique_count() {
      var values = ["hello", "hi", "hey", "hey", "hey", "hello"];
      var obj = arrayUniqueCount(values);
      alert(JSON.stringify(obj));
    }
  </script>
</body>
</html>
意訳
 


RawGitサイトで作られたproductionURLをスクリプトで読み込む



unique_count()を実行する
この機能がやること
配列を用意する
arrayUniqueCountに渡して結果を取得して
アラートに出す






実行結果



今回試した自作のコード

配列を渡すと要素の値をユニークにしてその数をカウントしてオブジェクトで返す
https://github.com/prepractice/javascript/blob/master/library/array_unique_count.js


参考

RawGit
https://rawgit.com/

2018年5月4日金曜日

Custom Search APIで画像の検索結果を取得する


Custom Search APIの設定をしてから
以下の検索URLの「取得したAPIKEY」「取得したCSE_ID」「検索したいテキスト」を置き換えてブラウザで開くと検索結果を得られる


検索URL:
https://www.googleapis.com/customsearch/v1?key=取得したAPIKEY&cx=取得したCSE_ID&searchType=image&q=検索したいテキスト




Google Custom Search APIの設定

1日あたり100件の検索クエリまでは無料など、利用上の制限もあるようです
http://www.pre-practice.net/2017/12/google-custom-search-api.html#limit



HTML Serviceで試したときの検索例を書き残しておきます

このアプリケーションでできること

  1. テキストボックスに検索テキストを入力して
  2. 検索ボタンを押すと
  3. 検索URLをUrlFetchして内容を取得して
  4. title, link, snippet, contextLinkなどを取得して
  5. HTML要素を作って結果を表示する




コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile('index');
}

var API_KEY = "取得したAPI_KEY";
var CSE_ID = "取得したCSE_ID";

var BASE_URL = "https://www.googleapis.com/customsearch/v1?key=";
var CX = "&cx=";
var IMAGE = "&searchType=image";
var Q = "&q=";

function get_value_gs(query) {
  var url = BASE_URL + API_KEY + CX + CSE_ID + IMAGE + Q + encodeURIComponent(query);
  var fetch = UrlFetchApp.fetch(url).getContentText();
  var obj = JSON.parse(fetch);
  var items = obj["items"];
  var array = [];
  for (var i = 0; i < items.length; i++) {
    var obj_items = {};
    var item = items[i];
    var title = item["title"];
    var link = item["link"];
    var snippet = item["snippet"];
    var context_link = item["image"]["contextLink"];
    obj_items["title"] = title;
    obj_items["link"] = link;
    obj_items["snippet"] = snippet;
    obj_items["contextLink"] = context_link;
    
    array.push(obj_items);
  }
  return array;
}
意訳
この機能がやること
指定したHTMLファイルを表示する


取得したAPI_KEY
取得したCSE_ID

BASE_URL
CX
IMAGE
Q

この機能がやること
受け取ったqueryでurlを作ってエンコードして
customsearchAPIから返ってきた内容を取得して
JSONを解析してオブジェクトに変換して
itemsを取得して
結果の入れ物を配列で用意して
itemsの数だけ以下を繰り返す
obj_itemsという名で新規オブジェクトを作る
itemsを一つずつ取得して
titleを取得して
linkを取得して
smippetを取得して
imageのcontextLinkを取得して
obj_items { "title": title,
"link": link,
"snippet": snippet,
"contextLink": contextLink} を作る

array配列にobj_itemsを追加する

出来上がったarrayを返す



上のコード.gsはHTML Serviceで使うためにqueryを受け取ってarrayを返してしています


HTML ServiceでUIを作る


index.html
<!DOCTYPE html>
<html>
<head>
  <style>
    .card {
      width: 25%;
      height: auto;
      padding: 15px;
      border-radius: 2px;
      border: solid 1px lightgray;
      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
      overflow-wrap: break-word;
    }

    .break_word {
      overflow-wrap: break-word;
    }

    .flex_div {
      display: flex;
      flex-direction: row;
    }
    
    .img_size {
      width: 60%;
      height: auto;
    }
    
    .underline_none {
      text-decoration: none;
    }
  </style>
</head>
<body>
  <input type="text" id="tb">
  <button id="bt">画像検索</button>
  <div id="main_div"></div>

  <script>
    var bt = document.getElementById("bt");
    var tb = document.getElementById("tb");
    bt.onclick = bt_clicked;

    function bt_clicked() {
      var value = tb.value;
      google.script.run
        .withFailureHandler(onFailure)
        .withSuccessHandler(onSuccess)
        .get_value_gs(value);
    }

    function onSuccess(array) {
      create_elements(array);
    }

    function onFailure(e) {
      alert(["error" + e.message, e.stack]);
    }

    function create_elements(array) {
      for(var i = 0; i < array.length; i++){
        var div_h = create_div_horizontal();
        var div_img_card = create_card(div_h);
        create_image(div_img_card, array[i]["link"]);
        var div_link_card = create_card(div_h);
        create_link(div_link_card, array[i]["title"], array[i]["contextLink"]);
        var div_text_card = create_card(div_h);
        create_text_div(div_text_card, tb.value);
       }
    }

    function create_div_horizontal() {
      var main_div = document.getElementById('main_div');
      var div_h = document.createElement("div");
      div_h.setAttribute("class", "flex_div");
      main_div.appendChild(div_h);
      return div_h;
    }

    function create_card(div_h) {
      var div_link_card = document.createElement("div");
      div_link_card.setAttribute("class", "card");
      div_h.appendChild(div_link_card);
      return div_link_card
    }

    function create_link(div_h, title, item) {
      var link = document.createElement("a");
      link.setAttribute("class", "underline_none");
      link.setAttribute("href", item);
      link.setAttribute("target", "_blank");
      link.textContent = title;
      div_h.appendChild(link);
    }

    function create_image(div_img_card, item) {
      var link = document.createElement("a");
      var img = document.createElement("img");
      link.setAttribute("href", item);
      link.setAttribute("target", "_blank");
      img.setAttribute("class", "img_size");
      img.setAttribute("src", item);
      img.textContent = item;
      
      link.appendChild(img);
      div_img_card.appendChild(link);
    }

    function create_text_div(div_h, text) {
      var div = document.createElement("div");
      div.textContent = text;
      div_h.appendChild(div);
    }
  </script>
</body>
</html>



2018年5月2日水曜日

文字列の中から漢字を抽出する正規表現を考えてみる /[々〆〇〻㐂-頻]+/g


本文がちょっと込み入っているので、まずは結果から書きます

今回書いた漢字を抽出する正規表現はこれ

/[々〆〇〻㐂-頻]+/g



調べてみると漢字の範囲は奥が深い

ひらがなやカタカナのように「ここからここまで」という始まりと終わりの文字がよくわからない

ひらがな:「あ」〜「ん」
カタカナ:「ア」〜「ン」
漢字:「?」〜「?」


今回調べて知ったこと・わかったこと等
  • 常用漢字やJIS第1水準〜第4水準の漢字を文化庁のホームページやwikipediaで知った
    (なんとなく知ってはいたが改めて調べて知った)
  • Unicodeは世界中の文字を1文字2バイトで65536字で表そうとしていた
    • でも65536字では足りないことに気づいた
  • 65536字では足りないので4バイトで1文字を表すサロゲートペアが作られた
  • 常用漢字の中で唯一のサロゲートペアは「𠮟」
    • ちなみに別の文字の「叱」は常用漢字ではないがJIS第1水準漢字
  • 今回書いた [㐂-頻] はJIS第1水準〜第4水準の漢字だけを絞っているわけではない
    • それ以外の漢字も範囲に含まれる
    • この範囲は個人的に絞ったものなので一般的ではない



今回書いた正規表現で一致させたい漢字
  • 㐂-頻の範囲に含まれるすべての漢字
    • 注意点としてはJIS第1水準〜第4水準の漢字だけではないということ
    • JIS第1水準〜第4水準の漢字を数値に置き換えて昇順に並び替えると先頭が 㐂 で末尾が 頻 ですが、その範囲内には第1〜第4に含まれない漢字も含まれる
    • サロゲートペアも含まれる
  • 々:上記の範囲に含まれないため直接追加
  • 〆:「しめ」「閉め」「締め」「絞め」「占め」などを表す
  • 〇:漢字のゼロ
  • 〻:現在は「々」で代用されることもある(上字の訓を繰り返す)

※JIS第1水準〜第4水準の漢字だけを抽出するにはこの正規表現では不十分です



Unicode 10.0 Character Code Charts
http://www.unicode.org/charts/ を見ると

2018/01/29現在以下のような種類があり今後ExtensionG以降も増えそう
  1. CJK Unified Ideographs (Han) Range: 4E00–9FEA 統一漢字
  2. CJK Extension-A Range: 3400–4DB5 拡張A
  3. CJK Extension B Range: 20000–2A6D6 拡張B
  4. CJK Extension C Range: 2A700–2B734 拡張C
  5. CJK Extension D Range: 2B740–2B81D 拡張D
  6. CJK Extension E Range: 2B820–2CEA1 拡張E
  7. CJK Extension F Range: 2CEB0–2EBE0 拡張F
  8. CJK Compatibility Ideographs Range: F900–FAFF 互換漢字
  9. CJK Compatibility Ideographs Supplement Range: 2F800–2FA1F 互換漢字補足
  10. CJK Radicals / KangXi Radicals Range: 2F00–2FDF 部首
  11. CJK Radicals Supplement Range: 2E80–2EFF 部首補足
  12. CJK Strokes Range: 31C0–31EF おそらく一画ごとの表
  13. Ideographic Description Characters Range: 2FF0–2FFF おそらく配置
CJKはChinese Japanese Koreanの頭文字


wikipediaでCJK統合漢字を見てみるとだいぶ複雑な歴史を経ている


16進数で表してみる


/[々〆〇〻㐂-頻]+/g



16進数で表す場合は、おそらくこのようになる

/[\u3005\u3006\u3007\u303b\u3402-\uFA6A\uD840-\uD869\uDC02-\uDFFF]+/g


それぞれの16進数が意味している文字は

3005:々
3006:〆
3007:〇
303B:〻
3402:㐂
FA6A:頻
D840:𠀋の上位サロゲート(範囲内のサロゲートペアの上位サロゲートで一番小さい)
D869:𪚲の上位サロゲート(範囲内のサロゲートペアの上位サロゲートで一番大きい)
DC02:𦐂の下位サロゲート(範囲内のサロゲートペアの下位サロゲートで一番小さい)
DFFF:𣟿の下位サロゲート(範囲内のサロゲートペアの下位サロゲートで一番大きい)


今回書いた正規表現を使って漢字を抽出してみる

コード.gs
function get_kanji(){
  var str = "abcABC123abcABC123かなカナカナ仮名ひらがなカタカナカタカナ漢字";
  var pattern = /[々〆〇〻㐂-頻]+/g;
  var result = str.match(pattern);
  Logger.log(result);
}
意訳
この機能がやること
探索対象の文字列を用意しておく
今回書いた漢字のパターン
一致するものを探して
ログに出す



実行結果




参考

Unicode 10.0 Character Code Charts
http://www.unicode.org/charts/


wikipedia
CJK統合漢字
https://ja.wikipedia.org/wiki/CJK%E7%B5%B1%E5%90%88%E6%BC%A2%E5%AD%97

常用漢字一覧
https://ja.wikipedia.org/wiki/%E5%B8%B8%E7%94%A8%E6%BC%A2%E5%AD%97%E4%B8%80%E8%A6%A7

漢字
https://ja.wikipedia.org/wiki/%E6%BC%A2%E5%AD%97


常用漢字表(平成22年内閣告示第2号)
http://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kijun/naikaku/kanji/


日本漢字能力検定
級別漢字表
http://www.kanken.or.jp/kanken/outline/data/outline_degree_national_list.pdf

正規表現を書いてmatchで抽出する


正規表現を変数にして一致する文字を出力するUIを作ってみる


デモ




.match( )





コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
  <head>
    <style>
    .ta {
      width: 360px;
      height: 120px;
    }
    
    .flag {
      width: 60px;
    }
    
    .text_dimgray {
      color: dimgray;
    }
    
    .text_orange {
      color: darkorange;
      font-weight: bold;
      font-size: 16px;
    }

    .regexp {
      font-size: 16px;
    }
    </style>
  </head>
  <body>
    <label class="text_dimgray">この文字列の中から一致する文字列を探したい</label>
    <br>
    <textarea id="input" class="ta"></textarea>
    <br>
    <div class="regexp">.match(
      <label class="text_orange">/
        <input type="text" id="tb" class="text_orange" placeholder="一致させたい文字列">/ 
        <input type="text" id="tb_flag" class="flag text_orange" placeholder="g,i,m等">
      </label>) 
    </div>
    <br>
    <label class="text_dimgray">一致する文字列</label>
    <br>
    <textarea id="output" class="ta"></textarea>
    <script>
    var tb = document.getElementById("tb");
    var tb_flag = document.getElementById("tb_flag");
    var input = document.getElementById("input");
    var output = document.getElementById("output");
    
    tb.onkeyup = get_result;
    tb_flag.onkeyup = get_result;
    
    function get_result() {
      var target = tb.value;
      var flag = tb_flag.value;
      var pattern = new RegExp(target, flag);
      output.value = input.value.match(pattern);
    }
    </script>
  </body>
</html>
意訳
 



taのスタイル
幅
高さ


flagのスタイル
幅


text_dimgrayのスタイル
文字色


text_orangeのスタイル
文字色
文字の太さ
文字サイズ


regexpのスタイル
文字サイズ




ラベル

インプットエリア

正規表現を入力するエリア






ラベル

出力エリア

idがtbの要素を取得
idがtb_flagの要素を取得
idがinputの要素を取得
idがoutputの要素を取得

tbでキーが上がったらget_resultを実行する
tb_flagでキーが上がったらget_resultを実行する

この機能がやること
tbのvalue(正規表現)を取得して
tb_flagのvalue(フラグ)を取得して
正規表現のオブジェクトを作成して
output(出力エリア)に一致する文字列を出力する







2018年5月1日火曜日

配列内の要素の組み合わせを列挙する(ペアのパターン)


["いちご", "めろん", "もも", "ぶどう"]

という配列の要素から2つ取り出した時の組み合わせ(nCr)のパターンを列挙してみる


このような感じで
[
  [いちご めろん, いちご もも, いちご ぶどう],
  [めろん もも, めろん ぶどう],
  [もも ぶどう]
]


簡単に書けそうな気がして書いてみたら意外と考えさせられました

その1とその2の実行結果は同じです



組み合わせのパターンの数だけを出す場合



コードを書かずにすぐに使えるデモもほしくて作りました


(このデモはブログに直接HTML/CSS/JSを書いたもの)




上の入力エリアに

いちご
めろん
もも
ぶどう

と入れてget pairsボタンをクリックすると
下の出力エリアに半角スペースでつながった2つのワードのパターンが出力される

いちご めろん,いちご もも,いちご ぶどう,めろん もも,めろん ぶどう,もも ぶどう


デモの入力例


.gs内でパターンのログを出してみる

コードの例 その2


下にあるその1よりも短く書いたコード


コード2.gs
function get2word_patterns() {
  var array = ["いちご", "めろん", "もも", "ぶどう"];
  var patterns = [];
  for (var a = 0; a < array.length - 1; a++) {
    var pattern = [];
    for (var i = a; i < array.length - 1; i++) {
      var head = array[a];
      var add = array[i + 1];
      pattern.push(head + " " + add);
    }
    patterns.push(pattern);
    pattern = [];
  }
  Logger.log(patterns);
}
意訳
この機能がやること
配列を用意する
結果を入れるpatterns配列を用意する
array配列-1だけ繰り返す
途中結果を入れるpattern配列を用意する
iにaを入れてarray配列-1より小さければiを足して以下を繰り返す
array配列のa番目と
array配列のi+1番目を
半角スペースでつないでpatternに追加する

patterns配列にpatternを追加して
pattern配列を空に戻して

出来上がったpatternsをログに出す




コードの例 その1


コード1.gs
function get2word_patterns() {
  var original = ["いちご", "めろん", "もも", "ぶどう"]
  var array = original.slice();
  var patterns = [];
  var pattern = [];
  var parent = array.shift();
  var childs = array.slice();
  
  var i = 0;
  while(i < original.length-1){
    if(childs != ""){
      var child = childs.shift();
      pattern.push([parent + " " + child]);
    }else{
      patterns.push(pattern);
      pattern = [];
      parent = array.shift();
      childs = array.slice();
      i++;
    }
  }  
  Logger.log(patterns);
}
意訳
この機能がやること
originalという名で配列を用意して
その配列をsliceでコピーする
patternsの入れ物
patternの入れ物
array配列の先頭を抜き出してparentに入れて
先頭を抜き出したarray配列の残りをchildsに入れて

iの初期値は0
iがoriginalの長さ-1より小さい限り以下を繰り返す
もしchilds配列が空じゃなければ
childs配列の先頭を抜き出して
patternにparent childを半角スペースでつなげて追加する
childs配列が空になったら
patternsにpatternを追加して
patternを空に戻して
array配列の先頭を抜き出してparentに入れて
先頭を抜き出したarray配列の残りをchildsに入れて
iをカウントアップしていく


出来上がったpatternsをログに出す



実行結果


HTML ServiceでUIを作ってみる


コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile('index');
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>
<head>
  <style>
    .ta {
      width: 360px;
      height: 240px;
    }
  </style>
</head>
<body>
  <textarea id="INPUT_VALUE" class="ta"></textarea>
  <br>
  <button id="BT">get pairs</button>
  <br>
  <textarea id="OUTPUT_VALUE" class="ta"></textarea>
  <script>
    var INPUT_VALUE = document.getElementById("INPUT_VALUE");
    var OUTPUT_VALUE = document.getElementById("OUTPUT_VALUE");
    var BT = document.getElementById("BT");
    
    BT.onclick = get2word_patterns;
    
function get2word_patterns() {
  var array = INPUT_VALUE.value.split("\n");
  var patterns = [];
  for (var a = 0; a < array.length - 1; a++) {
    var pattern = [];
    for (var i = a; i < array.length - 1; i++) {
      var head = array[a];
      var add = array[i + 1];
      pattern.push(head + " " + add);
    }
    patterns.push(pattern);
    pattern = [];
  }
  OUTPUT_VALUE.value = patterns.toString()
}
  </script>
</body>
</html>
意訳
 



taのスタイル
幅
高さ




入力エリア

実行ボタン

出力エリア

INPUT_VALUEの要素を取得
OUTPUT_VALUEの要素を取得
BTの要素を取得

BTをクリックしたらget2word_patternsを実行する

この機能がやること
配列を用意する
結果を入れるpatterns配列を用意する
array配列-1だけ繰り返す
途中結果を入れるpattern配列を用意する
iにaを入れてarray配列-1より小さければiを足して以下を繰り返す
array配列のa番目と
array配列のi+1番目を
半角スペースでつないでpatternに追加する

patterns配列にpatternを追加して
pattern配列を空に戻して

出来上がったpatternsをログに出す






補足


コードだけ見てるとよくわからなくなってきたので
コード1.gsの方を例に何をやっているのか書き出してみる


どっちが前でどっちが後ろかの順番は関係なくペアのパターンを作りたい

用意する配列の例
original
["いちご", "めろん", "もも", "ぶどう"]

その配列をshiftして行くので、その前にオリジナルを残しておく→.slice()でコピーを作れる(シャローコピー)
array = original.slice()
["いちご", "めろん", "もも", "ぶどう"]

要素のペアを作りたいので、順番に要素を抜き出してペアを作っていく
そのためにarrayをshiftしてまずは0番目を抜き出し、そこに残りの要素をくっつけていく
parent = array.shift()
"いちご"
残りのarrayは["めろん", "もも", "ぶどう"]

childs = array.slice()
["めろん", "もも", "ぶどう"]

originalの要素数は4つだけど最後の"ぶどう"は後ろにくっつける要素がないので-1
for(var i = 0; i < original.length-1;){

後ろにくっつける要素がある限り
if(childs != "")

childsの中からひとつずつ要素を抜き出して
child = childs.shift()
→"めろん"
→残りのarrayは["もも", "ぶどう"]

parentとchildをセットにしてpatternに追加していく
pattern.push([parent, child]);
→"いちご", "めろん"

後ろにくっつける要素がなくなったら
}else{

ここまでに作ったpattern配列をpatterns配列に追加する
patterns.push(pattern)
→[["いちご", "めろん"], ["いちご", "もも"], ["いちご", "ぶどう"]]

次のpatternを作るためにpattern配列を空に戻して
pattern = []

次のparentをarray["めろん", "もも", "ぶどう"]から抜き出して
parent = array.shift()
→"めろん"

残りのarrayは["もも", "ぶどう"]なので
childs = array.slice()
でコピー
→["もも", "ぶどう"]

これを繰り返してすべてのペアを作って

出来上がったパターンをログに出す


Latest post

Googleドキュメントに見出しを追加したい

今回の例では、ドキュメントの末尾に「見出しD」 を追加します。 見出しA, B, C, Dのスタイルは、見出し3 ( HEADING3 ) に設定しています。  下記Code.gsの  GOOGLE_DOCUMENT_URL を設定して  addHeadingToEnd()  を...