OmniAICreator/Qiita-1.07M huggingface dataset:
【ShellScript】Gitへのプッシュが面倒なのでシェルスクリプトを書いた話

めんどくさいのが嫌いなので、楽をする為に作りました。
シェルスクリプトの実行一発でpush完了です。

# 前提
・編集をローカルブランチでやって、masterにpushする。
・ステージングからコミットまではVScodeのGUIでやります。楽だし間違えにくいので。

## 実際のコード
```shell:gitPush.sh
git push origin local_branch

git checkout master
git pull
git cherry-pick local_branch
git push origin master

git checkout local_branch
git merge master
git push origin local_branch

```

## 注意
local_branchってのが自分の作業用に切ったブランチの名称です。
多分git merge masterでメッセージの入力を求められるので、qで抜けてください。
社員のお弁当の注文を取りまとめて、FAX 送信する LINE WORKS ボット #1

日替わりのオフィス宅配弁当の注文を、前日までに希望者を募りお弁当屋さんに FAX で注文書を送るという業務を LINE WOKRS の Bot に委ねたいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/5a1b4731-032f-df9a-70af-d91578eb6cae.png)

想定する処理のながれは次のとおりです。

- お弁当屋さんの日替わりメニューの Tweet を基に、社員宛にメニューのお知らせと注文する or しないの返答を求めるメッセージを送信
- 社員からの注文する or しないの応答を集計
- 集計結果を注文書にしてお弁当屋さんに FAX で送信
  
Micorsoft Flow でサクっと作ってみましょう。
## お弁当屋さんの Tweet を トリガーに LINE WORKS の Bot からメッセージ送信

お弁当屋さんが翌日のメニューを Tweet してくれている想定で、これをトリガーにしています。
他にも RSS や email をトリガーにする方法も可能だと思います。

付き合いのあるお弁当屋さんが、そんな情報発信してないよという場合はカレンダーとの連動などの方法を使いましょう。

ここではメニューのお知らせ専用アカウントからの Tweet をトリガーにする想定です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/c32465b0-2e3a-b874-2821-5c2fa6d5f16a.png)


from:@<twitter アカウント> の書式で条件を記述しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/a035cc5d-20cb-9758-6168-4c9e2b850565.png)

次に社員に個別に Bot からトーク メッセージを送ります。

以下の記事の手順も参考にしてください。
TITLE: 複数メンバーに個別に同報メッセージを送る LINE WORKS Bot
URL: https://qiita.com/iwaohig/items/3f25002608c79b26b996

"データ操作" の "作成" アクションを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/6fb8a8bd-76e9-2b06-bbe9-f718abe3af55.png)


"入力" でスクリーンショットのように、配信先の社員の LINE WORKS の ID を記述します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/fefcf2f1-8084-e631-1c86-af154bd9c7b0.png)

続けて、"条件" - "Apply to each" を指定し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/d548b457-5276-89f5-ebd5-910d4792c0dd.png)

"以前の手順から出力を選択" で "出力" を指定します。これにより、データ操作で入力した送信先ごとに LINE WORKS の API を実行できます。

Bot からメッセージ送信するための Azure Automation のジョブを作成します。Message パラメータ内に Tweet に含まれる弁当のメッセージをいれてます。

また、受信者が発注の有無を返信するためのボタンを含めています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/24205033-d7ec-10e7-5166-3ee91efdc8f4.png)

## ここまでの動作確認

お弁当屋さんの Tweet をトリガーに、社員に個別に Bot からトーク メッセージが送られることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/85e0e569-532e-94c8-8615-634da3027a03.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/5a1b4731-032f-df9a-70af-d91578eb6cae.png)

## 続編で社員からの注文を取りまとめて、お弁当屋さんに FAX で注文する処理を説明します。
RaspberryPiでGPG エラーの対応方法

# Raspberry PiでGPGエラーが出る場合

ラズパイでアップデートしようとしたら以下のエラーが出た。

```
$ sudo apt update
公開鍵を利用できないため、以下の署名は検証できませんでした: NO_PUBKEY XXXXXXXXXXXXXXXX
W: GPG エラー: https://dl.yarnpkg.com/debian stable InRelease: 公開鍵を利用できないため、以下の 署名は検証できませんでした: NO_PUBKEY XXXXXXXXXXXXXXXX
E: リポジトリ https://dl.yarnpkg.com/debian stable InRelease は署名されていません。
```

GPG公開鍵の有効期限が切れたのが原因。通常の手順で更新しようとしたらできなかった。
(keyserverが違うじゃないかと調べたがわからず・・・)

```
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys XXXXXXXXXXXXXXXX
Executing: /tmp/apt-key-gpghome.qkreNGDcHf/gpg.1.sh --keyserver keyserver.ubuntu.com --recv-keys XXXXXXXXXXXXXXXX
gpg: failed to start the dirmngr '/usr/bin/dirmngr': そのようなファイルやディレクトリはありませ ん
gpg: connecting dirmngr at '/tmp/apt-key-gpghome.qkreNGDcHf/S.dirmngr' failed: そのようなファイ ルやディレクトリはありません
gpg: keyserver receive failed: dirmngrがありません
```

dirmngrがないのが原因とのことで入れようとしたがうまくいかず。

```
$ sudo apt install dirmngr
E: パッケージ 'dirmngr' にはインストール候補がありません
```

## 解決方法
直接ダウンロードするもよう。

```
$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
OK
$ sudo apt update
```
エラーに出ているURLのパッケージの鍵を更新するので、その配布元から取得すればよい。
配布元はたぶん調べるしかないですが…
NETGEAR(ネットギア)  Stora MS2000-100JPS のディスクを2TB raid1から8TB raid1に交換する手順書

**※以下の手順はrootが必須です**
**※私が今回使用した8TBのディスクは
Seagate BarraCuda ST8000DM004 ですが
どのHDDでも同じ手順で動くと思います。**


既にrootを取得している前提で手順を記載致します。
#パーティションの作成
1. 電源を切ります。
2. ドライブをイジェクトします。
3. 新しい8TBのドライブに入れ替えて電源を入れます。
4. ターミナルからログインし、rootになります。
5. 以下のコマンドでパーティションを切ります。

```shell
cd /sbin

-- /dev/sda に対してのパーティション設定
./parted /dev/sda
mklabel gpt
yes
unit TB

-- 今回は8TBのディスクなので自分で8TBと入力している
mkpart primary 0.00TB 8.00TB
print
quit

-- /dev/sdb に対してのパーティション設定
./parted /dev/sdb
mklabel gpt
yes
unit TB

-- 今回は8TBのディスクなので自分で8TBと入力している
mkpart primary 0.00TB 8.00TB
print
quit

```

#raid1の作成
```shell
-- 以下のコマンドでraid1 を構成する。
./mdadm --create /dev/md0 --level=1 --chunk=128 --raid-devices=2 /dev/sda1 /dev/sdb1

-- ちなみにraid0 の場合には以下のコマンドで作成できると思う(未検証)
./mdadm --create /dev/md0 --level=0 --chunk=128 --raid-devices=2 /dev/sda1 /dev/sdb1

-- 以下のコマンドであとどれくらいで完了するのかを確認することができます。
watch cat /proc/mdstat

################ 実行結果例 ################
Every 2.0s: cat /proc/mdstat                                                                                                                                                        Sun Jun 20 15:10:45 2004

Personalities : [linear] [raid0] [raid1] [raid6] [raid5] [raid4]
md0 : active raid1 sdb1[1] sda1[0]
      7814026412 blocks super 1.0 [2/2] [UU]
      [=>...................]  resync =  ★5.5% (432168192/7814026412) finish=278.9min speed=184446K/sec★

ここで表示されている★5.5% (432168192/7814026412) finish=278.9min speed=184446K/sec★が重要な個所です。
上記例では現在5.5%が完了しており、ここり時間の目安は278.9分、毎秒184MB処理されているという表示になります。

※ただし、この時間は全くあてになりません。
ネット上の情報を見る限り1TBに付き、90分は必要なようです。
私も8TBで14時間くらいかかりました。

```

#ファイルシステムの作成と自動マウントの設定
```shell
-- ファイルシステムの作成
./mkfs.xfs /dev/md0

-- 念の為、普通にマウントできることを確認する
mount -t xfs /dev/md0 /home

-- 自動で/dev/md0がマウントされるように以下を設定しておく
-- /etc/fstab の以下の箇所をコメントアウトする
/dev/md0 /home xfs defaults,bsdgroups,noatime,nodiratime 0 0

-- raidの設定を保存
./mdadm -Q --detail --brief /dev/md0 > /etc/mdadm.conf

-- 再起動
./reboot

再起動し正常にマウントされていることを確認する
$ df -h
~中略
/dev/md0              7.3T  502M  7.3T   1% /home
/dev/md0              7.3T  502M  7.3T   1% /tmp
/dev/md0              7.3T  502M  7.3T   1% /var/cache/mt-daapd
-bash-3.2$

```

#元のディスクの内容を /home へ戻す

PCからネットワーク経由で戻しても良いのですが、
権限を再設定するのも手間なので、元のディスクはUSBで接続してしまい、
rsync -av で権限ごとコピーしてしまう方が作業量が少ないです。
ここではUSBでHDDを接続し、元に戻す手順について記載します。

```shell
-- ディレクトリの作成
cd 
mkdir /mnt/temp_disk

-- マウント
mount -t xfs /dev/sdc1 /mnt/temp_disk

-- 念の為、危なそうなファイル名のファイルは消しておく
# rm /mnt/temp_disk/.swapfile

-- 元の場所に戻す
cd /mnt/temp_disk/
rsync -av ./* /home/

-- ↑ ちなみに私の環境では転送速度は実測 16MB/s 程度でした。
-- 元の2TBのHDDのデータを全て移動し終わるまでには、約45時間必要でした。

-- 再起動
./reboot

```

これで元に戻す手順は完了です。
再起動の完了後はネットワークからドライブが見えることを確認して下さい。

![stora4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185260/141114f3-a698-a5f3-f069-a2a7e935a37d.jpeg)
**※storaの管理画面にログインし、何か操作すると
設定の上書きが行われる可能性があることにも注意して下さい。**

余談ですがstoraはUSBのHDDも認識できるので、
rootを取っているのであれば、外付けのUSB raid1 箱などを追加して、
シンボリックリンクでパスを通してしまう方法も可能です。
このページの手順の場合4日近くかかってしまう為、
短期的な延命で良いのであれば、USB HDDの追加する方が良いと思います。
FirefoxのCSS Transitionsを止めるにはプロパティの上書きが必要

# 内容

Off-Main-Thread Paintingが導入された頃から、CSS Transitionsが`transition`プロパティを削除するだけでは効果が止まらなくなりました。
開始したあとに止めるには、`transition: none;`のようなプロパティの上書きが必要になっているようです。

# コード

トランジション中に`transition`プロパティのある`useTransition`クラスを外していますが、2個目の`div.box`はそのままトランジションを続行します。

```HTML:HTML
<div class="box"></div>
<div class="box"></div>
```

```CSS:CSS
.box {
  width: 100px;
  height: 100px;
  background-color: #ff9500;
}

.box:not(.show) {
  transform: scale(0);
}

.box.useTransition {
  transition: transform 10s linear;
}

.box:nth-of-type(1):not(.useTransition) {
  transition: none;
}
```

```JavaScript:JavaScript
document.addEventListener('DOMContentLoaded', () => {
  const box = document.querySelectorAll('.box');
  Array.prototype.forEach.call(box, element => {
    element.classList.add('useTransition');
    element.offsetHeight; // reflow
    element.classList.add('show');
  });
  setTimeout(function() {
    Array.prototype.forEach.call(box, element => {
      element.classList.remove('useTransition');
    });
  }, 5000);
});
```
クッソー！！！！と思って勉強したら報われた。感謝の技術書5冊

どうも！ぞえです。

この記事は[成り上がりたくて必死に読んだ感謝の技術書6冊](https://qiita.com/kurkuru/items/97a90e3c267bd127af9b)を真似した記事です。

IT業界にHTMLコーダーとして飛び込んだ僕は、低い時給、マネジメントの方が多い職場に疑問を感じ、抜け出してやる。抜け出してやる。

と思ってひたすら勉強していたら、いつの間にかフリーランスになって給与も大幅にアップしました。

ということで、その間に読んだ感謝の技術書を紹介していこうと思います。

## JavaScript本格入門
JavaScriptの基本から応用までが乗っている本です。
30,000部のベストセラーとだけあってかなりいい技術書でした。

今でも理解できてない部分は多いと思いますが、当時は穴埋め形式にしてプリントアウトしたり、写経していました。

書いている人が山田 祥寛さんというのですが、この方の技術書はわかりやすいので好きです。
これからJavaScriptを学びたいという人は抑えておきたい神本です。

## 独習PHP
このご時世、フロントだけじゃなくてサーバーサイドもできなきゃダメなんだ！と思って手に取った技術書です。
この本をしっかり読めばPHPの基礎から応用までが理解できます。
正直初めてでこの本は難しかったので、何回か挫折して、たにぐちまことさんのよくわかるPHPの教科書を経てもう一回取り掛かりました。

山田さんが執筆していてわかりやすいので、PHPをしっかり学びたいという方にオススメです。

## エンジニアのためのWordPress開発入門
仕事でWordPressのテーマカスタマイズを行った時に購入した本です。
当時の職場にWordPressカスタマイズができる人がいなく、納期もあり、なにもわからない！！！
どこのファイルに記述をすればいいんだ！！！

とパニックになりながら二宮金次郎していた本です。
これさえ読めば、WordPressのカスタマイズがヘッチャラになるいい本です。

WordPress系のお仕事をしたい人には非常にオススメの一冊です。

## Angularアプリケーションプログラミング
職場にAngularアプリの開発をしているエンジニアがいて、なんかコンソールいじってる人ってカッコいい...
ということで取り組んだ一冊です。

AngularのチュートリアルにTour of heroesというのがあり、それを完了するのがプログラミング初心者には難しく、Tour of heroesの理解のサポートや、実際に職場を移ってから実務の参考にしました。

これも、山田さんが執筆していてAngularにしてはわかりやすいのですが、フロントエンドフレームワークを初めて学ぶなら、AngularじゃなくてVue.jsがいいと思います。

理由としてはAngularは難しく、時に泣きたくなるからです。

## フリーランスになってから眺めた参考書
### React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで (NEXT ONE)
AngularよりReactの方がお仕事多いっぽいぞ！という事で購入した一冊ですが、紛失した本です。
厚みが一番薄いという事で購入したのですが、比較的あっさりとReactに馴染めそうな良書だった気がします。

正直1章やったぐらいで紛失したので、オススメ度はわからないです。

### Docker/Kubernetes 実践コンテナ開発入門
職場にいた優秀なフルスタックの人たちが持っていたので、ついつい欲しくなって購入した一冊です。
Dockerってなんぞや？って事がわかるいい本です。

この本も山田さんが執筆しているのですが、この山田さんは山田 明憲さんです。

### PHPフレームワーク Laravel入門
PHPのサーバーサイドフレームワークならLaravel一択だよ！と聞いて入門した１冊です。

気軽にlaravelの雰囲気をつかめるので、PHPができてフレームワークをいじりたいといった方にオススメです。

### PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応
より深くLaravelを学びたい！！
サーバーサイドエンジニアになってみたい！と思って購入した本で3章ぐらいまでやってインテリアになりました。

僕がサーバーサイドエンジニアになれる日は来るのだろうか...

## 最後に
僕が技術書を買っていた頃はお金もあまりなく、正直3000円とか4000円を技術書に払うことに抵抗がありました。
ですが、技術書はインテリアにさえしなければ本当にいいもので、余裕で元が帰ってきます。
そして、今回紹介した本並びに執筆者の方々には感謝感謝でしかありません。

これを読んでくれた人も、自分にあった感謝できる技術書を見つけれるといいな！と思います。

ブログやtwitterもやっているので是非絡んでください。

ブログ　https://windii.jp/
twitter https://twitter.com/AKakizoe

雑談用slack作ったので気軽に絡んでください
https://join.slack.com/t/windiisocial/shared_invite/enQtNzQ2ODY1Mzc4NjE0LTE5ZGFjMzAyM2M4M2M1ODI1Nzg3NWY3Y2ZkNjM3MDdmNjNmYjZjNGIzNzAzOWY0OTM5MjRlZTBmNGQ3MWU2YTQ
[Firestore] CircleCIを利用したFirestoreの定期バックアップ

## CircleCIのスケジュール実行を利用したFirestoreの定期バックアップ

Firebaseの「[データのエクスポートのスケジュールを設定する](https://firebase.google.com/docs/firestore/solutions/schedule-export?hl=ja)」をぱっと見て面倒くさそうとなったので、CircleCIのcron使ってFirestoreのバックアップを取ることにしました。

下記の環境変数と`gcloud beta firestore --quiet export gs://firestore-backup`のgs://以降を変更すれば同じように動かせると思います。

```yaml
version: 2.1
jobs: 
  backup-database:
    docker:
      - image: google/cloud-sdk
    steps:
      - checkout
      
      - run:
          name: setup gcloud
          command: |
            echo $PRODUCTION_GCLOUD_SERVICE_KEY | gcloud auth activate-service-account --key-file=-
            gcloud --quiet config set project ${PRODUCTION_GOOGLE_PROJECT_ID}
            gcloud --quiet config set compute/zone ${GOOGLE_COMPUTE_ZONE} 

      - run:
          name: backup production database
          command: gcloud beta firestore --quiet export gs://firestore-backup

workflows:
  version: 2
  nightly:
    triggers:
      - schedule:
          cron: '0 15 * * *'
          filters:
            branches:
              only: master
    jobs:
      - backup-database
```
DnsmasqでCNAMEレコードを返却する

Dnsmasqを使って、任意のドメインに対してCNAMEで別のドメインを返却し、そのドメインの名前解決を外部DNSへ再帰問い合わせする、という機能が実現できるか調査した。

結論としては、DnsmasqでCNAMEレコードを返却できるのはDnsmasqが（hostsファイルなどで）名前解決できるドメインのみであり、上記の機能は実現できないことがわかった。

#### 検証方法・結果

##### docker-componse.yml

```yaml
version: '3'
services:
  dnsmasq:
    image: andyshinn/dnsmasq
    command: >
      --auth-server=abc.com,eth0
      --auth-zone=abc.com
      --server=/google.com/8.8.8.8
      --cname=one.abc.com,google.com
      --dns-rr=two.abc.com,5,06676f6f676c6503636f6d00
    hostname: dnsmasq
    ports:
      - 53:53/udp
      - 53:53/tcp
    restart: always
    cap_add:
      - NET_ADMIN
  shell:
    tty: true
    build:
      context: .
```

##### Dockerfile

```dockerfile
FROM ubuntu:18.04

RUN apt-get update \
    && apt-get install -y locales locale-gen ja_JP.UTF-8 \
    && echo "export LANG=ja_JP.UTF-8" >> ~/.bashrc
    && apt-get install -y iputils-ping net-tools dnsutils

CMD ["bash"]
```

##### DNS問い合わせ結果

```shell
# DnsmasqをAuthoritative modeで動かしている場合は、
# cnameオプションで返却するドメインもDnsmasqが権威を持つとみなされ、外部DNSへの転送が行われない。
# manページの引用：
# --cname as long as the record name is in the authoritative domain.
# If the target of the CNAME is unqualified,
# then it is qualified with the authoritative zone name. 
dig @dnsmasq one.abc.com

;; flags: qr aa rd ad; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;one.abc.com.                   IN      A

;; ANSWER SECTION:
one.abc.com.            600     IN      CNAME   google.com.
```

```shell
# 任意のレコードを返却できるdns-rrオプションも存在するが、
# このオプションで設定したレコードはAレコードの解決では返却されない。
dig @dnsmasq two.abc.com

;; flags: qr aa rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;two.abc.com.                   IN      A
```

#### 参考

- [Dnsmasq manページ](http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html)
- [dns-rr設定方法についての参考](https://serverfault.com/questions/914108/how-to-add-a-caa-record-to-dnsmasq)
Rails references型　簡易メモ

references型で設定すると、
・カラム名に_idをつけてくれる。
・インデックスの設定も自動的に行ってくれる。

```ruby
t.references :group, foreign_key: true
```
とmigration file に記述すると、
group_idのカラムができる（蛇足だが、例では、foreign_keyで外部キー制約をつけている）。
WSL2でのJupyter notebookのリモートアクセス

ついにWSL2の利用が可能になる18917ビルドが来ましたね！
インストールと起動については、

```
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
wsl --set-version ubuntu 2
```

とするだけで特に苦労する部分はありません。
WSL2の設定を早速進めて、リモートのjupyter-notebookを動かそうとしたところ、
なぜか"This site can't be reached"の表記が。。

Anaconda, Jupyter, extensionなどのreinstall/updateを試してみましたが直らず、
telnetでのlocalhostへのアクセスもうまくいかない。

公式の<a href="https://github.com/Microsoft/WSL/issues/2471
">Github issue</a>を見に行ったところ、以下の記事が紹介されており、
なんと今回のビルドではlocalhost経由のアクセスをサポートしていないとのこと。

"In initial builds of the WSL 2 preview you will need to access network applications using an IP address and not using localhost"
WSL2プレビューの初めのビルドでは、localhostではなくIPアドレスを用いてネットワークアプリケーションにアクセスする必要があります。

https://docs.microsoft.com/en-us/windows/wsl/wsl2-ux-changes#accessing-network-applicationsより

どうも毎回ipの割り当てがランダムに起こるようなのでそれを調べて、指定すればアクセスができるようですが...まだプレビュー状態なので仕方がないでしょうか(；´Д｀)ｻﾞﾝﾈﾝ...
TypeScript未対応のモジュールをimportするときのエラー対策

TypeScript未対応のモジュールをimportしようとするとこのようなエラーになります。

```
Could not find a declaration file for module 'blueimp-load-image'. 'node_modules/blueimp-load-image/js/index.js' implicitly has an 'any' type.
Try `npm install @types/blueimp-load-image` if it exists or add a new declaration (.d.ts) file containing `declare module 'blueimp-load-image';`  TS7016
g
```

# 回避策

### 1.require を使う
```
const loadImage = require("blueimp-load-image")
```

### 2.無視する
```
//@ts-ignore
import * as loadImage from "blueimp-load-image";
```

### 3.全てをanyとして扱う
.d.tsファイルにモジュールを定義する

```
declare module "blueimp-load-image";
```

### 4.定義を書く
```
// .d.ts （記事のため全部書くのが面倒なので全部 any を使用）
declare module "blueimp-load-image" {
  export function parseMetaData(file: any, 
                                callback: (data: any) => void, 
                                options?: {[key: any]: any}, 
                                data?: {[key: any]: any});
  function loadImage(file: any, callback: any, options?: any): any;
  export default loadImage;
}



// .ts
import {default as loadImage, parseMetaData} from "blueimp-load-image";
```

# どれにするか？
* 1 は import と requre が混在してしまうので使いたくない
* 2 は使う場所が多いと面倒なので使いたくない
* 3 は全部 any になるためタイプチェックが効かないため極力使いたくない
* 4 は面倒だけど安全なので極力これにしたい

他にあれば教えてください。
iOSのハーフモーダルをAndroidでも実装してみた(Kotlin+BottomSheetBehavior)

BottomSheetBehavior を Kotlin で実装した記事はあまりないような感じがしたので記事を書いてみました。
初記事のため、至らない点が多々あると思いますが、誰かの参考になればと思っています。

##対象読者
- 業務で Android にも iOS のハーフモーダルを実装することになった方(自分) 

##開発環境
- macOS Mojave 10.14.4
- Android Studio 3.4.1

## そもそもBottomSheetsってどんなやつ？🤔
画像はGoogle Mapで、周辺のスポットと書かれているところがBottomSheetです。
![ハーフモーダル最大.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236467/751f8f81-15b3-0374-3e3a-70765f05cf11.jpeg)
![ハーフモーダル最小.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236467/f7bf9163-746a-4cc2-b08a-599c5bd6e495.jpeg)
下から引っ張り出して表示するやつです。

## 実装方法

###1.以下の依存関係を app module の gradle ファイルに追加

```
dependencies {
    // MaterialDesign
    implementation 'com.google.android.material:material:1.0.0'
}
```

###2.レイアウトリソースファイルを作成。

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <!--他のView--> 

    <!-- 下から引っ張り出すView (BottomSheet) -->
    <LinearLayout
        android:id="@+id/bottomSheetLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent"
        android:orientation="vertical"
        app:behavior_hideable="false"
        app:behavior_peekHeight="56dp"
        app:layout_behavior="@string/bottom_sheet_behavior">

        <!-- BottomSheetに表示したいViewを設定する -->

    </LinearLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
```

ポイントは LinearLayout に設定している __app:layout_behavior__ です。
**app:layout_behavior** に **@string/bottom_sheet_behavior** を設定してやると、設定した View を BottomSheet にすることができます。
注意点ですが、BottomSheet にしたい View （この例でいうと、 LinearLayout ）は **CoordinatorLayout の直下に配置**しないとエラーとなるので気をつけてください。
他に設定している app:behavior_xxx の設定ですが、
[こちらの記事](https://qiita.com/napplecomputer/items/5b3d1225533a59488ac3)に詳しく書かれているので、参照していただければと思います。🙇‍♂️
一応、今回使用した設定に関して簡単に解説をすると、
・BottomSheetの表示位置を設定 (app:behavior_peekHeight="56dp")
・BottomSheetを画面の外へ隠さないようにする設定 (app:behavior_hideable="false")
となります。

###3.BottomSheetにするViewをコードで設定
```Kotlin
class MainActivity : AppCompatActivity() {

    private lateinit var bottomSheetBehavior: BottomSheetBehavior<LinearLayout>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // CoordinatorLayout の 直下に配置している View を渡す
        bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout)
    }
```
ここまで作成すると以下のようにできると思います。
![BottomSheetSample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236467/5d6698e0-6cf4-342b-5087-9c892e72e5cb.gif)
👏👏👏👏👏👏👏👏
##最後に
タイトルに Kotlin と入れておきながら、全然コード書いてないですね。。
次回は BottomSheet を引っ張り出したりする操作による state の変化イベントを取得していきたいと思います。
今回作成したコードは[こちら](https://github.com/fumiyatani/BottomSheetSample)に置いてあります。
上記のリポジトリを更新しながら記事も更新していきます。
ここまで見ていただきありがとうございます！
AndroidのGoogle ChromeでUIとウェブコンテンツを強制的にダークテーマにする

**注意　この記事で紹介する方法は開発途中の機能を有効にするものを含みます。また、画像はAndroidタブレットのもので、画面やボタンの配置は異なる可能性があります。**

この記事はGoogle Chrome 88向けです。以前のバージョンを使用している場合は、記事タイトル右上の点が3つ並んでいるメニューの編集履歴から、古いバージョン用のやり方を見ることができます。

また、この記事と同じ内容を[私のブログ](https://robot-inventor.github.io/article/2020/05/14/)にも投稿してあります。

# UIをダークテーマにする

簡単です。

```
Chromeのメニュー（画面右上の丸が縦に3つ並んだマーク）> 設定 >
基本設定 > テーマ > 暗
```

を選択すればUIをダークテーマにできます。

# ウェブコンテンツを強制的にダークテーマにする

ダークテーマに対応しているサイト（コンテンツの見た目を設定するCSSのメディアクエリでprefers-color-schemeを使用しているサイト）なら、上記のUIの変更だけでコンテンツもダークテーマになります。

ところが、現時点で対応しているサイトはほとんどありません。そこで、コンテンツを強制的にダークテーマにするために、Chromeの試験機能を使います。

まず、Chromeを開き、検索窓に

```
chrome://flags
```

と入力します。

すると、開発途中の機能の有効・無効を切り替えるメニューが表示されます。

![IMG_20190621_021225_672.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420120/51ca35e9-50ff-ca46-99e1-67b6643d835c.jpeg)

ページ内検索か、画面上部のSearch flagsに

```
dark
```

と入力し、[Darken websites checkbox in themes setting]という項目を[Enabled]にします。

すると、画面下部に

> Your changes will take effect the next time you relaunch Google Chrome.
> 訳：変更は、次回Google Chromeを再起動したときに有効になります。

と表示されるので[Relaunch]を押して再起動します。

再起動したら、設定の基本設定のテーマで、[システムのデフォルト]か[暗]を選びます。[ウェブサイトを暗くする]というチェックボックスが表示されるので、チェックを入れます。

すると、あらゆるWebページがダークモードで表示されるようになります。上手くダークにならない場合は1度Chromeを閉じてもう1度起動してみてください。

# 結果

ChromeのUIとウェブサイトの表示の両方をダークモードにすると、下の画像のようになります（画像はQiitaのトップページ）。以前試験機能の別の項目しか方法がなかったときはコンテンツをダークテーマにすると画像の色が反転していましたが、今は問題ありません。

![Screenshot_2020-03-29-15-59-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420120/18496106-4ab4-4f7c-48ca-b877ef8eecdc.png)

ただし、サイトによっては配色が不自然になることがあります。また、背景が透過に設定されている画像が見にくくなったり、文字が読みにくくなったりする可能性もあります。気になる場合、ダークモードにするのはUIのみにしましょう。

下の画像は、以前試験機能の別の項目しか方法がなかったときにコンテンツをダークテーマにした際のスクリーンショットです。配色や画像の色が不自然でしたが、現在は改善されているのが分かります。

![Screenshot_2019-06-21-02-43-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420120/bb524e6e-ca41-e4cf-7cc4-d7ea6e61d45e.png)

# 設定の解除

UIのダークモードを解除したい場合は、ダークモードにしたときの設定画面で[明]を選んでください。

コンテンツをダークモードにする試験機能を解除するには、**設定の基本設定のテーマで、[ウェブサイトを暗くする]チェックボックスのチェックを外してから**

```
chrome://flags
```

で[Darken websites checkbox in themes setting]を[Default]に戻します。

すると、画面下部に

> Your changes will take effect the next time you relaunch Google Chrome.
> 訳：変更は、次回Google Chromeを再起動したときに有効になります。

と表示されるので[Relaunch]を押して再起動します。
（学習中、随時更新）未経験からweb系プログラマーになるための独学履歴~JavaScript編~

○　今回の学習教材
https://youtu.be/GCa9xqQ2rxQ

○　学習教材の意義
・TechAcademy様の「はじめてのJavaScript」を終了したので、実際にJavaScriptを使ってみる。
・JavaScriptを学ぶ上で、理解しておきたいクラス構文及びオブジェクトに触れる

○　アウトプット

・JavaScriptにおけるクラスとインスタンス

JavaScriptにおけるクラスはオブジェクトを作成していくための設計図であり、以下のように書く。

```js
class Data {

}
```

こうして設計図を定義すると、下記のように書くことでオブジェクト（インスタンス）を作ることができる。

```js
let data = new Data();
```

これはつまり、普通にオブジェクトを作る際に書く

```js
let data = {

}
```

と同じ意味でありここにプロパティ、クラス構文においてはインスタンスを生成する際に実行したい処理や設定を追加するためにはまず以下のように書く

```js
class Data {
    constructor(x,y) {
        this.x = x;
        this.y = y;
    }
}
```

constructorはコンストラクタと呼ばれるメソッドであり、定義したクラスからオブジェクトを生成し、初期化する際に実行されるメソッド。
引数はそれぞれ自身のプロパティに、指定された引数の値を設定するという意味であり、こう書くことでオブジェクトが自らのプロパティとその値を設定できるようになり、あとに以下のように通常のメソッドを書くことができる。

```js
const fooclass = class {
  constructor(x, y) { /* コンストラクタ */
    this.x = x
    this.y = y
  }
calc() {  /* メソッド */
    return this.x + this.y  /* x と y を足した値を返却する */
  }
}
```

・クラスからオブジェクトを作ってみる。

作ったクラスからオブジェクトを作ってみる。

```js
const fooclass = class {
    construcotr(x,y) {
        this.x = x;
        this.y = y;
    }
calc(){
    return this.x + this.y;
}
}

let foolclass = {

}
```

という形式を作ればよく、それに相当するのは

```js
let fool = new foolclass();

var fool = new foolclass(10,20);
```

つまり、変数宣言をしてnew＋クラス名()をすればいい、クラス名()の括弧の中にはコンストラクタにわたす引数を設定する。
つまり、x=10,y=20　となる。

プロパティの取得と設定

クラスのプロパティは以下のように呼び出す

```js
var hoolclass = class {
    construcotr(z){
        this.z = z;
    }
    get getZ(){
        return this.z;
    }
    set setZ(z){
        this.z = z;
    }
}

var hoo = new hooclass(20);
var val = hoo.getZ;
console.log(val);
```

ここで設定されているメソッドはコンストラクタとgetZメソッドである。
getZの前のgetはget構文であり、後にオブジェクトを作成したあと、get構文で指定したメソッドの返却値を取得できる。
getZメソッドではhoolclassのプロパティzに指定された引数の値を取得することになる。
またsetはset構文であり、指定したコードの後に値を設定するメソッドを指定すると、インスタンスにそのプロパティを設定するすることができる。
つまり前述のコードを以下のように変更すると

```js
var hoo = new hooclass(20);
hoo.setZ = 100;
var val = hoo.getZ;
console.log(val);
```

まず、オブジェクト宣言によりzに引数として20が渡される。
次にsetZオブジェクトを用いる。
setZはつまりthis.z = zということになリ、このzに100を渡すということになるので、値が20から100に更新される。
よってコンソールに出力されるのは100ということになる。


○　参考サイト及び出典

TechAcademy「はじめてのJavaScript」
https://www.youtube.com/watch?v=8tbOF18zoP0&list=PLjw-30bsJNVUpFTm9FDof8B91uI3iorDH

Tech Play「JavaScriptのクラスを理解する」
https://techplay.jp/column/482

（敬称略）
そのバージョン管理システム、本当に必要ですか？ バージョン管理を必要としない人たち

#前書き
私の好きな本、ベタープログラマの第20章 効果的なバージョンコントロールの冒頭では、
> 開発者にとってバージョンコントロールは、食事したり呼吸したりするようなものです。コードを書くエディタやコンパイラと同じです。

とあり、続けていかにバージョンコントロールが重要かが丸々一章を使って解説されています。

また、バージョン管理ツールの一つであるgit にはとても分かりやすい日本語の公式ドキュメントがあったり、qiitaでも初心者向けの解説を書いている方が沢山います。

しかし、使いこなすにはある程度の学習コストがかかるのも事実です。本当に全ての人がバージョン管理システム(VCS)を使わなければならないのでしょうか？
庭師でも王様でもないのにブランチがどうのこうのやらないといけないのでしょうか？

今やバージョン管理は開発において常識とされていますが、あえてメスを入れてみたいと思います。

#バージョン管理をしなくてもいい人
##全ての変更履歴を覚えている人
これまでの全ての変更履歴を覚えており、さらに瞬時にしてある時点のソースコードを再現する能力がある方にとっては、バージョン管理システムを使う利点は少ないでしょう。私は自分の書いたソースコードの中身はすぐ忘れてしまうので、git だけでなくredmineのwikiやチケットにも記録を残して、経緯や意図を後から追えるようにしています。が、頭のいい人にとってはやり過ぎであり、ひたすらコードを書きまくった方が開発のベロシティが高くなるのでしょう。

##ミスを全くしない人
ミスを全くしない人は、そもそもコードベースの履歴を振り返る必要が殆ど無いため、やはりバージョン管理システムを使うメリットは小さいでしょう。バグが発覚した時、どの時点で仕込まれたかを調査するためには、バージョン管理システムによるロールバック、リリースごとのコミットに対する適切なタグ付けが非常に有効ですが、ミスを全くしない人にとっては他人事です。少なくとも自分が仕込んでいないことは確かなのですから。

##一生一人で開発をする人
引き継ぎをしないで済むことは非常に幸運です。特に引き継がれる人にとって。
例えまともな管理が行われていなかったとしても、ソースコードがどこにあるのか広大なストレージの中を探したり、分厚い引き継ぎ書を読んだり、過去のメールから断片的な情報を探したりする必要はないのです。引き継ぐ必要が無いのであれば。誰が文句を言うでしょうか？

##コードを書かない人
全く自分で手を動かすことが無く、他人の仕事に文句を言うだけの人がいるとすれば、バージョン管理システムはその人にとっては高級すぎるでしょう。あるいは非エンジニア、もしくは赤ちゃん👶

#終わりに
いかがでしたか？あなたはバージョン管理をしなくてもいい人でしたか？
当てはまらなかった方で、まだバージョン管理をしていない方は、git でもsubversionでも良いので、使いながら学んでいきましょう。<font color="Lavender">(うんcommitしても、げrevertしたらええんやで。)</font>

#bibliography
https://git-scm.com/book/ja

初心者がGitを少し触れるようになるための記事-commitからpushまで-
https://qiita.com/ihcamonoihS/items/e288e080379483dfbcf5

Hurry Coderと死の悲報 PART1
https://qiita.com/Yametaro/items/63dadd600526e9a8ac65#_reference-9b17d2f938135f4665c1
html&cssの知識が増えました。

①Webページの構成
header,footerの間にコンテンツが入る。
これらはタグとして用いることもできる。
CSSで背景画像を指定するためには、background-image:urlを用いる。
この画像を指定された範囲全体に表示するためには、background-size:coverを用いる。
指定したクラスを中央に寄せるためには、margin:autoを使用する。
このときの注意点は、widthも同時に指定することである。
要素を透過させたい場合は、opacityを0.0～1.0で指定する。
文字同士の間隔を指定したいときは、letter-spacingで指定する。
②ボタンを作るとき
要素をdisplayプロパティを用いてインラインブロック要素にする。
ボタンにカーソルを乗せたときの状態をhoverという。
背景を変えるときは、div:hover{background-color:}とかく。
ボタンの角を丸めるにはborder-radiusプロパティを用いる。
text-alignプロパティによってラインブロックの配置を指定する。
③ヘッダー
ヘッダーを透明にしたいときは、rgba{}を使う。
ログインリンクを作るとき、transitionを使ってアニメーションをつける。
EC2上にSelenium環境構築&Node.jsでテスト実装

EC2上にSelenium環境を構築し、Node.jsでテストを実装した際の備忘録。

# 手順
##ec2インスタンス作成
AMI:amzn2-ami-hvm-2.0.20190508-x86_64-gp2 (ami-0c6b1d09930fac512)

##chromeインストール
```
sudo yum -y install http://orion.lcg.ufrj.br/RPMS/myrpms/google/google-chrome-stable-71.0.3578.98-1.x86_64.rpm
```

##ChromeDriverインストール
```
wget https://chromedriver.storage.googleapis.com/2.45/chromedriver_linux64.zip
unzip chromedriver_linux64.zip -d bin/
```

注意:chromeとChromeDriverは対応したバージョンでないと動作しないことがあります。
こちらを参考にしてください。
http://chromedriver.chromium.org/

##Node.jsインストール
```
sudo curl -sL https://rpm.nodesource.com/setup_8.x | sudo bash -
sudo yum install nodejs
```

##npmモジュールインストール
```
sudo npm -g install selenium-webdriver mocha expect.js selenium-webdriver
```

##test.js作成
qiitaトップページの「Hello hackers !」文字列を取得して検査。

```js

const webdriver = require('selenium-webdriver');
const { Builder, By, until } = webdriver;
const assert = require("assert");

const capabilities = webdriver.Capabilities.chrome();
capabilities.set('chromeOptions', {
    args: [
        '--headless',
        '--no-sandbox',
        '--disable-gpu',
        `--window-size=1980,1200`
    ]
});

describe("top page test", () => {
    it("top page h1 value check", async () => {
        const driver = await new Builder().withCapabilities(capabilities).build();
        await driver.get('https://qiita.com/');
        let element = await driver.findElement({ css: '.nl-Hero_title' }).getText();
        assert.equal("Hello hackers !", element);
        await driver.quit();
    });
})

```

##実行
```
mocha test.js
```
タイムアウトで失敗した場合は、```-t 5000```のようにタイムアウト時間を指定してください。デフォルトは2000ミリ秒なので、それよりも大きい値を指定してください。


#参考
https://chida09.com/selenium-nodejs-dev/
https://oliversi.com/2019/01/06/python-aws-selenium-chrome/
初心者用 AtCoderでよく使うPython表記



## 目的

pythonと競技プログラミングを学びたいためatcoderを始めました
他の言語ではあまり見ない構文等が沢山あるので、備忘録的な形で少しずつまとめます
速度改善やテクニック的なのも分かり次第追記予定


### 値取得

- 入力値を取得する方法

**取得**

```py
# 入力
# 2
# 1 2 3
N = int(input()) # 2
A = list(map(int, input().split())) # [1, 2, 3] 数値変換してリストで代入
```

**一括**

```py
# 入力
# 4
# 5
# 6
X, Y, Z = map(int, [input() for __ in range(3)]) # X=4 Y=5 Z=6
```

- sys.stdin.readline
 - こっちのほうが早いらしい
 - 下記でinputに置き換え可能？

```py
import sys
input = sys.stdin.readline
```


### 配列

- 初期化

```py
a = [0] * 3 # [0,0,0]
b = [0, 1] * 2 # [0, 1, 0 ,1]
```

- 最後から取得

```py
a = [1, 2, 3]
a[-1] # 3
a[-2] # 2
```

- スライス
 - リスト、タプル、文字列の一部を切り取れる   
 
```py
s = [1, 2, 3, 4, 5]
s[2] # 3
s[1:] # [2, 3, 4, 5]
s[:2] # [1, 2]
s[1:2] # [2]
s[:] # [1, 2, 3, 4, 5]
```

- ステップ
     
```py
a = [1,2,3,4,5]
a[::2]  #  [1,3,5]
a[1::2] #  [2,4]
```

- 特定値の抽出

```py
l = [1, 2, 3, 4, 5]
data = [i for i in l if i % 2 == 0] # [2, 4]
```

### 組み込み関数

- all(), any()
    - list, tuple等のイテラブル要素が全て、又は１つ以上Trueか判定 
 
```py
data = [2, 4, 6 ,8]
while(all(i%2 ==0 for i in data):
    # リスト内が全て偶数であれば処理
```

- enumerate
 - イテラルオブジェクトの要素とインデックスを取得出来る
 
```py
data = [10, 20, 30, 40]
for i, n in enumerate(data):
    print(i, n)
# 1 10
# 2 20
# 3 30
# 4 40
```

- sorted
 - ソート　降順、昇順

```py
data = [3,1,2]
sorted(data)  #[1, 2, 3]
sorted(data, reverse=True) #[3, 2, 1]
```

- replace
 - 文字列を置換

```py
s = 'abcdefg'
s.replace('cde', '') #abfg


```

- abs
 - 絶対値

```
a = 1
b = -1
abs(a) # 1
abs(b) # 1
```

### 数値

- fractions
 - math は使えないので注意
```
import fractions
```

- 最大公約数、最小公倍数

```py
x, y = 10, 4
gcd = franctions.gcd(x, y) # 2
lcm =  x * y // gcd # 20
```


### numpy

pythonのみで計算より高速なので状況によっては好まれる
勉強中

### 考え方やテクニック メモ

- スター (グラフ)

中央の頂点と、それから放射状に辺が繋がる合計N個の頂点を持つグラフ
ここから完全グラフにするには、(n-1)(n-2)/2 の辺が必要
下の問題では、スターグラフから辺を増やして求められる値に近づけるテクニックが必要
https://atcoder.jp/contests/abc131/tasks/abc131_e 

- ２部グラフ
 
https://atcoder.jp/contests/abc131/tasks/abc131_f
xとyの２部グラフで考え、連結性毎にx * y - 辺の数で求められる
Asciidoctor で <br> 的なものを定義する

# Asciidoctor で 属性と Passthrough を使いこなす

passマクロを色々試していたら、自己解決。なんで用意されていないのか分かった。標準機能で普通に実現できるから。

```asciidoc
:br: pass:p[ +]

a{br}b
```

結果のイメージ(AsciiDocが使えないのでMarkdown記法で結果を再現)

```
a
b
```

## 解説

* pass:[～] は入力されたものを加工せずに出力する。
* p は Post Replacements の略。Post Replacementsは[～]のテキスト中に ` ` `+` を見つけると、改行に置き換える。(この記法、実はpdfでもうまくいく。)
* pass:q,p,...[～]は、「～」で書式適用、改行置換などを適用したテキストを、そのまま出力する。

## passマクロの応用例

### PDF向けには画像を出して、HTML出力向けにはフォントを使う。
image は macros でマクロを置換してから、passすれば良い。

```asciidoc
ifeval::["{backend}" == "html5"]
:phi: pass:r[&#120601;]
endif::[]

ifeval::["{backend}" == "pdf"]
:phi: pass:macros[image:U03D5_phi_straight.svg[]]
endif::[]

{phi}
```

そもそも、PDFはいらない子という話はあるかもしれないが、アイコン等を埋め込んだ文書を作るには良いのかもしれない。

### HTMLタグを埋め込む
ルビを入れることもできる。PDFは無理。
この例は、pass 指定しないと、 *AsciiDoc* 文書と思われて、＆や＜＞などの記号が通常の文字に置換されてしまう。pass指定で、生のHTMLをそのまま出力できる。

```asciidoc
ifeval::["{backend}" == "html5"]
pass:[<ruby><rb>hello world</rb><rp>（</rp><rt>こんにちは世界</rt><rp>）</rp></ruby>]
endif::[]
```

こういうのは、属性とあわせて使うと力を発揮するものと思われる。
ただ、外道(道を外れた使い方)ではある。こういうのに頼らずに済めばよいのだが、まずは出来ることが重要。

### 太字のマークアップ付きの属性を定義する
q は quote の略。太字/斜体(slant?)/オーバーライン/アンダーラインなど、マークアップを変換した後、passthrough する。html的に言うと、 `Z` --quote--> `<strong>Z</strong>` --pass--> `<strong>Z</strong>` という感じで適用される。

```
:ZZ: pass:q[**Z**]

{ZZ}
```

### 一旦メモしたものを少し加工して見栄えを良くする。

ノートを取った直後

```asciidoc
----
  i     j
1 2 3 4 5 6
1 2 3 5 4 6
1 2 5 3 4 6
1 5 2 3 4 6

1 5 2 3 4 6
1 5 3 2 4 6
1 5 3 4 2 6
  ^     ^
----
```

強調したい場所にマークアップを入れる

```asciidoc
[subs="quotes"]
----
  i     j
1 2 3 4 *5* 6
1 2 3 *5* 4 6
1 2 *5* 3 4 6
1 *5* 2 3 4 6

1 5 *2* 3 4 6
1 5 3 *2* 4 6
1 5 3 4 *2* 6
  ^     ^
----
```

表示結果
![capture_20190621_012515.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326797/ac21cf27-e980-9e9a-d520-4345af68efb1.png)


## おわりに
AsciiDocって attribute を使いこなすと、太字・斜体を含めた入力を簡略化できる。ノートを取るのにも実用的な感じがしている。

## 参考

- [How to insert sequential blank lines · asciidoctor/asciidoctor Wiki ...](https://github.com/asciidoctor/asciidoctor/wiki/How-to-insert-sequential-blank-lines)

### マクロの表
マニュアル見れば載ってるが、一応。
![capture_20190621_013916.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326797/252a3c69-8df7-c075-5aab-fac62a5d870a.png)
シーンをロードしたとき、GameObjectのInspectorを表示し続けたかった。

#はじめに


デバッグのなどで、図の`Sphere`の Inspector を表示し続けたいと思ったとします。
![SceneLoad_20190621_1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285370/5a71b9f8-ba8e-60a4-5274-6072845a5dad.jpeg)

そこに、以下のコードでシーンを読み込んだとします。
SceneManager.LoadScene("MainScene");

すると、下図の状態になります。
`Sphere`のInspectorを表示するためには、MainSceneを開きGameObjectを選択する必要があります。
（階層が深くなれば深くなるほどしんどい。。。）

![SceneLoad_20190621_2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285370/987b3fd4-5475-73ef-0062-67b1369c330f.jpeg)

しんどかったので、簡易的なやり方ですが、Hierarchy の検索欄に`Sphere`を指定しておいて、、、
![SceneLoad_20190621_3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285370/7731a849-bfc6-dabe-bcc0-4a9f3db90657.jpeg)

シーンをロードすると、検索欄の文字列は残ったままなので、`Sphere`を選択することが楽になります。

![SceneLoad_20190621_4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285370/8f9e3db9-d1dd-9950-dea0-43b1c9c656e5.jpeg)

～2020年2月4日更新～
[こちら](https://kan-kikuchi.hatenablog.com/entry/Selection)の記事に任意のオブジェクトを選択する方法が紹介されていました。

```
Selection.activeGameObject = デバッグで確認したいオブジェクト;
```
Awake、OnEnable、OnDisable 内に記述することで、シーンロード後に自動でオブジェクトで選択された状態になります。
Elm はじめました

---
本稿は、***Elm*** を使って何か作るための筆者の ***アイデアノート*** 的な記事です。
作成物が出来たら、別記事として切り分け、更新します。

## 目的（動機付け？）

- Elm と仲良くなりたい
- フロントをどうにかかけるようにしたい
- Web 開発でも ***Haskell*** (っぽく)書きたい


## 作りたいもの

1. [駅検索フォーム（DB 情報検索 Webフォーム）](https://github.com/aq-nozomi/elmWebForm)
  - 大枠のデザイン = 最終的には、駅探クラスにしたい（ダイヤ改正などでの対応について検討必須）
  - DB作成 → フォームから登録して、データをうめていく。
  - クエリを検索用に切り替え修正

2. ***Wordpress***のような、ブログ記事投稿サイトテンプレ
  - 大枠のデザイン = ギルドの張り紙的な感じ、LP、辞書的なwiki的な

3. ニュースピックBot（分野：ICT,IoT,Web,DB,Raspberry Pi）
  - 大枠のデザイン = twitterAPI と連携して、Slackに通知？

4. ドローン遠隔操作
  - 悪用はしません。あくまでも、変態飛行とか。編隊

5. 機械学習お試しWeb ( Haskell & Elm で実装？)


まだまだ続くよ！

---
##### 参考記事、本とか

[公式](https://elm-lang.org)
[公式ガイド](https://guide.elm-lang.jp)
[基礎からわかるElm](https://www.amazon.co.jp/dp/B07P253FPG/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)
リモートで開発しようと思ったけど常駐を選んだ話

# なに言ってるの？いまどきリモートでおkだよね？

そう思ってました。
とはいえ、いきなりリモートできるとは思ってなくて、以下のような条件を満たしていればOKと思ってました。

- 仕様がある程度固まっている
    - メッセのやりとりでは難しい部分とかは固めておく
- 最低でも3週間くらいは近くで仕事をしたあと
    - いきなりリモートでやるより近くの席で話したりしながらお互いを知る
    - リモートでやっていけそうか判断できる 
- メンバー間で気軽に質問できるような雰囲気
    - 気軽に質問できる空気がないとリモートは難しい
    - メッセやビデオ会議の際のコミュ力必要 

## 背景
今回は、Aチームのプロジェクトに自分のチームから3人が参加することになった。
Aチームのオフィスと自分のチームのオフィスは別の場所(移動すると40分くらい):tram:
しかもみんな通勤遠くなる・・・:frowning2:
Aチームのオフィスは遠いから要件定義終わるまでは、常駐してその後はリモートでやろうと思った。

## 結果どうだったか
要件定義終わった後も、常駐するスタンスを取る決断をした。

## 理由
以下の理由から対面コミュニケーションをすぐに取れるようにしておく必要があった

- 詳細設計・実装という工程を踏んでいく中で、Aチームの開発環境の細かい確認が必要だった
    - テーブルの結合方法や、どのようなデータが入るかなど
    - インフラ構成のキャチアップ(独自のフレームワークやツールの仕組みなど)
    - APIの使い方
- 実装していく中で、要件定義の見直しが発生した
- 他システム連携のAPIが揃ってないなど、見えない部分が多かった
- ドキュメント不足
    - この情報、ドキュメントにあります？ いやそれはないから、あの人に聞いて〜　みたいな 
- チームの他メンバーが忙しい
    - 簡単な質問はメッセで解決できるが込み入った内容は対面で図などを書きながらやりたい
- 開発メンバー以外(企画、QA担当など)も常駐している
    - QAで問題が合った時に、仕様を再検討すべき場面があり企画のメンバーとのMTGが必要だった

# 結論
リモートがうまく回るかどうか、割と"個人"にフォーカスして考えていたが、それよりも
"プロジェクトの規模・状況"(落ち着いている時期なのかリリース前なのか)に大きく左右されると感じた。
リリースまであまり時間がなくプロジェクト全体で忙しいような時は、リモートでのコミュニケーションはメッセでは機能するものの、ちょっと今ビデオ会議いいですかーみたいなのがやりづらい部分がある。
また役割によっても、リモートとして完結しやすいかが変わってくる。開発だけするか、要件定義から入ってリリースまでするかでコミュニケーションを取る必要がある人も変わってくるため。

# 余談
近くで開発する場合、座席の位置も重要で、向かいに配置するより、隣あるいは後ろにいる(お互いが立ち上がらないで話しかけられる)のが本当に重要:soccer:
向かいは近いけど、モニター見せながら説明とかはやりにくい。
これを変えただけで、コミュニケーション面は大きく改善されました。
###### 席を近くにすることで安心するのではなく、物理的にコミュニケーションが取りやすい位置関係になっているかを意識することが重要！
In-App Messagingでできることが増えてた！

# この記事について
アプリの成長のためにFirebaseが提供するサービスの中にIn-App Messagingというサービスがあります。
In-App Messagingに更新が入り新しくできるようになったことが増えたっぽいので試して感じたことを記事にしました。

# 追加されたもの
気づいた範囲で記載します！
漏れてたらコメントいただけると幸いです😍

## メッセージレイアウトにCardが追加された
<img width="1184" alt="card.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75593/c60f4bbe-0e43-017b-5b23-513fd4cb5f06.png">

適当にメッセージと画像を設定してみた感じ↓
<img width="437" alt="スクリーンショット 2019-06-21 0.38.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75593/7af2e02e-6b50-6f13-a230-14b1e7bd31a8.png">

Cardが追加される前のメッセージレイアウトの中からだとModalが一番近いデザインになりそうですが、Cardの方が画像の大きさも大きそうなのと全体的な見た目が良い気がします！！！
(デザイナーさんと相談するときに両方見せるが吉😁)
<img width="435" alt="スクリーンショット 2019-06-21 0.43.06 1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75593/88cd0e75-eab1-37b8-8900-4bd1b658bc69.png">

Card特有の設定になりますが、secondaryButtonを配置することが可能です！
こちらは必須ではないので必要があればって感じですかね

<img width="1181" alt="スクリーンショット 2019-06-21 0.29.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75593/72aacc37-7db2-6d2c-4833-e36b6810c295.png">

また、`「閉じる」アクションに指定する`というチェックボックスがCardではButtonを配置する際に存在します。

Modalでは閉じるボタンが右上に存在していましたが、Cardでは存在してないためなのかな？

閉じるButtonを開発者側で設定しなければならないので、primaryButtonのActionが閉じるでない場合はsecondaryで閉じるアクションを指定しておくのが良さそうですね！
(なんだよ閉じれないのかよこのMessageというヘイトを与えないためにも)

## In-App-Messagingのキャンペーンを手動で呼び出すことが可能に！
こちらがAnalyticsが関連する新しいくできることでした😅

これまでは、アプリからMessageを表示させるための処理を書くことはできなかったのですが、
Messageを表示する際のトリガーに設定しているEventをアプリ側で以下のように書くとMessageを開くことが可能になるそうです！

```swift
InAppMessaging.inAppMessaging().triggerEvent("view_item")
```

詳細はsu-さんのブログでまとまっているのでリンクを共有させていただきます！
[In-App-Messagingのキャンペーンを手動で呼び出せるようになった](https://tech-blog.sgr-ksmt.org/2019/06/21/023259/)

## Messageを表示するトリガーにAnalyticsEventを設定することができる！(既存でした😅)

(こちら新しくできるようになったというより僕が気付いてなかっただけかも😅)追記
がしかし、便利な機能なので消さすに残しておきますね！

`view_item`: Eventを商品画面に遷移したタイミングで送るEvent
view_itemをトリガーに設定することにより商品画面に遷移したタイミングで設定したMessageを表示させることができます。
<img width="1062" alt="スクリーンショット 2019-06-21 0.30.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75593/c7ac4a5c-2b3f-9540-7a3d-612b0ea0a118.png">

これまでは、Messageの配信先をAudienceなどで絞り込むことはできましたが、タイミングをコントロールすることはできなかった(はず)なので今回の変更によりAudienceで配信先を絞り込むかつタイミングは指定のEventが発火したときなど開発者側でMessageをよりコントロールしやすくなったのではないでしょうか！

商品画面に遷移したタイミングで表示を確認！
<img width="375" alt="iOS の画像.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75593/9a023b60-4152-5e11-af2b-cc0e13dbc0b1.jpeg">


# 最後に
アプリを開発するための機能としてFirestoreやCloud Functionsがありますが、
アプリの成長を支えるための機能の更新も見逃せないですね！

どんどん進化していく中キャッチアップが大変ですが、この記事が他の方のキャッチアップにかける時間の短縮になれば幸いです😁

以前書いたIn-App Messagingの記事も追加しておきますmm

AnalyticsのEventをトリガーにするのは前からありましたね笑
以下記事に書いてました🤣
[Firebase In-AppMessagingで爆速Popup実装](https://qiita.com/giiiita/items/301dc342a1f8f033c009)
Firebaseでクライアントからサーバー時刻を取得する

## 概要
ウェブアプリだとクライアント側の時刻ズレを考慮して、サーバ側の時刻を取得して使用する事が多いと思います。
Firebaseでこのような方法を実現する場合の方法の紹介になります。

## 方法
残念ながら調べた限りでは、直接API等で時刻を取得する方法は無いようです。
しかしながら、Firestoreの機能でドキュメント内にサーバ時刻を書き込む機能があるのでそれを利用する事で実現します。
具体的には下記のようになります。


```javascript:sample.js

// 略　firebase.initializeApp等を実施

export default {
  async getServerTime() {
    // ダミーのコレクションにサーバー時刻を書き込む
    const ref = await firebase
      .firestore()
      .collection('dummy_for_time_get') // 任意のダミーコレクション名
      .doc('time')
    await ref.set({
      timestamp: firebase.firestore.FieldValue.serverTimestamp()
    })
    return new Promise((resolve, reject) => {
      try {
        // 書き込みが終わったらデータを取得して返す
        ref.onSnapshot(snapshot => {
          const timestamp = snapshot.data().timestamp
          resolve(timestamp.toDate())
        })
      } catch (err) {
        reject(err)
      }
    })
  }
}

```

上記のコードでは下記のような処理を実施します。

1.  Firestoreのダミーコレクションにサーバ時刻(firebase.firestore.FieldValue.serverTimestamp())を書き込む
2.  書き込んだデータを変更通知で取得

ちょっと手が込みますが、無事取得できました。

[参照元](https://stackoverflow.com/questions/24202641/how-can-you-get-the-server-time-from-firebase)
firewalld インバウンドとアウトバンドのIP制限

## firewalld IP制限 インバウンド

#### 設定前の確認

```
# firewall-cmd --get-active-zones
public
  interfaces: em1
```

#### 永続的にIP遮断

```
# firewall-cmd --zone=drop --permanent --add-source=xxx.xxx.xxx.xxx/24
```

#### 設定反映

```
# firewall-cmd --reload
```

#### 設定後の確認

```
# firewall-cmd --get-active-zones
drop
  sources: xxx.xxx.xxx.xxx/24
public
  interfaces: em1
```

## firewalld IP制限 アウトバウンド

#### イーサネットの口を確認

通常 lo, em1が表示される。
複数イーサネットがあると em2とか表示される。

```
# ip a
```

#### firewalld IP制限 アウトバウンド

ダイレクトルールで記載する。

```
# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 1 -d xxx.xxx.xxx.xxx/24 -o em1 -j DROP
```

* ダイレクトルールはiptablesの書き方が利用できる。
* OUTPUT の後の数字は優先順位。複数ダイレクトルールを書くなら数字変える。
* -o em1 は対応するイーサネットを書く。
    * 例えば複数あってem1, em2があった場合、em1で1回、em2で1回firewall-cmdを流す

#### ダイレクトルール反映前の確認

```
# firewall-cmd --direct --get-all-rules
反映前なので結果は空
```

#### 設定反映

```
# firewall-cmd --reload
```

#### ダイレクトルール反映後の確認

```
# firewall-cmd --direct --get-all-rules
ipv4 filter OUTPUT 1 -d xxx.xxx.xxx.xxx/24 -o em1 -j DROP
```

#### 参考：ダイレクトルールで反映されるファイル

```
# cat /etc/firewalld/direct.xml
```

```xml:direct.xml
<?xml version="1.0" encoding="utf-8"?>
<direct>
  <rule priority="1" table="filter" ipv="ipv4" chain="OUTPUT">-d xxx.xxx.xxx.xxx/24 -o eth0 -j DROP</rule>
</direct>
```

## 情報基URL

* CentOS7のfirewalldで特定のIPを遮断する : クロビス ブログ
  * http://qurobise.blog.jp/archives/1046549836.html
* ダイレクトルールの追加表示削除
  * https://qiita.com/hana_shin/items/bd9ba363ba06882e1fab#63-ダイレクトルールの追加表示削除
* iptablesで特定のIPの通信を遮断する - Qiita
  * https://qiita.com/S-T/items/bf3b1bc85b3e5880871c
ノマドワーカーのための電源確保BOTを作った

#作ったきっかけ

本当はポケモンGoAPIを使って近くのポケモン通知作りたかったけど、
2段階認証やらでつまづいたのであきらめるが、
位置情報を使ったAPIは色々と使い道が出てきそうと思った。

移動中に仕事の電話が入ってきて途中のカフェで急遽仕事したくなった。
電源あるカフェってどこなのかな…って思い、
スタバだったら電源あるだろって思って入ると無かったりしてコーヒー代無駄になったり…

そんな時にDiscordでトリキで電源がなんとか...って会話が聞こえてきた。
現在地から電源が確保できて自分の入りたい店舗が表示されるとありがたいなと思ってLINEBOTを作りました。

#動作イメージ
[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/cTTfOaH2EY4/0.jpg)](http://www.youtube.com/watch?v=cTTfOaH2EY4)



#機能概要
1.LINEBOTの対話画面にて、自分の位置情報を送る
2.経度緯度情報からモバイラーズオアシスのMAPリンクが送られてくる。
3.リンクを開くと電源が確保できるカフェがアイコン表示される。



#サンプルコード
ngrokで動くけどNowで動かなかった…

```node.js：server.js
'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const request = require('request');
const PORT = process.env.PORT || 3000;

const config = {
    channelSecret: '[channelSecret]',
    channelAccessToken: '[channelAccessToken]'
};

const app = express();
var json;

app.get('/', (req, res) => res.send('Hello LINE BOT!'));
app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);
    console.log(req.body.events[0].message.latitude);
    console.log(req.body.events[0].message.longitude);

    var latGeo =req.body.events[0].message.latitude;
    var lonGeo =req.body.events[0].message.longitude;

    request('http://oasis.mogya.com/api/v0/map?lat=' + latGeo + '&lng=' + lonGeo + '', function (error, response, body) {
        var jsonBefore = JSON.parse(body);
        //console.log(jsonBefore.url);
        json=jsonBefore.url;
    })
    
    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
    });

const client = new line.Client(config);

function handleEvent(event) {

//位置情報以外が来た場合は、テキストで返す
  if (event.type !== 'message' || event.message.type !== 'location') {
    //return Promise.resolve(null);
    return client.replyMessage(event.replyToken, {
    type: 'text',
    text: event.message.text, //実際に返信の言葉を入れる箇所
    text: '左下の「＋」アイコンから位置情報を送ってください' //実際に返信の言葉を入れる箇所

    
    });
  }


  //Message
  return client.replyMessage(event.replyToken, {    
    type: "template",
    altText: "代替テキスト",
    template: {
        type: 'buttons',
        actions: [
            {
                type: 'uri',
                uri: json,
                label: '近場で電源確保できる場所を開く'
            }
        ],
        title: '電源がなくて困ったらこココ！',
        text: ' 電源マップ'
    }
}

);
  

}


//app.listen(PORT);
(process.env.NOW_REGION) ? module.exports = app : app.listen(PORT);
console.log(`Server running at ${PORT}`);

```
妄想プロダクト室「おふくろの味BOT」のプロトタイプ

# はじめに
おっさんになったからだろうか、、、、。
最近、やけに”おふくろ系の味”を求めるようになった。
（フランチャイズは、もう勘弁してくれ、、、）
けれど、ググっても中々おふくろの味に出会えない。
そこで、おふくろの味を見つけてくれるLINEbotをつくろうと考えた。
実装の前に、、、　**”ググればいんじゃね？”**　そんな声が聞こえた気がしたので
**Linebotで実装する意味**があるか調査してみた。

## ”おふくろの味”botが必要か調査してみた

![reserch.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420554/c34a4bb7-7ee6-0abc-9b94-2025f3ada936.jpeg)


知人68人にアンケート**「*1人で外食するときの悩み*」**をとった結果、、、
多くの人に下記のような悩みがあるのがわかった。
**・飲食店選びで、検索をするのに疲れている**
**・いつものお店（フランチャイズなど）に飽きている**


**少なくとも、50人はおふくろの味botを求めている！！！**
（単純な私はモチベーションが爆上がりした）


# おふくろの味bot概要
位置情報を入力すると、おふくろの味がしそうなお店を3件botが返してくれます。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">初めてのjs、node.jsでLINE botつくりました！ <a href="https://t.co/WmKualidUh">pic.twitter.com/WmKualidUh</a></p>&mdash; taichi (@MakingEmo) <a href="https://twitter.com/MakingEmo/status/1141834469906583553?ref_src=twsrc%5Etfw">2019年6月20日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 制作内容
###素材
・node.js
・Messaging API
・ぐるなび　レストラン検索AOU

###構成図
![image.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420554/67431f66-22c0-a704-5841-d21441423958.jpeg)


## 使い方
###①位置情報の入力
<img width="375" alt="iOS の画像 (1).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420554/6521cbf2-489f-0027-26d6-e2c5d8b5990d.png">

<img width="375" alt="iOS の画像.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420554/bfaf1718-c116-3d6b-da4d-4c0e330549d7.png">

###②おふくろの味を感じるお店が3件オススメされる
<img width="375" alt="iOS の画像.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420554/3993df71-6314-b553-08d3-c71feb007b4f.png">

## その他①〜お好きな飲食botをつくってください〜
検索クエリをいじって、自分だけの飲食botをつくってください！
（下記は一部のパラメータです）
![parameta.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420554/f76ffc8b-2817-1b89-b326-4db0329fdb35.jpeg)



## その他②〜おふくろの味bot ver2をつくるなら〜
できるかわからないが、ユーザーがlineに投稿した日時を取得し、
**ユーザーがいる場所×Line投稿日時**で、リアルタイム性のあるレコメンドをするbotをつくリたい。
また、お店の情報をもっとお伝えしたいので、カルーセルのUIも検討される。
【JavaScript】文字列を任意の文字数で分割し、配列を作成する方法

# はじめに
JavaScriptで文字列を任意の文字数で分割し、配列を作成する方法です。
結論からいうと正規表現での取得が一番スッキリ書けました。
他にもいくつか試したの方法も記載しておきます。

例では`UGGUGUUAUUAAUGGUUU`の文字列を三文字毎に分割して`["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]`の配列を作成しています。

## 正規表現
```javascript

const rna = "UGGUGUUAUUAAUGGUUU";
let cdn1 = rna.match(/.{3}/g);
console.log(cdn1)
// ["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]
```

## substr

```javascript

const rna = "UGGUGUUAUUAAUGGUUU";
let cdn2 = [];
for (let i = 0; i < rna.length / 3; i++) {
    cdn2.push(rna.substr(i * 3, 3));
}
console.log(cdn2)
// ["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]
```

## substring

```javascript

const rna = "UGGUGUUAUUAAUGGUUU";
let cdn3 = [];
for (let i = 0; i < rna.length; i += 3) {
    cdn3.push(rna.substring(i, i + 3));
}
console.log(cdn3);
// ["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]
```

## slice

``` javascript
const rna = "UGGUGUUAUUAAUGGUUU";
let cdn4 = [];
for (let i = 0; i < rna.length; i+=3) {
    cdn4.push(rna.slice(i, i+3));
}
console.log(cdn4);
// ["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]
```

## reduce
@hayashi-ay さんより教えて頂いたものです。

```javascript
const rna = [..."UGGUGUUAUUAAUGGUUU"];
let cdn5 = rna.reduce( (acc, c, i ) =>  i % 3 ? acc : [...acc, rna.slice( i, i+3 ).join('') ], [] );
console.log(cdn5);
```

# 参考URL
[JavaScriptで、文字列を、指定した文字数で分割して、配列で返す](https://qiita.com/kazunorisasaki/items/453e3fdf5692be9be872)
【SAS】TSVファイルをインポートする


kaggleで使う場面だけでなく、実務でも以外とTSVファイルのSASにインポートして、データセットとして使うことはよくある。

一度、マクロ化すればそうそう直接書くことは少ないので忘れやすい。
マクロ化も含めて備忘録を取っておきたい。

## proc import



```
%macro importTSV(inTsv, outDS);

proc import datafile= &inTSV
	out=&outDS dbms=dlm replace;
	
	delimiter='09'x;
	getnames=yes;
	datarow=2;
run;

%mend;
	
%let train = '/tsvファイルのパス/test01.tsv';

%importTSV(&train, '出力先データセット名');

quit;

```

- 上記の`delimiter='09'x`でimportするファイルの区切り位置を指定する。
- `getnames=yes`で変数名も取り込む。
- `datarow=2`つまり、データセットにする値は２行目からを指定する。



## 終わりに

日本語のファイルを取り込む時ちょいちょい死ねるのはやめてください、、、。

SASェ、、、。



質問、間違いなどがあればコメントください。