OmniAICreator/Qiita-1.07M huggingface dataset:
Apollo Client 3で追加された機能

## はじめに

Apollo Client 3がリリースされました。AC2からの変更点としては

- これまで別々のパッケージから呼び出していた各モジュールが `@apollo/client` に集約された
- `typePolicies` や `Reactive Variable` など、ローカルのステート管理機能が拡充された

といった感じです。実際にAC3を試してみると、今までメインで使っていた機能の使い方はほぼ変わらず、特に上記ローカルのステート管理周辺が改善された印象です。今回はtypePoliciesとReactive Variableのご紹介をします。

## typePolicies

ローカルのキャッシュ（Apollo Clientの`InMemoryCache`と呼ばれる）において、GraphQLスキーマの各タイプをどう扱うかをtypePoliciesで定義できるようになりました。また、バージョン2まで`@client`とローカルリゾルバで行っていた同様の処理を扱うことができます（AC3からLocal Resolverは`deprecated`となっています）。

#### 呼び出し時（`read`）

```javascript

const cache = new InMemoryCache({
  typePolicies: {
    Person: {
      fields: {
        name: {
          read(name) {
            return name.toUpperCase();
          }
        }
      },
    },
  },
})

// クエリの例
export const GET_PERSON = gql`
  query getPersion($id: String!) {
    person(id: $id) {
      id
      name
      age	
    }
  }
`
```
レンダーされるnameが大文字に。

#### 更新時（`merge`）

```javascript

const cache = new InMemoryCache({
  typePolicies: {
    Agenda: {
      fields: {
        tasks: {
          merge(existing = [], incoming: any[]) {
            return [...existing, ...incoming];
          },
        },
      },
    },
  },
})
```
上記はキャッシュに存在する配列とサーバーから送られる配列の値をマージする例。


## Reactive Variable

上記typePoliciesの`read`からReactive Variableを参照すれば、Reactive Variableを更新することでキャッシュが返す値を更新することが可能。

```javascript

import { makeVar } from '@apollo/client'

const cartItems = makeVar([])

// 値のセット
cartItems([100, 101, 102])

// 値の取得
const currentCartItems = cartItems()
```

## まとめ
今回はApollo Client 3で追加されたtypePoliciesとReactive Variableを紹介しました。サンプルは[こちら](https://github.com/taisuke-j/apollo-demo)。

### References
- https://www.apollographql.com/blog/announcing-the-release-of-apollo-client-3-0/
オブジェクト指向のプログラミング用語

##クラス
・データの型
・別名：「オブジェクトの設計図」「オブジェクトのひな形」
・クラスが同じであれば、保持している属性（データ項目）や使用できるメソッドが同じ

##オブジェクト、インスタンス、レシーバ
・クラスをもとに作られたデータのかたまり
・メソッドとの関係を説明する場合、レシーバーと呼ばれることもある。

```ruby:
food = Food.new('apple', '7:00')
food.name
```
このコードは以下のように説明される場合がある。
「２行目でFoodオブジェクトのnameメソッドを呼び出しています。」
「ここでのnameメソッドのレシーバはfoodです。」

##メソッド、メッセージ
・処理をひとまとめにし、名前をつけたもの。

```ruby:
food = Food.new('apple', '7:00')
food.name
```
「2行目ではfoodというレシーバーに対して、nameというメッセージを送っている」

##状態（ステート）
・オブジェクトごとに保持されるデータのこと
・Foodクラスがもつ「名前」や「年齢」といったデータも「Foodの状態」に含まれる

##属性（アトリビュート、プロパティ）
・オブジェクトから取得できる値のこと

```ruby:
food = Food.new('apple', '7:00')
food.name = 'apple'
```

参照
プロを目指す人のためのRuby入門
docker コンテナで conda install が失敗するようになった (Solving environment: ...working... failed with initial frozen solve. )

こちらのコンテナをベースにtensorflow-gpuを使っていたのですが、
https://hub.docker.com/r/jupyter/datascience-notebook/
ある時から表題のエラーでコンテナのビルドに失敗するようになってしまいました。

こちらのSOの解決法が有効でしたので、メモがてら記事にしておきます。
https://stackoverflow.com/questions/60300787/errors-such-as-solving-environment-failed-with-initial-frozen-solve-retrying

#解決策
```dockerfile
# 序盤にこの2つのコマンドを追加
RUN conda config --set channel_priority false
RUN conda update --all
```

下の行だけで解決する、との記事も拝見し昨年はそれで解決したケースもあったのですが、
今回は上の行も必要でした。
HomebrewでGoogle Cloud SDK (gcloudコマンド) をインストールする


HomebrewでGoogle Cloud SDKをインストールするには[Homebrew Cask](https://github.com/Homebrew/homebrew-cask)を使います:


```
brew cask install google-cloud-sdk
```

インストールが成功すると、`gcloud`コマンドが使えるようになります:

```
$ gcloud --version
Google Cloud SDK 301.0.0
bq 2.0.58
core 2020.07.10
gsutil 4.51
```

## Zshの設定


```zsh:.zshrc
source "$(brew --prefix)/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/path.zsh.inc"
source "$(brew --prefix)/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/completion.zsh.inc"
```
SSHパスワード認証時の入力を自動化する

SSH公開鍵認証だとsshconfigに記載すればいいが、パスワード認証の場合はセキュリティの関係でパスワードを記載することは許可されていない。
そのため、自動化するためにはsshpassというツールを使ってワンコマンドで作成する必要があった。

**sshpassをインストールする**

```
brew install https://raw.githubusercontent.com/kadwanev/bigboybrew/master/Library/Formula/sshpass.rb
```

** インストールされているかを確認する **

```
sshpass
Usage: sshpass [-f|-d|-p|-e] [-hV] command parameters
   -f filename   Take password to use from file
   -d number     Use number as file descriptor for getting password
   -p password   Provide password as argument (security unwise)
   -e            Password is passed as env-var "SSHPASS"
   With no parameters - password will be taken from stdin

   -P prompt     Which string should sshpass search for to detect a password prompt
   -v            Be verbose about what you're doing
   -h            Show help (this screen)
   -V            Print version information
At most one of -f, -d, -p or -e should be used

```

**sshpassを使ってログインできるか試す**

```
sshpass -p "パスワード" ssh ユーザ名@ホスト名
```


**コマンドを登録する**
毎回入力するのは手間なので、自分の使っているシェルファイルに関数として登録しよう。
他に書くところがあるのかもしれないが、ひとまず.zprofileに記載した。

```zsh

function myssh(){ 
　　sshpass -p "パスワード" ssh ユーザ名@ホスト名
}
```
sourceで読み込み直して、コマンドが実行できるかを確認しておしまい。

```zsh

myssh
```
AWS認定試験 AWS Developer Associate(認定開発者) の学習内容

## AWS認定試験 AWS Developer Associate(認定開発者) の学習内容

##### AWS Developer Associate試験に合格(2020/07/13)したので参考までに実施した学習内容を公開します。

AWS Developer AssociateはAWS Solution Architect Associateほど情報がないので受験にむけた準備がやりづらい試験です。書籍はAWS認定アソシエイト３資格対策しか出ておらず、それも３つの試験に跨がる内容のためこの一冊読んだだけで合格できるか自信が持てないところです。

##### 試験に向けて次の内容を実施しました。

・[試験問題サンプル](https://d1.awsstatic.com/ja_JP/training-and-certification/docs-dev-associate/AWS-Certified-Developer-Associate_Sample-Questions.pdf)実施
・AWSの公式オンライン講義 [Exam Readiness: AWS Certified Developer – Associate (Digital)](https://www.aws.training/Details/Curriculum?id=19185) を視聴
・[AWS認定アソシエイト３資格対策 リックテレコム](https://www.amazon.co.jp/dp/B07TT1N73K/) ¥2,503の章末問題の実施
・DVA-P01: AWS 認定 デベロッパー – アソシエイト模擬 ¥2,200 受験

##### 準備期間は１週間ほど

ただし、直近のプロジェクトでDynamo, ElastiCache Redis, RDS(Aurora)を使ったLambdaアプリケーションを作っていたので一部、開発内容が役にたっています。
また、AWS Solution Architect Associateには既に合格済みでした。

##### 試験の感想

試験は140分で65問が出ました。
試験問題は思ったより知らないことが出てこれは無理と思いました。合格できたのは絶対に違う選択肢を落として正しい答えを選択できたのだと思います。ただし、ふるい落とした後の２択で悩んだものもありました。
また、日本語がおかしい選択肢はふるい落とせます。

-------------



##### AWSの公式オンライン講義の内容に沿って試験出題内容との関連性を述べたいと思います。

### 1. Development

#### CI/CD

普通に使ったことがあれば試験対策は十分かと思います。

* CodeCommit

CodeCommitがリポジトリのため、どうやってローカルから接続するのか、認証ルールの設定などを経験しておくと良さそうです。

* CodeBuild
* CodePipeline
* CodeDeploy

buildspecの記載内容を少し見ておくと良いです。

* X-Ray

X-Rayで取得する情報を絞る方法について出題された。注釈が正解。

* CloudWatch

CICDに関わらずいっぱい出題されます。

#### Scalable Application

以下の講義内容でした。AWS Well-Architectedフレームワークを知っておきましょうということです。

* AZを使う
* ヘルスチェックを行う
* ステートレスにしてステータス情報はdbに保存する
* AWSのことをよく知る

#### Elastic Beanstalk

今回思ったより多くのElastic Beanstalkの出題がありました。

一度チュートリアルでElastic Beanstalkのアプリケーションを作っておいた方が良さそうです。

定義ファイルの記述内容。
Dockerイメージの取得方法。
Dockerの設定はどこに記述するか。
ebコマンド。
ロードバランサーの切り替え方法。

#### AWS OpsWorks

出題ありませんでした。

#### AWS CloudFormation

構築したAWS資源がどの様にスタックと紐づいているのかを問われる試験が出題されました。

### 2. Security

s3の暗号化、通信内容を暗号化するのか保存ファイルを暗号化するかが複数出題されました。
認証、CognitoやAWS Security Token Serviceで何ができるかを押さえておくと良いでしょう。

応用、ひっかけ問題としてAWSアカウントIDを跨いで利用するときにどうすれば良いのかなどが出題されました。

[試験問題サンプル](https://d1.awsstatic.com/ja_JP/training-and-certification/docs-dev-associate/AWS-Certified-Developer-Associate_Sample-Questions.pdf)では、DBのパスワードをAWS Secrets Managerに保存する方法が出てくるが、Systems Managerのパラメータストアに保存するのを答えさせる出題はあったがAWS Secrets Managerは選択肢には出てこなかった。

### 3. Development with AWS Services

* S3

s3単体より、他の分野と絡めて多く出てきます。

* DynamoDB

RCU, WCUの計算は必ず出る様です。覚えておきましょう。

DAXとElastiCacheのどちらを選ばせるのかの試験も出ました。Dynamoを高速化するのはDAXです。

* SNS

CloudWatchからの通知などで出題されました。

* SQS

大量データの受信の対策として出てきました。

* Step Functions
* API Gateway

キャッシュをクリアする方法

* CloudFront

常識的なCDNとして使う試験が出題されました。

* ElastiCache

遅延読み込みのロジックを確認する試験が出題されました。
遅延読み込みと書き込みスルーは必ず出る様です。

* ECS

aws cliによるecr(Elastic Container Registry)へのログイン方法が出題されました。

* Lambda

Lambda共通ライブラリをLambdaレイヤーとしてデプロイする問題が出題されました。
Lambdaの２重起動について出題されました。

### 4. Refactoring

標準的なアプリケーションをAWSではどのサービスを使って構築するか。
ソリューションアーキテクトの試験と同じ内容が出ていました。

### 5. Monitoring and Troubleshooting

#### CloudWatch

標準メトリクス、カスタムメトリクスで何を取得できるのか。
CloudWatch Logsはどうすると使える様になるのかを問う問題。ロググループを作らないとCloudWatch Logsに出力される様にならないなど。
画像の人の関心を得やすい部分(Saliency)のサムネイル化について

そこまで詳しくないのですが、今回はDeepLearningのモデルでSaliencyを測ることで、画像内の人の関心を集めている部分(人の視線が集まるような部分)をトリミングできることを紹介します。
ここでは、すみませんが、こいうことができるよという話が主に中心で、iOSのトリミングのケースでしか紹介しませんが、iOSに限らず他のプラットフォームでも同様なことが可能だと思います。

今までは画像のサムネイル化する際に画像の中心をサムネイル化することが多かったと思いますが、
下記全体の画像内から

![Screen Shot 2020-07-15 at 9.36.19のコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308423/62ccda6a-bf1d-578f-7104-6100582d8309.png)

人が関心を集めるポイントをDeepLearningのモデルでSaliencyで抽出することで

![Screen Shot 2020-07-15 at 9.35.39のコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308423/ec2db3ed-2ff6-976d-cf2e-fcd71b48ec58.png)

参考:https://medium.com/better-programming/cropping-areas-of-interest-using-vision-in-ios-e83b5e53440b

画像内で人の関心を集めるであろうポイントを抽出できます。(上記の場合は、何もないところで左端にあるリモコンを抽出できています)
上記の場合はiOS13のVision Frameworkで標準で実装されている機能を使用していますが、どこかで学習されているモデルを手に入れればサーバーにアップロードされた画像のサムネイルを作成する際に、画像内で人の関心を集めるであろうポイントを中心にサムネイル化することもできると思います。

そうすることで、より人が関心を集める箇所をサムネイル化していくことができ、ユーザー体験が向上すると思います。

iOSのVison Frameworkの公式ドキュメントや公式のサンプルは下記に記載されています。
https://developer.apple.com/documentation/vision/cropping_images_using_saliency
float型の数値は10進数の少数と同じではない

**Python学習のアウトプット。**

mathモジュールには、`.isclose()関数`というものがある。
float型の数値を比較する際に用いる。それぞれが近似値かどうか。

```
>>> import math
>>> math.isclose(0.1+0.1+0.1, 0.3)
True
```

大前提として、Pythonを含めた計算機ハードウェアは浮動小数点数を正確に表すことができない。2進法の分数として表現されるため、例えば、2進法の分数

    0.001
    
は

    0/2 + 0/4 + 1/8
    
という値になる。

ただし、ほとんどの少数は2進法の分数として正確に表現できないため、近似値が記憶される。
    
float型の正確な値を取得するには、`float.as_integer_ratio()`メソッドを使う。xに0.3を代入し、このメソッドを使って正確な値を有理数として表現すると……

```
>>> x = 0.3
>>> x.as_integer_ratio()
(5404319552844595, 18014398509481984)
>>> x == 5404319552844595 / 18014398509481984
True
>>> 5404319552844595 / 18014398509481984
0.3
```

なんかすごい。

今度はxに0.1を代入し、3をかけてみる。すると、

```
>>> x = 0.1
>>> x.as_integer_ratio()
(3602879701896397, 36028797018963968)
>>> 3602879701896397 /  36028797018963968
0.1
>>> 3602879701896397 /  36028797018963968 * 3
0.30000000000000004
```

こう。限りなく0.3に近いんだけど、`0.1 + 0.1 + 0.1 == 0.3`の条件式は成立しない。

しかし、`0.30000000000000004`は`0.3の近似値`と言える。そのため、上記のように、

```
>>> import math
>>> math.isclose(0.1+0.1+0.1, 0.3)
True
```

`.isclose()関数`はTrueを返してくれる。

参考
Pythonの公式ドキュメント: [15. 浮動小数点演算、その問題と制限](https://docs.python.org/ja/3/tutorial/floatingpoint.html)
re.splitの分割とre.compileの置換

```py
import re

s = 'my name is mike'
print(s.split())

p = re.compile(r'\W+')
print(p.split(s))

p = re.compile('blue|white|red')
print(p.sub('colour', 'blue socks and red shoes'))
print(p.sub('colour', 'blue socks and red shoes', count=1))
print(p.subn('colour', 'blue socks and red shoes'))
```
実行結果：

```txt
['my', 'name', 'is', 'mike']
['my', 'name', 'is', 'mike']
colour socks and colour shoes
colour socks and red shoes
('colour socks and colour shoes', 2)
```
Unity ML-Agentsで強化学習を行う「環境構築」「サンプル実行」

## 0．前提 
この記事はUnityを触ったことがあり、強化学習を行ってみたいという人向けになります。（自分用の備忘録でもある）
間違いなどありましたら教えて頂けるとありがたいです。
##1.実行環境
Unity 2019.3.0f6
Anaconda 2019.03 for Windows 64bit Python3.7
ml-agents-release_3
##2.環境構築
####Unityのダウンロード 
https://unity3d.com/get-unity/download/archive
上記URLから好きなバージョンのUnityをダウンロードします。
####ML-Agentsのダウンロード
https://github.com/Unity-Technologies/ml-agents/tree/release_3
ここからzipファイルでダウンロード、またはgitでクローンします。
####Anacondaのダウンロード
https://www.anaconda.com/products/individual
Windows 64bit Python3.7をダウンロードします。

`Anaconda Prompt`を起動します。
#####環境作成
```
conda create -n [名前] Python=3.7
```
環境を作成します。
`[名前]`は各自好きな名前をつけて構いません。
#####環境切り替え
```
conda activate [名前]
```
先ほど作成した環境に切り替えます。
`[名前]`は先ほどつけた名前になります。
![スクリーンショット (17).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666040/86108574-d994-fafe-ef27-66b846229755.png)

先頭の（）内が現在の環境の名前になっていて、環境が切り替わっているのがわかると思います。
#####フォルダ切り替え
```
cd [ダウンロードしたML-Agentsのフォルダまでのパス]
```
先ほどダウンロード（クローン）したフォルダに移動します。
#####ML-Agents用の環境インストール
```
pip install mlagents
```
ML-Agents用の環境をインストールします。
少し時間がかかるので気長に待ちましょう
学習時にもターミナルは使用するので、閉じずに残しておいて下さい。

以上で、環境の準備は完了です。
##3.サンプル実行
ここでは、サンプルを使って実際に強化学習を行います。
今回は「3DBall」を用いて説明していきます。
####学習の開始
```
mlagents-learn config/ppo/3DBall.yaml --run-id=[名前]
```
他のサンプルでやるときは`3DBall`のところは選んだサンプルの名前に変えてください。
`[名前]`に関しては自由です。保存される時のフォルダ名にもなるので、各自わかりやすくしてください。
![スクリーンショット (14).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666040/4af1915a-935c-e362-f5f4-7de3e4dd3965.png)
Unityマークとこのような文が出てきたら**`シーンを実行して下さい。`**
![スクリーンショット (24).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666040/729e6f9a-7dc8-98ea-749a-e1a36d299692.png)

規定のステップ数が終わると学習が完了します。
完了すると上のようなログが流れます。
※途中でやめたい時は`CTRL+C`で途中で切りあげることもできます。

####モデルを実行

学習が完了したモデルは`results`フォルダの中に実行時に決めた名前フォルダごとに入ります。
フォルダの中に入っている`NNファイル`をプロジェクト内にコピーしてください。
![スクリーンショット (24).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666040/d88c4a19-241b-88ab-7fa4-cd91491075f1.png)
持ってきたモデルを`Behavior Parameters`の中の`Model`に貼ってください。
これで、モデルを切り替えることができました。

では、実際にシーンを実行してみましょう。
学習ができていることがわかると思います。
##4.おわりに
今回は、詳しい説明は省いて、実際に学習を行うまでのやり方を説明しました。
次は、もう少し詳しいところまで記事にする予定です。
Vue.js備忘録(1)

# Vue.jsの勉強過程の記録(1)
### 目標
ボタンを押すとテキストが切り替わる

```index.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <div id="app">
        <p>{{ name }}</p>
        <button v-on:click="change">{{ name2 }}</button>
        <!-- このボタンを押すとpタグの中身が切り替わる -->
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- vue.jsの読み込み -->
    <script src="index.js"></script>
  </body>
</html>
```

```index.js
// インスタンスの生成
var app = new Vue({
  el: "#app",
//適用範囲の指定（id="app")
  data: {
    name: 'before',
    name2: 'change'
    //nameの中身を'before',name2の中身を'before2'に
  },
  methods: {
    change: function(){
      this.name = 'after';
      //changeメソッドの設定
      //nameの中身を'after'に変える
    }
  }
})
```

jqueryに比べて分かりやすく書けるなと思いました。
まだまだ初歩中の初歩ですが、頑張ります。
Azure Automation Update Management ってなんぞや

Microsoft のクラウドービス Azure のサービスの1つである Azure Automation Update Management について整理したいと思います。
# Azure Automation Update Management とは
Azure Automation Update Management は Azure Log Analytics と Azure Automation を組み合わせたサービスらしく
1. Azure Log Analytics で対象のサーバーの状態監視
2. Azure Automation でアップデートのタイミングを管理
といった感じみたい

ここで注目ポイントは

- クラウドサービスを使用するので組織ネットワーク外への通信が必要
- アップデート自体は WSUS や Windows Update の力を借りること
- Linux サーバーも守備範囲にはいっていること

です。

# ネックになりそうなこと
もちろん Azure 上の VM との連携はできるので、オンプレミスの話に焦点を当てると、
サーバー自体は組織外との通信をしないことが前提のものがほとんどのなか、Azure Automation Update Management はご法度ともいえるそれをする必要があるのがネックだといえるかもしれない。
しかし、そこは対応策が用意されているのでそれで解決できれば便利なサービスだと思われる。

## 対応策① プロキシサーバー
そもそもサーバーが組織の外と通信を行うときに使う常套手段で、これがそのまま使えるならこれに越したことはない。
一方で、社内のファイルサーバーなどで、外との通信を普段からしていないサーバーに関しては Log Analytics ゲートウェイ を用意することで一度そこを経由して Azure と通信をすることができる。

## 対応策② 閉域網
Azure との通信のセキュリティをあげる手段として、Express Route を使用した閉域網の接続方法がある。
これを使えば Azure Automation Update Management でもセキュアな方法で通信をすることができる。
Express Route を使用した通信方法にはPrivate Peering と Microsoft Peering があり、これを使うと通信がインターネットに出ることを防ぐことが可能。
この二つの機能については別記事でまとめる予定。
しかし、Azure Automation Update Management はパブリック IP アドレスを持つサービスなので Private Link などのサービスと組み合わせないとPrivate Peering は使用できず、Azure Automation の Private Link はプレビューなので実用段階ではない。
一方、Microfoft Peering の方はパブリック IP でそのまま使用可能であるので今すぐ使いたい場合はこっちを選択することになるだろう。

# まとめ
以上が駆け出しのインフラエンジニア的立場から見た Azure Automation Update Management であった。
ネガティブな面をたくさん書いてしまったが、オンプレミスや Linux のサーバーを対象にできるのは相当な強みだといえるだろう。
Xcodeでビルドできなかった話と対処法(Command PhaseScriptExecution failed with a nonzero exit code)

# 起きたこと


```
Command PhaseScriptExecution failed with a nonzero exit code
```

こんな感じでエラーが出た。

<img width="872" alt="スクリーンショット 2020-07-15 9.22.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199256/71d98f2a-6174-d916-a882-0eaae2d6d2a2.png">


# 解決法
注目すべきは、以下の部分

```
Cannot retrive binary file from bundle at (省略)/Carthage/Build/iOS/RealmSwift.framework%0A/
```
末尾に`framework%0A/`とあるように、以下画像のInputFilesに改行が入っていたのが原因でした。

cocoapodsからCarthageに移行している時に、コピペしながらここら辺作っていたので起きたようです。（コピペする時に何か動きがおかしい感じはしたので、、）


<img width="702" alt="スクリーンショット 2020-07-15 9.20.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199256/bc67cd17-822f-d5a7-42f9-c04ebc48363b.png">


2020/07/15
Xcode version 11.4.1
以上です。
vimでI LOVE...と打つと恋つづ、何度も


![z6M9Dc2ytC.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67471/dbe81d93-ce4d-1b5d-76e6-3988424fe2ac.gif)

vimで`I LOVE...`と打つと恋がどこまでも続きました。
I LOVE I LOVE 何度も

## 一応解説

`I`: insertモードに移行
`LOVE`: 文字を入力
(esc): insertモードを抜ける
`.`: 直前の操作を繰り返す
`.`: 何度も
`.`: 何度も

## 関連

[vimでi love uと打つと愛が消える](https://qiita.com/yamamoto_hiroya/items/73e385785608d2f6133f)
AudioPlayer + 署名付きURLを使うときはExpireに気をつけるべし

# 概要
AudioPlayerで最大60分のサウンドを流すAlexaスキルで、10分過ぎたあたりで無音になるという問題に遭遇しました。
その原因の調査と対応についてのメモです。

# 結論
AudioPlayerで署名付きURLを使うときのExpireは、サウンドの長さ程度にしておいたほうが良い。

# 調査
まずは、mp3自体に問題がないかを確認しましたが特に以上はありません。
次に怪しい箇所としては、署名付きURLだったので、S3のアクセスログを有効にして眺めてみます。

※1 `hogehoge.mp3`は60分のサウンド
※2 署名付きURLのExpireは60秒


```
$ cat 2020-07-14-* | sort -k3 | grep hogehoge.mp3 | awk '{print $3, $9, $10, $12, $13}'
[14/Jul/2020:13:10:51 ${BUCKET}/hogehoge.mp3 "HEAD HTTP/1.1" 403
[14/Jul/2020:13:10:51 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 200
[14/Jul/2020:13:10:52 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 200
[14/Jul/2020:13:10:52 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 206
[14/Jul/2020:13:16:23 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 403
[14/Jul/2020:13:16:25 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 403
[14/Jul/2020:13:16:27 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 403
[14/Jul/2020:13:16:30 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 403
[14/Jul/2020:13:16:34 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 403
[14/Jul/2020:13:17:34 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 403
[14/Jul/2020:13:18:35 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 403
[14/Jul/2020:13:19:35 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 403
[14/Jul/2020:13:20:35 ${BUCKET}/hogehoge.mp3 "GET HTTP/1.1" 403
```

どうやら最初は200を返しているが、途中から403(URLの期限切れ）を返している。
約6分後に403を返しているが、無音になるのは10分過ぎなので、ある程度バッファリングして、足りなくなったら随時取りに行く実装の様子。
エラーになったら1分ごとにリトライしてるのも良いですね。

# 対応
署名付きURLのExpireをサウンド長(60分)にしてリトライ

```
$ cat 2020-07-14-* | sort -k3 | grep fugafuga.mp3 | awk '{print $3, $9, $10, $12, $13}'
[14/Jul/2020:13:48:53 ${BUCKET}/fugafuga.mp3 "HEAD HTTP/1.1" 403
[14/Jul/2020:13:48:53 ${BUCKET}/fugafuga.mp3 "GET HTTP/1.1" 200
[14/Jul/2020:13:48:53 ${BUCKET}/fugafuga.mp3 "GET HTTP/1.1" 206
[14/Jul/2020:13:48:53 ${BUCKET}/fugafuga.mp3 "GET HTTP/1.1" 200
[14/Jul/2020:13:55:15 ${BUCKET}/fugafuga.mp3 "GET HTTP/1.1" 206
[14/Jul/2020:14:00:42 ${BUCKET}/fugafuga.mp3 "GET HTTP/1.1" 206
[14/Jul/2020:14:06:10 ${BUCKET}/fugafuga.mp3 "GET HTTP/1.1" 206
```
6分後以降のアクセスも2XXを返していて、サウンドも無音にならず長時間流れるようになりました。

# 備考
[wikipedia](https://ja.wikipedia.org/wiki/HTTP%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89)のステータスコード206の説明
> 部分的内容。部分的GETリクエストを受理したときに、返される。
> 例: ダウンロードツール等で分割ダウンロードを行った場合や、レジュームを行った場合に返される。

部分取得してくれてるみたいですね。転送量的にもお優しそう。

----

 [公式ドキュメント](https://developer.amazon.com/ja-JP/docs/alexa/hosted-skills/alexa-hosted-skills-media-files.html)の説明
Alexa hosted skillsの署名付きURLを利用する場合
> URLの有効期間は60秒です。また、ユーティリティ関数から有効期間を上書きすることはできません。

という制約があります。
つまり長いサウンドを流そうとすると、途中で無音になる可能性があるので注意したほうが良いかもですね。
（データサイズにもよるかもしれない）
ubuntu にpostgresqlを入れたらpeerの関係で入れなかった

他の方も書かれていますが、自分用のものとして。キータの練習投稿として。

環境 ubuntu18.04 PostgreSQL 12.3

ログインしようとすると

```
~/D/v/ubuntu ❯❯❯ psql -U test -d test
psql: error: could not connect to server: FATAL:  Peer authentication failed for user "test"
```

と怒られた。

pgadminを使いたかったので、自分用のロールを作ってしまうのも手かと思ったが、勉強のため

pg_hba.conf の

```
# Database administrative login by Unix domain socket
local   all             postgres    peer


# "local" is for Unix domain socket connections only

local   all             all         peer

```

の二行のpeerをmd５に入れ替え。ハマってしまったところが、この時点でpostgresをリスタートしてしまい、md５パスワードを設定してなかったので、ログインできなくなった。
もう一度peerに戻し、psqlに入り

```

ALTER USER postgres with encrypted password 'your_password';

```
としてpostgresのパスワードを設定後、リスタート。もう一度、pg_hba.conf の前出箇所を peer から md5 にしてやるとOK。pgadminをpostgres として使えます。
ちなみにpg_hba.confは　/etc/postgresql/numOfPg/main/にあると思います。

初投稿なので許して。
re.groupとre.compileとre.VERBOSE

```py
import re

s = ('arn:aws:cloudformation:us-east-2:123456789012:stack/'
     'mystack-mynestedstack-sggfrhxhum7w'
     '/f449b250-b969-11e0-a185-5081d0136786')

# [\w-]で英数字もしくはハイフンの意味になる
# group名をつけています
m = re.match(r'arn:aws:cloudformation:(?P<region>[\w-]+):(?P<account_id>[\d]+)'
             r':stack/(?P<stack_name>[\w-]+)/[\w-]+', s)

if m:
    # group名でアクセスできます
    print(m.group('region'))
    print(m.group('account_id'))
    print(m.group('stack_name'))
```
#re.compileを使う場合
正規表現の部分を何度も書く場合に、効率的です。

```py
import re

s = ('arn:aws:cloudformation:us-east-2:123456789012:stack/'
     'mystack-mynestedstack-sggfrhxhum7w'
     '/f449b250-b969-11e0-a185-5081d0136786')

RE_STACK_ID = re.compile(r'arn:aws:cloudformation:(?P<region>[\w-]+):(?P<account_id>[\d]+)'
                         r':stack/(?P<stack_name>[\w-]+)/[\w-]+')

m = RE_STACK_ID.match(s)

if m:
    # group名でアクセスできます
    print(m.group('region'))
    print(m.group('account_id'))
    print(m.group('stack_name'))
```
#更に、re.VERBOSEを場合
きれいに書けます

```py
import re

s = ('arn:aws:cloudformation:us-east-2:123456789012:stack/'
     'mystack-mynestedstack-sggfrhxhum7w'
     '/f449b250-b969-11e0-a185-5081d0136786')

RE_STACK_ID = re.compile(r"""
    arn:aws:cloudformation:
    (?P<region>[\w-]+):             #region
    (?P<account_id>[\d]+):          #account_id
    stack/
    (?P<stack_name>[\w-]+)/         #stack_name
    [\w-]+""", re.VERBOSE)

m = RE_STACK_ID.match(s)

if m:
    # group名でアクセスできます
    print(m.group('region'))
    print(m.group('account_id'))
    print(m.group('stack_name'))
```
Laravel MySQL 既存テーブルの任意の場所にカラムを追記する 簡易版

# 目的

- 途中でカラムを追加する際に既存カラムの間に新規カラムを追加する方法をまとめる

# 実施環境

- ハードウェア環境

| 項目 | 情報 |
| --- | --- |
| OS | macOS Catalina(10.15.5) |
| ハードウェア | MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) |
| プロセッサ | 2 GHz クアッドコアIntel Core i5 |
| メモリ | 32 GB 3733 MHz LPDDR4 |
| グラフィックス | Intel Iris Plus Graphics 1536 MB |

- ソフトウェア環境

| 項目 | 情報 | 備考 |
| --- | --- | --- |
| PHP バージョン | 7.4.3 | Homwbrewを用いて導入 |
| Laravel バージョン | 7.0.8 | commposerを用いてこちらの方法で導入→[Mac Laravelの環境構築を行う](https://qiita.com/miriwo/items/adaaaeecae69d6392e08) |
| MySQLバージョン | 8.0.19 for osx10.13 on x86_64 | Homwbrewを用いてこちらの方法で導入→[Mac HomebrewでMySQLをインストールする](https://qiita.com/miriwo/items/ef6a064bc545fbea29d9) |

# 記載例

- 下記の様にカラムを追加するマイグレーションファイルに記載することにより任意のカラムの後に新規カラムを追加することができる。

    ```php
    $table->データ型('新規カラム名', オプション)->after('新規カラムを追加したい位置の一つ前ののカラム名');
    ```
Pythonのインデントと文字列フォーマット

# Pythonのインデントと文字列フォーマット
## インデント
インデントは1~いくつまでもでき、違う数同士でやると、エラーが出る。インデント = スペース
## 文字列フォーマット
これらで文字列と文字列を合体できる。

```
print("xxx{}xxx".format(a))
print("xxx{1}xx{0}x".format(a,b))
print("xxx%sxx%sx"%(a,b))
print(f"xxx{b}xx{a}x")
```
Pythonの小数点の扱い

## Pythonの小数点の扱い
小数点の足し算では、0.1+0.1+0.1=0.3と思ったが、0.3000000000000000004になった。
パソコンでは、2進数で計算しているため、0.1が表せないから、少しの誤差がでる。
freeCodeCampでPythonの勉強 part2

# Scientific Computing with Pythonの続き
freeCodeCampでコツコツPythonを勉強しています。
[前回の記事](https://qiita.com/makky0620/items/0f5dbcdd77b5b10cce96)では、**Python for Everybody**と**Arithmetic Formatter**に挑戦しました。今回は**Time Calculator**に挑戦します。

- Python for Everybody
- Scientific Computing with Python Projects
    - Arithmetic Formatter
    - **Time Calculator(今回はここ)**
    - Budget App
    - Polygon Area Calculator
    - Probability Calculator

# 2問目:[Time Calculator](https://www.freecodecamp.org/learn/scientific-computing-with-python/scientific-computing-with-python-projects/time-calculator)

最終的に欲しいものは`add_time`メソッドで、動きは以下の通り

```python
add_time("3:00 PM", "3:10")
# Returns: 6:10 PM

add_time("11:30 AM", "2:32", "Monday")
# Returns: 2:02 PM, Monday

add_time("11:43 AM", "00:20")
# Returns: 12:03 PM

add_time("10:10 PM", "3:30")
# Returns: 1:40 AM (next day)

add_time("11:43 PM", "24:20", "tueSday")
# Returns: 12:03 AM, Thursday (2 days later)

add_time("6:30 PM", "205:12")
# Returns: 7:42 AM (9 days later)

```

## 実装方針

* 第一引数を`datetime`に変換し、第二引数は`:`で時間と分に分けて足していく。
* 曜日に関しては前処理で`大文字+小文字`に変換する(tueSdayなどがあったため)。
* 出力の中の括弧内の文字は、第一引数で入力した時間との日付の差分を計算し、1以下と1より大きい時で分ける。

### 個人的ポイント: 曜日を含めた日時の受け取り
日時と文字列を相互に変換するメソッドとして

* `strptime()`: 文字列 -> 日時への変換
* `strftime()`: 日時 -> 文字列への変換

があります。`strftime()`では`%A`や`%a`などの書式化コードを使うことで曜日を出力することができます。また、AM/PMのような12時間表の場合も`%p`という書式化コードがあります。

```python
import datetime

dt = datetime.datetime(2020, 7, 13)
print(dt.strftime('%A, %a'))
# Monday, Mon
```

しかし、`strptime()`では曜日を`%a`などで受け取ることができませんでした。(年月が決まらないと曜日なんてわからないからね…)

```python
import datetime

dt_str = "7:49 Wednesday"
format = "%H:%M %A"
dt = datetime.strptime(dt_str, format)

print(dt.strftime("%H:%M %A"))
# 07:49 Monday
```

そこで、曜日のリストを作って実装をしてみました。

```python
weeks = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]

def get_week_by_diff(week, diff):
    index = weeks.index(week)
    target_index (index + diff) % len(weeks)
    return weeks[target_index]
```

# 最後に
前回の問題より簡単だった気がした。

次の問題は[*Budget App*](https://www.freecodecamp.org/learn/scientific-computing-with-python/scientific-computing-with-python-projects/budget-app)です。
AWS SDK for Java 1.11.x と 2.x を共存させる

[WindowsローカルのAWS SDKでJavaプログラムを動かすまで](https://qiita.com/e99h2121/items/7b153c12f61470ffdf48) の続き

Amazon AWS SDK 1.x と 2.x は共存できるようだ、というメモ。

```pom.xml
        <aws-java-sdk.version>1.11.699</aws-java-sdk.version>
        <aws-java-sdk2.version>2.13.53</aws-java-sdk2.version>

        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-bom</artifactId>
            <version>${aws-java-sdk.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>${aws-java-sdk2.version}</version>
             <type>pom</type>
            <scope>import</scope>
        </dependency>
```

[AWS SDK for Java のドキュメント](https://docs.aws.amazon.com/sdk-for-java/index.html)

[SDK for Java 1.11.x と 2.x の相違点](https://docs.aws.amazon.com/ja_jp/sdk-for-java/v2/migration-guide/whats-different.html)

[Using the SDK for Java 1.x and 2.x Side by Side (SDK for Java 1.x and 2.xを共用する)](https://docs.aws.amazon.com/sdk-for-java/v2/migration-guide/side-by-side.html)
週間ユーザーランキングに入る方法を考察するために、上位５名の記事投稿頻度を比較してみた

#目的

Qiitaでフォロワー数を増やす1つの方法として、「書いている記事が目立つ！」のが大事だと思いますが、そのためには、「週間ユーザーランキングで10位以内に入る」「1日のトレンドに入る」を１つの目標とするのがいいのではないかと考えました。

では、１つの目安として、「週間ユーザーランキングで上位5名はどのくらい記事を書いているんだろう」という点に興味を持ち、分析してみました。

#仮説

* 1週間に1度くらいのペースで記事を投稿している方が多いのではないか

#分析方法

ユーザー毎の全ての記事の日時を取得し、記事投稿の頻度を考察しました。


```
const axios = require("axios");

async function main() {
  let response = await axios.get(
    "https://qiita.com/api/v2/items?per_page=100&query=user:halhal23"
  );
  for (let i = 0; i < response.data.length; i++) {
    console.log(response.data[i].created_at);

  }
}

main();
```

#分析結果

2020年7月14日の週間ランキングに基づき、分析しました。
※西暦は、西暦毎の記事数

| 順位 | ユーザー名 | フォロワー数 | 総記事数 | 2020 | 2019 | 2018 |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|  1位 | arowM  | 509  |  58 | 3  |  14 |  15 |
|  2位 | okazuki | 322  |  53 | 53  | 0  |  0 |
| 3位  | tomo_makes  | 324  |  27 | 5  |  6 |  5 |
| 4位  | TakahikoKawasaki    |  873 |  44 |  6 | 10  | 8  |
| 5位  | halhal23  | 19  | 1  |  1 | 0  |  0 |

* 2位のokazukiさんのみが、1週間1本以上の記事を投稿していた（投稿開始は2020年3月）
* 3名は、1-2ヶ月に1本コンスタントに記事を投稿していた（投稿開始は2014年-2016年）
* 1名は、記事数1本のみで、5位にランクインしていた。（記事内容は、高卒フリーターからエンジニアになれた話）


#考察

* フォロワー数が少なく、記事数1本でもランクインする方がいることから、記事数よりもQiitaユーザーのニーズを捉えた記事の質が重要、という当たり前の結論になった。（私はプログラミング初心者なので、記事の質は判別できないが。。）
* 5名中4名はコンスタントに記事を書いていることから、エンジニアとしての知見をたくさん持っている方々との比較になるので、なかなか難儀なことだと感じた。
* 一方で、「高卒からエンジニアになれた話」の記事のみでランクインしていることから、プロのエンジニアたちだけでなく、エンジニアになりたい層も比較的Qiitaユーザーには多いのではないか、と仮説を持った。
* つまり、私がプログラミング初心者だからこそ、初心者に寄り添える記事を書くことで、ランキングに入っていくことも可能なのではないか。

#感想

はじめて記事を書きました。

現状は、非エンジニアなので、プログラミングをすること自体に全く慣れていませんが、その分、違った視点から記事が書けるのではないかと思いました。

もっと多くのビジネスパーソンが、プログラミングに慣れ親しみ、アフターコロナ、ウィズコロナ環境下で、生産性を上げていく社会にするための記事を書けるようになっていけたらと思います。

最後までお読みいただき、ありがとうございました！
TeXstudioの導入

#はじめに
前回に引き続き、TeXstudioを入れました。
おもったより簡単だったので記事にするか悩みましたが、
一応自分用のメモとして残しておきます。

#ダウンロード
下のURLからDownload nowを押します。
https://www.texstudio.org/
インストールも早いのですぐ終わります。
セキュリティがどうとかなるので許可をしましょう。


#設定
TeXstudioを開いたら、
オプション→TeXstudioの設定に行き、
ビルドとコマンドをいじりましょう。
下のURL参照しました。
[TeXstudio/設定](https://texwiki.texjp.org/?TeXstudio%2F%E8%A8%AD%E5%AE%9A)
ビルドについては
既定のコンパイラを「LaTeX」としてビルド & 表示を「DVI->PDFチェーン」
にすればいいので簡単ですが、
コマンドのとこはぶっちゃけわからなかったです。
forward and inverse search.....？？？ヽ(;´･Д･)>???

コマンドのLaTeXってところをlatex.exeからuplatex.exeにするとかどうとか書いてあるけど、
そもそも今書いているのってplatexなのでは??とか思ったり、
-src を -synctex=1に変える....(;´･Д･)>???
よくわからなかったです。

ここ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661791/fdba9b6d-ac2f-e8ed-e122-07ba86fe2c37.png)

いろいろな記事を読んで、
LaTeXのところは
platex.exe -src -interaction=nonstopmode %.tex
とすることで落ち着きました。
DviPdfやMetapostも変更している記事があったけど、
変更しなくても動いたんでとりあえず変更しませんでした。

高度なオプションとかは変更しなかったです。
エディタのインラインチェックのチェックは念のため外しておきました。
フォントか色とかその辺はそのうち変えるかもですが大した問題ではないので割愛します。
おつおつ。(´･ω･`)ﾉ

追記
forward and inverse searchとかの説明、「TeXstudio/使い方」てとこにあったわ。
CodeSys#Pi3の上にVisualizationのWeb Severアクセス


表示用変数を作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/914df017-666e-eeb2-01cd-b3bd600c3afe.png)

適当に画面を作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/7cf5a7d5-e547-5839-a599-abfd3800a3eb.png)

Visualization Manager>WebVisuをクリクします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/528b2700-152b-3ce7-f6ee-a1617637b80b.png)

ここでStart画面・更新時間などが設定できます。
詳しくはOnline Helpで。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/dad199e8-be9d-7188-a485-8cbed8e64a1b.png)

最後にhttp://YourIP:8080/webvisu.htm　にアクセスすればOKです。
![in4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/0dfcca29-98f9-da42-444a-04c4d57ae82c.gif)
for文を使って何度もsedコマンドを実行する方法

## 概要
Linuxで、**sedコマンドを使って行内の色々な箇所に文字を挿入する**、ということをfor文を使って行う方法です。
他のコマンドにも応用できるかと思います。（もっとシンプルな方法がありそうですが...）

---
```sh:insert.sh（シェルスクリプトではなく、コマンドライン上にコピペでも実行できます）
#!/bin/bash
cat sequence.txt > sequence2.txt
for i in 15 12 9 6 3
do
echo "$(sed -E "s/(^.{$i})/\1-/g" sequence2.txt)" > sequence2.txt
done

## 3文字おきに "-" を挿入する
## sequence.txt -> ("-"挿入) -> sequence2.txt
```
もしくは

```sh
#!/bin/bash
cat sequence.txt > sequence2.txt
for i in 15 12 9 6 3
do
sed -E "s/(^.{$i})/\1-/g" sequence2.txt > temp.txt
cat temp.txt > sequence2.txt
done
rm temp.txt
```

```sh:実行結果
$ cat sequence.txt
xxxx5xxx10xxx15
xx3xx6xx9x12x15
xxxxAxxxxAxxxxA
xxAxxAxxAxxAxxA

$ ./insert.sh ; cat sequence2.txt
xxx-x5x-xx1-0xx-x15-
xx3-xx6-xx9-x12-x15-
xxx-xAx-xxx-Axx-xxA-
xxA-xxA-xxA-xxA-xxA-
```

## 解説

1. 同じファイル名を利用して<br>　`sed -E "s/(^.{$i})/\1-/g" sequence2.txt > sequence2.txt`
のようにすると普通、**sequence2.txt**の中身は空っぽになってしまいますが<br>　`echo "$(コマンド)" > sequence2.txt`<br>とすることで、**sequence2.txt**が空っぽで出力されることなく、うまく上書きすることができます。<br><br>
<font color=gray>（そもそも空のデータが出力されるのは、`> sequence2.txt`実行時、`sed`より先にまず**sequence2.txtの中身を空にする**という操作が行われているため（その結果、空のデータに対して`sed`している）。
`echo "$(コマンド)" `の中身は`> sequence2.txt`よりも先に実行されるので、これにより**sequence2.txt**がうまく上書きされます）</font><br><br><br>
2. `sed`コマンドの中でfor文の変数**`$i`**を利用できるようにするためには`""`を利用する必要があります（`''`を使うと変数ではなく、ただの文字列として認識されるため）

なお、同名ファイルへの出力に関しては他にもいろいろな方法があるようなので、興味あれば以下のリンクを参照ください。（1つ目のリンク）

>1. に関して<br>[bashでファイルを処理して結果を同じファイルに上書きする](https://orebibou.com/ja/home/201706/20170608_001/)<br>
[同じ名前のファイルにリダイレクト](https://japan.zdnet.com/article/20356533/)<br><br>
2. に関して<br>[【bash】 「\"」、「\'」、「`」の違い - どちらかというとごはん派](https://blog.goo.ne.jp/01_mai/e/a4f9f01fb647066d0c7eb37e8ae0a254)
ローカルの Box イメージを Vagrant にインポートする方法 (Vagrant Cloud が遅いときの対応)

`Vagrant Cloud` のネットワークが著しく遅く開発が進められないときがあります。
たとえば、`Packer` で `Vagrant` の `Box イメージ` を作成したものの、
`Vagrant Cloud` へのアップロードが終わらない場合などです。
GitHub でも議論が続いていて、解決に至っていません。

- [Vagrant box downloads extremely slow · Issue #5319 · hashicorp/vagrant](https://github.com/hashicorp/vagrant/issues/5319)
- [Fetching box on Atlas is painfully slow · Issue #8434 · hashicorp/vagrant](https://github.com/hashicorp/vagrant/issues/8434)
- [Use torrents to speed up vagrant box downloads from slow sources · Issue #9719 · hashicorp/vagrant](https://github.com/hashicorp/vagrant/issues/9719)
- [Box downloads from Vagrant Cloud · Issue #11354 · hashicorp/vagrant](https://github.com/hashicorp/vagrant/issues/11354)

このような場合は、一時的な回避策として、
`Vagrant Cloud` にアップロードしようとしている `Box ファイル`を、ローカルで `Vagrant` に直接追加します。

## 手順

1.
`metadata.json` というファイルを新規作成します。
`metadata.json` の例:

```json
{
    "name": "futureys/centos-8",
    "versions": [{
        "version": "202002.17.0",
        "providers": [{
        	"name": "virtualbox",
            "url": "packer_vagrant_virtualbox.box"
        }]
    }]
}
```

`metadata.json` 各項目は次のような内容で作成します:

項目|内容
---|---
name|この `Box イメージ`を `Vagrant` に登録したときの名前
version|この `Box イメージ`を `Vagrant` に登録したときのバージョン
providers name|このイメージがどの仮想マシンのためのものであるか `virtualbox`, `vmware_desktop` など
providers url|`Box ファイル`へのパス

2.
以下のコマンドを実行します。

```console
vagrant box add metadata.json
```

3.
以下のファイルを Visual Studio Code 等のテキストエディターで開きます。

`C:\Users\(ユーザー名)\.vagrand.d\boxes\(Vagrant Cloud のユーザー名)-VAGRANTSLASH-(Vagrant Cloud の box 名)\metadata_url`

4.
ファイルの中身が `metadata.json` へのファイルパスになってしまっているので、
`Vagrant Cloud` の URL に戻します。

`https://vagrantcloud.com/(Vagrant Cloud のユーザー名)/(Vagrant Cloud の box 名)`


参考: [Is there a way for Vagrant to import a local box file with a specified version? - Super User](https://superuser.com/a/1005973/1167741)
コンテナイメージをDocker Hubにプッシュする

# はじめに

コンテナイメージを別の環境でも使用したい、他の人と共有したい、と思ったときに真っ先に思いつく方法は、Docker Hubでコンテナイメージを共有することだと思います。

私がDockerのコンテナイメージをDocker Hubにプッシュしたときに行ったことを書き記しておきます。


# 環境

* macOS Catalina
* git 2.27.0
* Docker 19.03.8



# サンプルプロジェクトをGitから取得

サンプルプロジェクトはDockerの公式のものを利用させていただいております。リンクは[こちら](https://docs.docker.com/get-started/part2/)になります。

```Terminal
$ git clone https://github.com/dockersamples/node-bulletin-board
$ cd node-bulletin-board/bulletin-board-app
```


# Dockerfileの作成

Dockerfileはコンテナを定義するものです。ご自分のお好きなDockerfileを作成していただいて、任意のディレクトリに置いておきます。本記事ではDocker公式のものを利用させていただいているので、Dockerfileは以下のものをとなっております。ソースは[こちら](https://docs.docker.com/get-started/part2/#sample-dockerfile)です。

```Dockerfile
FROM node:current-slim
WORKDIR /usr/src/app
COPY package.json .
RUN npm install
EXPOSE 8080
CMD [ "npm", "start" ]
COPY . .
```


# コンテナイメージのビルド

ここでDockerfileの通りにコンテナイメージを作成します。このコマンドを打つ際には、今いるディレクトリにDockerfileがあることを確認しておきます。

```Terminal
$ docker build --tag bulletinboard:1.0 .
```

正常に終了していれば、```Successfully tagged bulletinboard:1.0```と出力されているかと思います。

```Terminal
$ docker images
```

とTerminal上で打ち、Repositoryに存在していることを確かめておくのもいいかもしれません。


# コンテナの起動

コンテナイメージを作成したら、そのコンテナを起動します。

```Terminal
$ docker run --publish 8000:8080 --detach --name bb bulletinboard:1.0
```

コンテナが起動できたら、そのコンテナ上で動いている掲示板アプリケーションにアクセスできることを確認しておきます。ブラウザで```localhost:8000```にアクセスします( http://localhost:8000 )。

アクセスできることを確認したら、このコンテナイメージはDocker Hubにプッシュしないので削除しておきましょう。

```Terminal
$ docker rm --force bb
```


# Docker Hubでプッシュする

さて、お待たせしました。やっと本題です。ちなみにDocker HubにプッシュするにはDocker Hubアカウントが必要です。もしDocker Hubアカウントがない方は[こちら](https://hub.docker.com/signup)でDocker IDを作成しておいてください。

Docker IDが作成できたら、Terminal上でDocker Hubにログインします。

```
$ docker login
```

認証画面（CLI）が出てきたら、Docker IDとご自身で設定されたパスワードを入力してログインを完了させておきます。

ここで注意点なのですが、Docker Hubにプッシュできるコンテナイメージにも条件があります。コンテナイメージの名前を```<Docker ID>/<Repository Name>:<tag>```としておかないといけない、という条件です。

Docker Hubにプッシュしたことのない人からすれば寝耳に水な訳です。私はこのパターンでした。面倒な作業をもう一回やらないといけないのかと思いましたが、[公式ページ](https://docs.docker.com/get-started/part3/)を見ていると後からでもできるようでした。

```Terminal
$ docker tag bulletinboard:1.0 <Docker ID>/bulletinboard:1.0
```

というコマンドを打ち、コンテナイメージの名前・タグを変更します。これでDocker Hubにプッシュできるようになりました。Docker Hubにプッシュします。

```
$ docker push <Docker ID>/bulletinboard:1.0
```

エラーが何もなかった場合、ご自身のDocker Hubのリポジトリにプッシュされているハズです。確認しておきましょう( https://hub.docker.com/ )。


# おわりに

Dockerは最近注目され始めている技術で、自分の分野（データサイエンス）にもDockerが浸透し始めました。正直自分はネットワークとかサーバの分野には明るくないのですが、自分のローカルを汚さなくて良い点、別の環境でも同じ分析環境を作成できる点で非常に嬉しいです。自分はパソコンを購入して再度環境構築するのが面倒だったので、いっそのこと、とDockerを導入してみました。Gitも勉強しているので、DockerとGitは親和性が高いので今後は連携させて行きたいですね。
ROS勉強#1ubuntu20.04へのros-noeticのインストール

ubuntuのバージョンを20.04にしたのでrosをインストールし直した

#感想は沼of沼だった
まず、以前使っていたros-melodicは20.04LTSでは動作しないみたい
20.04LTSではnoeticという新たなバージョンが発表されてたのでそれをインストールした

#とその前に
melodic関連の依存関係が邪魔してるっぽかったのでros関連を全消去した

```
sudo apt-get remove ros-*
sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade
```
これでまっさらになったはず(あとでわかるがなってなかった)
#いよいよnoeticさんを迎える
まずnoetic repoを加える。

```
echo "deb http://packages.ros.org/ros/ubuntu focal main" | sudo tee /etc/apt/sources.list.d/ros-focal.list
```
次になんとか鍵を登録

```
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
```
いつものやって、

```
sudo apt update
```
いよいよインストール

```
sudo apt install ros-noetic-desktop-full
```
ふつうにdesktop-fullにした。他にもいろいろあるみたい。
参考記事
https://varhowto.com/install-ros-noetic-ubuntu-20-04/

#rosdepのインストール
これも沼だったなあ

```
sudo apt-get install python3-rosdep python3-rosinstall-generator python3-vcstool build-essential
sudo rosdep init
rosdep update
```
ここで、以前に残ってたrosdepを消去しなきゃいけなかった。
参考記事
http://wiki.ros.org/noetic/Installation/Source

#rosinstallのインストール
どこにも書いてなかったわ、、

```
sudo apt-get install python3-rosinstall
```
#あとは公式wiki通り
```
$ mkdir ~/ros_catkin_ws
$ cd ~/ros_catkin_ws
```
```
$ rosinstall_generator desktop --rosdistro noetic --deps --tar > noetic-desktop.rosinstall
$ mkdir ./src
$ vcs import --input noetic-desktop.rosinstall ./src
```
```
$ rosdep install --from-paths ./src --ignore-packages-from-source --rosdistro noetic -y
```
```
$ ./src/catkin/bin/catkin_make_isolated --install -DCMAKE_BUILD_TYPE=Release
```

一番最後がものすごい時間かかった。melodicとかなり違うので不安。
自分が使った Vagrant plugin のまとめ

## vagrant-env (旧名: dotenv)

`.env` ファイルを作成することで、 `Vagrantfile` に環境変数として読み込めます。
個別の環境に依存する設定を構築処理から取り除くことができるので、ほぼ必須でしょう。
検索すると複数件の GitHub リポジトリーが引っかかりますが、
次の Vagrant 公式ページでは、このリポジトリー紹介しています。
[Available Vagrant Plugins · hashicorp/vagrant Wiki](https://github.com/hashicorp/vagrant/wiki/Available-Vagrant-Plugins)

[gosuri/vagrant-env: Vagrant plugin to load environment variables from .env into ENV](https://github.com/gosuri/vagrant-env)

## vagrant-teraterm

vagrant ssh の代わりに vagrant teraterm とコマンドを実行することで
仮想環境に Teraterm で SSH 接続できます。
Teraterm がどうこうというよりも、コマンドを実行するときに
仮想環境と実環境の間で捜査対象を間違えることを防止できたことが大きかったです。

[tiibun/vagrant-teraterm](https://github.com/tiibun/vagrant-teraterm)

## vagrant-rsync-pull

仮想環境側のディレクトリーを実環境のフォルダーに複製します。
vagrant up コマンドと同時に実行されるので注意しましょう。
vagrant rsync-pull コマンドで手動実行できます。

[edtoon/vagrant-rsync-pull: Vagrant rsync from guest to host](https://github.com/edtoon/vagrant-rsync-pull)

## vagrant-vbguest
`GuestAdditions` を自動的にインストール・更新します。

[dotless-de/vagrant-vbguest: A Vagrant plugin to keep your VirtualBox Guest Additions up to date](https://github.com/dotless-de/vagrant-vbguest)

## vagrant-share

仮想環境を HTTP, SSH などの方法で共有することができます。
ペアプログラミングなどのときに有用です。

[Vagrant Share | Vagrant by HashiCorp](https://www.vagrantup.com/docs/share/)

## vagrant-alpine

Vagrant で Alpine のイメージを起動するときに必要となります。

[maier/vagrant-alpine: Vagrant plugin for Alpine Linux Guest](https://github.com/maier/vagrant-alpine)
コピペした際に時々紛れてくる$を無視する

インターネットからコピーペーストをしたときに紛れてくる`$`を無視してコマンドを実行できるようにするパッケージを作りました。

コピーした際に$がコピーされる時とされない時がありもどかしい

### インストール

```bash
npm -g install dlll
```

※注意 yarnの場合、`warning`が出ます。詳しくはこちら
https://github.com/valerybugakov/yarn/blob/84fc1b51e1d9ce424c495e225a790c2eeaca8627/src/util/normalize-manifest/util.js

### 使用例

使用例

```bash
$ ls
zsh: command not found: $
```

```bash
npm -g install dlll
$ ls
README.md               node_modules            package.json            tsconfig.json
built                   package-lock.json       src
```

### 削除方法

```bash
npm -g uninstall dlll
```


## コード

コード自体はJavaScriptで4行で書かれています。

https://github.com/yushimatenjin/dllll

#### JavaScript
```index.ts
#!/usr/bin/env node
import { spawn } from "child_process";
if(!process.argv[2]) process.exit(0)
spawn(process.argv[2], [...process.argv.slice(3)], { stdio: "inherit" });
```

#### package.json

package.jsonの`bin`に$を指定し、パッケージとして実行できるようにします。
https://docs.npmjs.com/files/package.json#bin

```package.json
{
  ...
  "bin": {
    "$": "./built/index.js"
  }
  ...
}
```

これで`$`が入力された際に、このプログラムが実行されるようになります。
paizaラーニングを使ってみて良いなと思ったところ

paizaラーニングの良いなと思ったところについて書いていきます。

その１：開発環境を用意しなくていい
その２：取り扱っている講座が豊富で選びやすい
その３：とりあえず**無料**で始められる

ここからそれぞれの良いところを自分なりに少し掘り下げていきます。

**その１：開発環境を用意しなくていい**
　paizaラーニングはpaiza.IOという、ブラウザで動くオンライン実行環境を使って
学習ができます。
paizaラーニングを始めるにあたって、特別な知識や経験は必要ないのです。

**その２：取り扱っている講座が豊富で選びやすい**
　paizaラーニングの講座は、「目的別」、「言語別」、「技術別」の大きく3種類に分かれており、自分が学びたいと思っている分野が選択しやすいです。

**その３：とりあえず無料で始められる**
　paizaラーニングは無料で利用ができます。(paizaのユーザー登録が必須です)
もちろん有料でないと完走できない講座もありますが、完全無料で完走ができる講座もあります。
　paizaラーニングを通して、プログラミングの楽しさを感じられる人は、有料会員になって片っ端から講座を網羅するのも良いかもしれません。

**あとがき**
　paizaラーニングを使ってみて良いなと思ったところについて、書いてみました。

　今回の3点の中では、やはり**無料で始められる**のが大きい点ではないでしょうか。
パソコンとネット環境があれば、とりあえずプログラミングの勉強が始められるなんて、良い時代になったなと思います。

　最後に余談ですが、私は**[PHP入門編]**を完走しました。
https://twitter.com/2go_r2071/status/1283063220106752002

　なぜPHPを選んだかというと、業務で使っているソフトのコードの文法がPHPによく似ていたためです。
HoloLens 2 アプリケーションデプロイ時に TimeoutException が頻発し、デプロイが失敗する場合の解決策 ( DEP0600 )

# はじめに
__2020年も後半戦の7月に突入し、遂に Microsoft Store から、
個人の開発者の方でも Microsoft HoloLens 2 を購入できるようになりました__ 👏
( Twitter 上では早速購入し、予定より早く届いたと話題になっていましたね！ )

今回はそんな Microsoft HoloLens 2 にアプリをデプロイする際に、
Visual Studio 上で TimeoutException が頻発し、デプロイが出来なくなった話を共有したいと思います。もちろん解決策を見つけたので、解決策とあわせてご紹介します！

# 開発環境
- 開発用PC : Windows 10
 - Unity 2019.2.21f1
 - Visual Studio 2019 ( Version : 16.6.2 ) 

# どんなエラーが発生したのか？
Visual Studio 2019 にて、<font color="red">__USBケーブル経由でのデプロイ__</font> __[Device/デバイス]__ を選択し、 HoloLens 2 へアプリケーションをデプロイしようとした際、以下画像のようなエラーが発生し、デプロイが正常終了しないという事態が発生しました。筆者の体感ですが、前回 Windows 10 (OS) をアップデートした直後、5月末～6月頭ぐらいからエラー発生頻度が非常に高くなり、業務や趣味開発に支障をきたすようになりました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/84283ffb-597c-4822-6a79-c92b5c57f806.png" width="600"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/1daa9f25-94e0-3dc2-6027-e5a76608b1fc.png" width="600">

__【 エラー内容 】__ 
TimeoutException : This operation returned because the timeout period expired.(Exception from HRESULT: 0x800705B4) / DEP0600 : Deployment failed. Failed to deploy through new development pipline.

エラー発生直後に、以下の対応を実施してみましたが、解決したりしなかったりと、再現性が低く、デプロイ時に毎回頭を悩ませていました。

- __USBケーブルの抜き差し__
- __HoloLens 2 / PC ( Visual Studio ) 再起動__
- __使用するUSBケーブルを別のものと取り換える__ etc..

Twitter 上でも特に話題になっていないようだったので、筆者開発環境特有のエラーなのだと思い、解決策を見つけた後も特に共有することはしなかったのですが、今回社内のエンジニアも同様のエラーに困っているとの話を聞いたので、私が行った解決策をご紹介したいと思います。

# 解決策
この問題の解決策 (代替案) としては、__デバイスのデプロイ方法を [Remote Machine/リモートマシン] に切り替えること__ です。この方法に切り替えることで、ほぼ 100% ビルドに成功するようになりました！

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/9d370e34-76d5-a433-f78c-d679c8d28f1e.png" width="600">


Microsoft HoloLens 2 へのアプリケーションデプロイ方法は、以下3種類ありますが、<font color="red">__時間的なコストと手軽さを考えると、現時点で 「Remote Machine/リモートマシン」 を選択し、ビルドを実行する方法が最適__</font> だと思います。

1. Visual Studio から USBケーブル経由でのインストールする方法
2. __Visual Studio から 同一ネットワーク (WiFi) 経由でのインストールする方法__
3. Device Portal から .appx ファイルを指定してインストールする方法

# デプロイ先に Remote Machine / リモートマシン を設定する方法について
USBケーブル経由ではなく、同一ネットワーク (WiFi) 経由でデプロイを実行する方法は、Microsoft 公式ドキュメントにて丁寧に解説されているので、以下サイトをご覧いただければと思います。

- __[Wi-Fi 経由でのアプリの配置 - HoloLens 2](https://docs.microsoft.com/ja-jp/windows/mixed-reality/using-visual-studio#deploying-an-app-over-wi-fi---hololens-2?WT.mc_id=MR-MVP-5003868)__

この記事が新しく HoloLens 2 アプリケーション開発に携わられる方のお役に少しでも立てれば幸いです。LGTM もサクッと押していただけると嬉しいです。最後までお読みいただき、ありがとうございました。
Vagrant Box イメージを作成するための環境構築

## 注意

現在は `VirtualBox` や `Vagrant` を使ったワークフローよりも `Docker` を使ったワークフローの方が効率的で、
この記事で紹介しているすべてのツールが習得不要になります。
`Docker` が導入できないかどうかを最初に検討することをおすすめします。

この記事では、`Vagrant` の `Box イメージ`を作成するための環境構築の手順を解説します。

## Vagrant Box イメージの作成が必要になる経緯

`Vagrantfile` に構築手順を定義することで `VirtualBox` など仮想環境の構築自動化ができます。
一方で、仮想環境を消去してしまうと、次に使うときに再度構築処理が実行されることになり、
構築までに時間がかかってしまいます。

`Vagrantfile` だけで構築手順を自動化するのではなく、
あらかじめ `Vagrant` の `Box イメージ`を作成しておき、その `Box イメージ`を共有することで
構築の時間をなくし、生産性を上げることができます。

### Vagrantfile だけで構築手順を自動化した場合の問題点

- 環境をリセットしたいとき、構築処理の再実行が必要となり、待ち時間が必要になります
- チームで利用する場合、チームの人数分構築処理が実行され、チーム全体で消費される時間が大きくなります

上記の問題点を解消するために、事前に `Box イメージ` を作成しておき、
その `Box イメージ` を共有します。

## 環境構築の手順

### 1. 物理 PC を準備します

VirtualBox は仮想マシンにはインストールできないためです。

### 2. Chocolatey をインストールします。
[Chocolatey の使い方 | ultra code](https://futureys.tokyo/how-to-use-chocolatey/)

### 3. VirtualBox, Vagrant, Packer をインストールします。

ここでインストールするツールの用途は次の通りです:

ツール|用途
---|---
`VirtualBox`|仮想環境を動作させるため
`Vagrant`|`VirtualBox` をコマンドやプログラムで制御するため
`Packer`|`Vagrant` を利用したイメージ作成手順を定義し、コマンドひとつでイメージを作成するため

```console
choco install -y virtualbox vagrant
```

2020-07-15 現在、次の手順でインストールする `packer-builder-vagrant` が
`Packer` バージョン `1.5.0` 以降に対応していないため、`Packer` はバージョン `1.4.5` をインストールします:

```console
choco install -y packer --version=1.4.5
```

### 4. packer-builder-vagrant をインストールします。

初期状態では `Packer` は `Vagrant` の `Boxイメージ` をベースイメージにしてイメージを作成することができません。
このプラグインは、`Packer` でイメージを作成するときのベースイメージとして
`Vagrant` の `Boxイメージ` を指定することを可能にします。

ちなみに、`Packer` は `VirtualBox` の `iso` や `ovf` をベースイメージにすることはできます。
つまり、`Vagrant` のイメージのビルドを複数段階に分けなくてもよければ、
このプラグインがなくてもイメージの作成を行うことはできます。

#### 4-1.

個人用フォルダーの packer.d フォルダーに
“plugins“ というフォルダーを新規作成します。

#### 4-2.

次の GitHub リポジトリーを開きます:
[themalkolm/packer-builder-vagrant: 📦 Packer builder to use existing vagrant boxes](https://github.com/themalkolm/packer-builder-vagrant)

#### 4-3.

Release タブから次の名前の exe ファイルをダウンロードします:
`packer-(version)_packer-builder-vagrant_windows_amd64.exe`

#### 4-4.

ダウンロードしたファイルを手順 4-1. で新規作成した plugins フォルダーに移動し、
ファイル名を次の名前に変更します:
`packer-builder-vagrant.exe`

以上で、
`Packer` のコマンドを実行するだけで `Vagrant` の `Boxイメージ` を作成することができる環境ができあがりました。
JSONからURLのクエリーを生成する

# 動機
初めてPythonのRequestモジュールを触った。**JSONからクエリーを作成できる**んすか？！便利じゃん！素晴らしい!→Shortcutsで再現した:relaxed:

# フロー全体
今回はAppleMusicのURLからidを取り出して、[iTunesSearchAPI](https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/)にリクエストするためのURLを生成してます。![079E3100-6A2E-40CC-9328-D34267E2BF38.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561438/42b96181-0db6-e9e0-57a9-d50df4d789ac.jpeg)

## 入力されたURLからクエリ文字列を取得
![44E3B10A-2C8A-40DD-865B-45EF3ECECAD1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561438/208cb24e-90f5-0e22-1b75-02125ed13f9e.jpeg)
`[URLのコンポーネントを取得]`でクエリーを取得するよう指定します。
`i=40284574`が出力されます。

## クエリー用の辞書に値を入れる
![7AAC9F70-FF78-4B4E-A352-DC3990537D20.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561438/9be0f48b-beb6-6072-a5da-34336ce9f6a4.jpeg)出力するURLのクエリーを
`country=jp&lang=ja_jp&id=40284574`にしたいので、それに合った値を入力しておきます。
この変数*i*は、上の`[URLのコンポーネントを取得]`から出力されたマジック変数です。以下のGIFのように、
1.マジック変数をタップ
2.型を`辞書`にする
3.キー`i`を指定
この動作で、**`i=40284574`を`{"i":"40284574"}`に変換し、キー`i`の値として扱うことができます。**(めちゃ便利)
ちなみに、このテクを使わずに同じことをしようとすると2アクション分多くなります(下画像参照)。面倒くさいので上のテクニックを使うことを強ーくおすすめします。
![1AD0CB83-761E-4F37-84E2-2EFB9D776219.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561438/4e685a17-ae5a-7a16-e2cd-8056b5ddd0d4.jpeg)

## 辞書のキーごとに値を取得してペアにする
![92DC4083-9E66-4EEB-8D68-A1F4930D0378.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561438/7f405e9c-65b1-6b71-c81b-56b327476ee8.jpeg)
`[辞書]`のキー(`country`,`lang`,`id`)を`[各項目を繰り返す]`に入れて対応した値とペアにします。
さっきのテクニックを使って、辞書のマジック変数をキーのリスト(配列)として扱います。
![7D92D38F-9251-4B78-8026-FD98D199981B.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561438/40776e22-3086-bff5-e5b6-1abef73cee05.gif)
`[各項目を繰り返す]`内でキーと値を'='で繋いでリストを出力します。
このリストを`[テキストを結合]`で'&'を使って結合し、URLのクエリー部分に突っ込んでおしまいです。

[このショートカットのリンク](https://www.icloud.com/shortcuts/5a446085001242209515730bc8cda57e)