OmniAICreator/Qiita-1.07M huggingface dataset:
Rails　お気に入りランキング実装

この記事は学習中に実装した内容をアウトプットし、更に理解を深めるための記録用投稿になります。
投稿している内容と同じ箇所で躓いている方に少しでもお力添えできれば幸いです。

## 実装した内容
今回はユーザーが投稿した内容に複数のユーザーがお気に入り（いいね）をし、そのいいね数の多い順番にランキング形式で表示する内容になります。

## 前提条件
・投稿した記事のテーブル名はdishesとします。
・いいね機能のテーブル名はfavoritsとします。
・いいね機能を実装済み

## モデル
コントローラで使用するためのメソッドをモデルに記述します。
コントローラの記述を長くしないようにするためのようです。モデルの使い方を再確認！

＊/app/models/dish.rb
```ruby
def self.create_all_ranks
  Dish.find(Favorit.group(:dish_id).where(created_at: Time.current.all_week).order('count(dish_id) desc').limit(10).pluck(:dish_id))
end
```

[self.create_all_ranks]
self・・・クラスメソッド。modelクラス（今回ならばDishモデル）のレコードを検索するなどの用途や作成されているメソッド。

[Favorit.group(:dish_id)]
Favorit(いいね)がついているdish_idを呼び出す。

[where(created_at: Time.current.all_week)]
この記述で直近一週間の投稿を選びます。
rails cでTime.currentと入力すると現在の時間が表示されます。そこにall_weekと付け加えることで直近位週間を表示できます。これはとても勉強になりました！
```
2.6.3 :001 > Time.current
 => Sun, 17 Apr 2022 03:14:39 UTC +00:00 
```

[order('count(dish_id) desc')]
orderメソッドでいいねがついているdish_idをいいねの多い順に並び替えます。

[limit(10)]
上位１０までを指定。

[pluck(:dish_id)]
dish_idを数字で表すための記述

## コントローラ
先ほどのモデルでの記述を呼び出します。
```ruby
def ranking
  @all_ranks = Dish.create_all_ranks
end
```
## ビュー
```erb
<h3>お気に入りランキング</h3>
<% @all_ranks.each.with_index(1) do |dish,i| %>
第<%= i %>位
<%= dish.dish_name %>
投稿者
<%= dish.user.name %>
❤いいね数️<%= dish.favorits.count %>
<% end %>
```
[with_index(1)]
このように記述することで上位１から表示することができます。２位以降から表示したい時は[with_index(2)]と」記述します。
## 振り返り
今まで見たことのないメソッドがかなり多く出てきました。
これだけ使いこなせるようになるには時間がかかりそう、、、
でも一個一個調べてrails cで試していけば理解が深まりそうな気がする！

・self：modelクラス（今回ならばDishモデル）のレコードを検索するなどの用途や作成されているメソッド。

・group：指定したカラムのレコードの種類ごとにデータをまとめるメソッド。
参考記事
https://pikawaka.com/rails/group

・order：id順にデータを取得できる。
参考記事
https://pikawaka.com/rails/order

・limit：所得件数の上限を指定
・pluck：指定したカラムを数字で表すためのメソッド。

-----
以上になります。

説明の不足している点などあるかもしれないのでご指摘いただけると幸いです。
よろしくお願いします。
Flutterの`flutter format`コマンドのオプションTips


例えば、以下のように余分なスペースが入ってしまっているコードがあるとします。

```dart:lib/hello.dart
Text( 'Hello world')
```

flutterでは`flutter format` と言うformatを自動で整形してくれるコマンドが用意されているのでこれを使います。

```bash
$ flutter format lib
Formatted lib/hello.dart
Formatted 1 files (1 changed) in 0.45 seconds.
```

以下、`flutter format`を扱う際に便利なオプションについて記述します。

## formatエラーの際にExit code 1を返したい

CIでformat違反が発生した場合にCI全体をfailさせたい場合など、exit code 1を 返却したいケースがあります。
その場合`--set-exit-if-changed`オプションを追加することで設定可能です。

```bash
$ flutter format lib --set-exit-if-changed 
Formatted lib/hello.dart
Formatted 1 files (1 changed) in 0.45 seconds.
Formatting failed: 1
```

## コードの更新まで行わずにformat違反かだけチェックしたい

コードの自動整形は行わずに違反が存在するかのチェックだけで充分なケースがあります。
その場合`--dry-run`オプションを追加することで設定可能です。

```bash
$ flutter format lib --set-exit-if-changed --dry-run
Formatted lib/hello.dart
Formatted 1 files (1 changed) in 0.45 seconds.
Formatting failed: 1
```

# 環境

* Flutter 2.10.4
* Dart SDK version: 2.16.2
Cloud StorageのログをBigQueryに自動で転送する

Cloud StorageのアクセスログをCloud Storageに出力する機能がある。
https://cloud.google.com/storage/docs/access-logs?hl=ja

このドキュメントでもBigQueryにログを突っ込む手順を解説しているが、ローカルにダウンロードしてローカルで加工してという方法でやっているが、BigQueryの機能を駆使すればそんな手間は不要である。あとBigQueryに突っ込むのにパーティショニングする手順が抜けているのもよくない。

というか、ドキュメントではCloud Storage上にログを吐くより、Cloud Audit Logsを推している。が、この設定をするとAnonymousのアクセスができなくなるという制限（不具合?）がある。
https://cloud.google.com/storage/docs/troubleshooting#trouble-download-storage-cloud

そういう制限がなくても、BigQueryのSQLは割とリッチなので、BigQueryに突っ込んでおくと何かと捗る。
BigQueryにCloud Storage上のCSVを転送する機能があるので、それを活用する方法を説明する。

# Cloud Storage上にログを出力するようにする
手順はこちらを参照
https://cloud.google.com/storage/docs/access-logs?hl=ja#delivery
これで指定されたバケットにアクセスログが出力される。

# BigQueryに転送する
同じページにBigQueryに流し込む手順があるが、スキーマの設定ファイル(`cloud_storage_usage_schema_v0.json`)だけ借用する。
https://cloud.google.com/storage/docs/access-logs?hl=ja#BigQuery

BigQueryにはパーティショニングという透過的にシャーディングしてくれる機能がある。クエリの範囲を絞ってコストを抑えることができるが、これをやらないとデータ量が多くなると大変なことになる。

作成時刻でパーティションするのがよさそうだが、生成されるログのcsvの`time_micros`がintegerなので"時間単位列パーティショニング"はできない。なので取込み時間でパーティションするように、下記のコマンドでテーブルを作成した。

```
bq mk \
   --time_partitioning_type DAY \
   --table storageanalysis.usage \
   <ダウンロードDir>/cloud_storage_usage_schema_v0.json
```

後はこちらの手順でStorageからBigQueryに転送設定すればOK
https://cloud.google.com/bigquery-transfer/docs/cloud-storage-transfer?hl=ja

# BigQueryで解析する
データが入ったらSQLを投げればよいが、URLパスをSQLでパースするのは辛い。BigQueryにはUDFをJSで書ける機能があるので、それを使えば楽にできる。

例えば、下記のSQLを投げると
```
CREATE TEMPORARY FUNCTION parsePath(path STRING)
    RETURNS STRUCT<a STRING, b STRING, c STRING>
    LANGUAGE js AS r"""
        const [a, b, c] = path?.split("/") ?? [];
        return {a, b, c};
""";

SELECT parsePath(cs_object).* FROM `storageanalysis.usage` WHERE DATE(_PARTITIONTIME) = "2022-04-18";
```
これでkeyの第1から第3階層までをそれぞれa,b,cというフィールドに割り当てることができる。
BigQueryはレコードや配列も扱うことができる。
先の例では、`parsePath`がa,b,cというフィールドを持つレコードを返し、`.*`でそのフィールドを展開している。
Nimの構造体のフィールド名と型を取得する

# 動的に構造体のフィールド名と型を取得

- 構造体をもとにDBテーブルを作るときなどに利用できる。
- ポイントは、型情報を取得する「typetraits」とオブジェクトのフィールド名を取得する「fieldPairs」。


```nim:main.nim

# typetraitsをimportして、型の情報を取得する機能を読み込む
import typetraits

# 型定義
type SampleSet = object
  name: string
  age: int

# オブジェクトのフィールド名と型を表示
for field, value in SampleSet().fieldPairs:
  echo field, ":", value.type.name


```

以下、表示結果。

```sh

# nimbleでプロジェクトを作っている前提で、実行
nimble run main.nim

name:string
age:int
```
JetBrains MonoをVisual Studio Codeで使用する

# 概要

JetBrainsがオープンソースとして配布しているフォント[JetBrainsMono]((https://www.jetbrains.com/ja-jp/lp/mono/))をVisualStudioCode(VSCode)で利用する方法です。

## 前提条件

OS: macOS Monterey 12.3.1
VSCode: 1.66.2
JetBrainsMono: 2.242

---

## インストールする

[公式サイト](https://www.jetbrains.com/ja-jp/lp/mono/)からフォントをダウンロードをクリックします。

解凍すると直下に`fonts`というフォルダーがあります。

好きなフォントを選べば良いのですが、今回は基本のJetBrainsMono-Regular.ttfをインストールします。
![スクリーンショット 2022-04-17 13.15.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627146/8434162c-700d-9bbb-abbb-0191b568c63f.png)

以上でインストールは完了です。

## VSCodeに設定する

VSCodeのSettingから設定します。

VSCodeでは、設定されている順番=優先順位なので、先頭に`JetBrains Mono`を入れます。

上記で`Regular`以外をインストールしても、同じ`JetBrains Mono`で良いようです。

編集前(デフォルト値): `Menlo, Monaco, 'Courier New', monospace`
変更後: `JetBrains Mono,Menlo, Monaco, 'Courier New', monospace`

![スクリーンショット 2022-04-17 13.16.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627146/c10fe71c-eca9-3c4f-f03c-65d6aa2b4715.png)

----

## 結果

(私にとって)圧倒的に見やすくなりました！

![スクリーンショット 2022-04-17 13.19.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627146/b0693295-d134-2c8b-fec9-e6fdd7376875.png)

# 背景

普段開発ではPythonやTypescriptを使用することが多く、全て会社配布のPycharmで書いていました。

最近、Goを書く機会があり、GoはPycharmで非対応！だったので、ひさしぶりにVSCodeを開くことになりました。

Pycharmに慣れていた私は、VSCodeの見た目やショートカットに慣れず、今回はフォントをJetBrains風にカスタマイズしてみました。
TypeScriptで定義できる型まとめ

# 概要
TypeScriptで定義できる型をまとめる

https://www.udemy.com/course/typescript-complete/


## tscコマンド
tscコマンドでtsファイルをjsファイルへ変換する
```ts
tsc index.ts
```

## ES5へのコンパイラ
ほぼすべてのブラウザが対応しているES5へコンパイルすることができる
例：ES6への変更

```ts
tsc index.ts --target es6
```

## 型注釈と型推論
基本的には型推論が推奨。初期化する際は型注釈を使用するのがおすすめ。
```ts
// 型注釈
let hasValue1: boolean = true;
let count: number = 10;
let float:  number = 3.14;
let negative: number = -12;
let single: string = "hello";

// 型推論
let hasValue2: boolean;
```

## オブジェクト
このように定義できるが、基本的には型推論に任せ省略する。
```ts
const person: {
    name: string;
    age: number;
} = {
    name: 'Jack',
    age: 21
}
```

## 配列
型推論にすれば文字列だけではなく数字も入るようななんでも入る配列を作成することができる。
```ts
const fruits: string[] = ['apple', 'banana'];
```

## Tuple型
配列の中身の型を型注釈で定義する事ができる
```ts
const book: [string, number, boolean] = ['business', 2, true];
```

## Enum
```ts
enum CoffeeSize {
    SHORT = 'SHORT',
    TALL = 'TALL',
    GRANDE = 'GRANDE',
    VENTI = 'VENTI'
}

const coffee = {
    hot: true,
    size: CoffeeSize.SHORT
}
```

## any型
なんでもありの型
```ts
let anything: any = true;
anything = "aaaa";
anything = 2;

// string型で定義した変数にany型を代入してもエラーは出ない
let banana: string;
banana = anything;
```

## union型
複数の方を同時に定義
```ts
let union: number | string = 10;
```

## Literal型
特定の値のみしか受け付けない型
```ts
const apple: true = true;

// だめ
const apple: true = 'hello';


let clothSize: 'small' | 'medium' | 'large' = 'large';
// OK
clothSize = 'small';
// NG
clothSize = 8888;
```

## typeエイリアス
型を変数に定義することができる
```ts
type ClothSize = 'small' | 'medium' | 'large';
let clothSize: ClothSize = 'large';
```

## 関数へ型を適用
```ts
function add(num1: number, num2: number): number {
    return num1 + num2;
}

add(2, 8);
```

## void型とundefined型
返り値がない関数にはvoid型を定義することができる。
console.logで出力した場合undefinedが表示されるため、返り値にundefinedを指定することもできるが、その際は必ずretuenが必要。
しかし、ほとんどの場合はundefined型を返り値として定義することはないため、void型を使っておけば大丈夫。
```ts
function sayHello(): void {
    console.log('Hello');
}
```

## 関数型
変数に代入する関数へ型を指定したい場合
コロンが重複してしまうため、アローを使って返り値を定義する
```ts
const doubleNumber: (num: number) => number = number => number * 2;
```

## callback関数へ型をつける
```ts
function doubleAndHandle(num: number, cb: (num: number) => number): void {
    const doubleNum = cb(num * 2);
}

doubleAndHandle(21, doubleNum => {
    return doubleNum;
});
```

## unknown型
any型とは違いunknown型はどんな型でも一度は代入することができるが、違う型を更に代入することはできない
```ts
// unknown型
let unknownInput: unknown;
let text: string;
text = 'gggg';
// NG
text = unknownInput;
```

## error型
void型はundefinedを返すが、never型は何も返さない場合に使用する。
以下のようにerrorの場合や、whileで終わらないループ処理を実行する際にも使用されるらしい。
```ts
// never型
function error(message: string) {
    throw new Error(message);
}
```
strlcat自作

man strlcat!!!!
```
SYNOPSIS
     #include <string.h>
     size_t
     strlcat(char * restrict dst, const char * restrict src, size_t dstsize);
DESCRIPTION
 strlcat() appends string src to the end of dst.  It will append at most dstsize - strlen(dst) - 1
     characters.  It will then NUL-terminate, unless dstsize is 0 or the original dst string was longer
     than dstsize (in practice this should not happen as it means that either dstsize is incorrect or
     that dst is not a proper string).
If the src and dst strings overlap, the behavior is undefined.
```
オーバーラップした時は未定義か。。(·ε·｀)ｽﾈﾁｬｳ



### 検証
strlcatの挙動を見てみよう
ここでは
コピー先の文字列（１つ目の引数）ことをdest
コピー元の文字列（２つ目の引数）ことをstr
と呼びますね！

##### xがdest文字数以下の時
```c
#include <stdio.h>
#include <string.h>

int main()
{
    char dest[20] = "ABCDE";
    char src[] = "1234";
    int x = 2;
   
    unsigned int result1 = strlcat(dest, src, x);
    printf("%s: %u\n", dest, result1);
    return(0);
}
```
```: 結果
ABCDE: 6
```
xがdest文字数以下の時(x=2)
destの文字列は変わらない
返り値は６
６はx＋str文字数
##### xが(dast文字数＋str文字数)以下の時
```c
#include <stdio.h>
#include <string.h>

int main()
{
    char dest[20] = "ABCDE";
    char src[] = "1234";
    int x = 7;
   
    unsigned int result1 = strlcat(dest, src, x);
    printf("%s: %u\n", dest, result1);
    return(0);
}
```
```: 結果
ABCDE1: 9
```
xが(dest文字数＋src文字数)以下の時
destの文字列の後ろにsrcが(x-dest文字数-1)文字ペーストされてる？？
返り値は９（destの文字数＋srcの文字数）ｹﾞｯ!(꒪ꇴ꒪|||)⚡

##### xがdestの文字数+srcの文字数より大きい時の時
```c
#include <stdio.h>
#include <string.h>

int main()
{
    char dest[20] = "ABCDE";
    char src[] = "1234";
    int x = 13;
   
    unsigned int result1 = strlcat(dest, src, x);
    printf("%s: %u\n", dest, result1);
    return(0);
}
```
```: 結果
ABCDE1234: 9
```
xがコピー先の文字数以上コピー先の文字数＋コピー元の文字数よりおおきい時
destの文字列の後ろにsrcの文字列がペーストされてる
返り値は９（destの文字数＋srcの文字数）


### わかったこと
strlcatはdestとsrcを結合させる
destの後ろにsrcを最大（x-dest文字数-1)文字ペーストしてくれる
返り値は、dest文字数がx以上の時は（x＋src文字数）
xよりdestの文字数が小さい時は時は（dest文字数＋src文字数）
![BF5F110C-62D2-4E2F-B1CB-6F224F80406D.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2123815/fdfe2831-1825-38ef-b59f-6c80b25a02d2.jpeg)




```c

unsigned int	len_str(char *str)
{
	unsigned int	count;

	count = 0;
	while (str[count])
		count++;
	return (count);
}//文字数数える

unsigned int	ft_strlcat(char *dest, char *src, unsigned int size)
{
	unsigned int	k;
	unsigned int	max_copy;
	unsigned int	d_nb;
	unsigned int	s_nb;

	d_nb = len_str(dest);
	s_nb = len_str(src);
	if (size <= d_nb)
		return (size + s_nb);
	max_copy = size - d_nb - 1;
	k = 0;
	while (k < max_copy)
	{
		if (src[k] == '\0')
			break ;
		dest[d_nb + k] = src[k];
		k++;
	}
	dest[k + d_nb] = '\0';
	return (s_nb + d_nb);
}
```
大城希乃樺ちゃん疲れたでござる
Docker Desktop (WSL2) ファイルの保存先変更

Docker Desktopで作成される仮想ハードディスクファイルの保存先を変更する。

| ソフトウェア   | バージョン      | 
| ---------------| --------------- |
| OS             | Windows 10 Home | 
| Docker Desktop | 4.7.0 (77141)   |

変更内容

| 名前                | 変更前                                     | 変更後                         |
| :------------------ | :----------------------------------------- | :----------------------------- |
| docker-desktop      | %LocalAppData%\Docker\wsl\distro\ext4.vhdx | D:\WSL\Docker\distro\ext4.vhdx |
| docker-desktop-data | %LocalAppData%\Docker\wsl\data\ext4.vhdx   | D:\WSL\Docker\data\ext4.vhdx   |

## 1. Docker Desktop を終了する

タスクバーの「Docker Desktop」アイコンを右クリックし、「Quit Docker Desktop」をクリックする。

PowerShell を管理者権限として実行する。

Linux ディストリビューションの一覧を表示する。

```PowerShell
wsl --list --verbose
```

実行結果

```Text
  NAME                   STATE           VERSION
* Ubuntu                 Running         2
  docker-desktop-data    Stopped         2
  docker-desktop         Stopped         2
```

「STATE」が「Stopped」となっていることを確認する。

## 2. tarファイルにエクスポート

変更後のディレクトリを作成する。

```PowerShell
mkdir D:\WSL\Docker\data
mkdir D:\WSL\Docker\distro
```

カレントディレクトリを変更する。（tarファイル保存先）

```PowerShell
cd D:\WSL\Docker
```

tarファイルにエクスポートする。

```PowerShell
wsl --export docker-desktop docker-desktop.tar
wsl --export docker-desktop-data docker-desktop-data.tar
```

## 3. Linux ディストリビューションの登録解除

Linux ディストリビューションの登録解除をする。

```PowerShell
wsl --unregister docker-desktop
wsl --unregister docker-desktop-data
```

## 4. tarファイルをインポート

tarファイルをインポートする。

```PowerShell
wsl --import docker-desktop D:\WSL\Docker\distro docker-desktop.tar --version 2
wsl --import docker-desktop-data D:\WSL\Docker\data docker-desktop-data.tar --version 2
```

Linux ディストリビューションの一覧を表示する。

```PowerShell
wsl --list --verbose
```

```text
  NAME                   STATE           VERSION
* Ubuntu                 Stopped         2
  docker-desktop         Stopped         2
  docker-desktop-data    Stopped         2
```

## 5. Docker Desktop を起動する

デスクトップの「Docker Desktop」ダブルクリックし、正常に起動することを確認する。
PowerShellのSelectで角括弧[]を含む列の取り出し

PowerShell の Select-Object では角括弧[]はワイルドカードとして扱われるため、
列名に角括弧[]を含む列をselectしたい場合は、以下のいずれかの方法を使います。
※以下では列名「打率[%]」を取り出す前提です。

1. 開き角括弧[をバッククウォート2個でエスケープ
``` select 打率``[%] ```
1. 列名をシングルクウォート’’で括り、開き角括弧[をバッククウォート1個でエスケープ
`` select '打率`[%]' ``
1. 開き角括弧[をワイルドカード[]の対象に指定
``` select 打率[[]%] ```


-----
## 検証環境
~~~powershell
PS C:> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.19041.1645
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.1645
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
~~~

## 元のデータ
打率.csv
|選手|打数|安打|打率[%]|
|:-|-|-|-:|
|aaa|543|353|0.650|
|bbb|654|311|0.476|
|ccc|454|254|0.559|
|ddd|586|423|0.722|

```powershell
PS C:> $data = Import-Csv .\打率.csv
PS C:\> $data

選手 打数 安打 打率[%]
---- ---- ---- -------
aaa  543  353  0.650  
bbb  654  311  0.476  
ccc  454  254  0.559  
ddd  586  423  0.722  
```

## Selectで取り出す
* 列名をそのまま指定しても取り出せません。
~~~powershell
PS C:> $data | select 打率[%]    

打率[%]
-------





PS C:\> $data | select "打率[%]"
打率[%]
-------






~~~

* 開き角括弧[をバッククウォート2個でエスケープ
``` select 打率``[%] ```
ダブルクウォートで括っても同じ結果になりました。
``` select ”打率``[%]” ```

~~~powershell
PS C:\> $data | select 打率``[%]

打率[%]
-------
0.650
0.476
0.559
0.722


PS C:\> $data | select "打率``[%]"
打率[%]
-------
0.650
0.476
0.559
0.722
~~~

* 列名をシングルクウォート’’で括り、開き角括弧[をバッククウォート1個でエスケープ
`` select '打率`[%]' ``

~~~powershell
PS C:\> $data | select '打率`[%]'
打率[%]
-------
0.650
0.476
0.559
0.722

~~~


* 開き角括弧[をワイルドカード[]の対象に指定
``` select 打率[[]%] ```
ワイルドカード[xyz]は、「xyz」のいずれかの1文字にマッチします。
この対象に「[」を指定することで「[」自体がマッチします。

~~~powershell
PS C:\> $data | select 打率[[]%]

打率[%]
-------
0.650
0.476
0.559
0.722


PS C:\> $data | select "打率[[]%]"

打率[%]
-------
0.650
0.476
0.559
0.722

~~~


## プロパティとして列を取り出す場合
シングルクォートまたはダブルククォートで囲む必要があります。

~~~powershell
PS C:\> $data.打率[%]
発生場所 行:1 文字:10
+ $data.打率[%]
+          ~
配列インデックス式が存在しないか、または無効です。
発生場所 行:1 文字:11
+ $data.打率[%]
+           ~
'%' 演算子に続けて値の式を指定する必要があります。
発生場所 行:1 文字:11
+ $data.打率[%]
+           ~
式またはステートメントのトークン ']' を使用できません。
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException    
    + FullyQualifiedErrorId : MissingArrayIndexExpression
 
PS C:\> $data.'打率[%]'
0.650
0.476
0.559
0.722
PS C:\> $data."打率[%]"
0.650
0.476
0.559
0.722

~~~

-----
## 参考記事
- [PowerShellワイルドカードと正規表現とエスケープ](https://qiita.com/ryemug1/items/560b65a58a925ec3aeef)
【Rust】複数要素を持つデータの比較について【then_with()メソッド】 

# 概略
Rustで複数要素を持つデータの大きさを比較する際に便利なthen_with()メソッドの説明をします。  
本記事の内容は、https://doc.rust-lang.org/std/cmp/enum.Ordering.html#method.then_with に説明を少し加えたものです。

# 単体のデータの比較
まず単体のデータで比較するときは下記のようにします。  
比較の結果として、Less, Greater, Equalのいずれかが返されます。  
```rs
fn main() {
    // 比較時にはLess, Greater, Equalのいずれかが返される
    let x = 1;
    let y = 2;
    let z = 1;
    println!("{:?}", x.cmp(&y)); // Less
    println!("{:?}", y.cmp(&x)); // Greater
    println!("{:?}", x.cmp(&z)); // Equal
```

# 複数要素を持つデータの比較
次に複数要素を持つデータの比較します。
その際に使うのがthen_with()メソッドです。

## then_with()メソッドの定義
- then_with()の直前がOrdering::Equalでない場合は、then_with()の直前のものを返す。
- then_with()の直前がOrdering::Equalである場合は、then_with()の中の処理を返す。

下記は使用例です。
```rs
use std::cmp::Ordering;

fn main() {
    // 下記はthen_with()の直前が、Ordering::Equalなので、then_with()の中のOrdering::Lessを返す
    let result = Ordering::Equal.then_with(|| Ordering::Less);
    println!("{:?}", result); // Less

    // 下記はthen_withの直前が、Ordering::Equalではない(Ordering::Less)なので、then_with()の直前のOrdering::Lessを返す
    let result = Ordering::Less.then_with(|| Ordering::Equal);
    println!("{:?}", result); // Less

```

## then_with()メソッドを使った複数要素を持つデータの比較
実際に、then_with()メソッドを使って複数要素を持つデータの比較をしている例を下記に示します。
```rs
use std::cmp::Ordering;

fn main() {
    // 下記のタプルの大小比較を考える
    let x = (1, 2, 7);
    let y = (1, 5, 3);

    // 0個目の要素を比較する場合: 
    // x.0 = 1, y.0 = 1 => x.0 = y.1なのでOrdering::Equalを返す
    let result = x.0.cmp(&y.0);
    println!("{:?}", result); // Equal

    // 1個目の要素を比較する場合: 
    // x.1 = 2, y.1 = 5 => x.1 < y.1なのでOrdering::Lessを返す
    let result = x.0.cmp(&y.0);
    println!("{:?}", result); // Less

    // タプル全体の比較をする。
    // 0個目の要素から比較を開始し、もし0個目の要素同士が等しければ1個目の要素を比較し、1個目の要素も等しければまた次の要素を比較していく
    // 今回は0個目の要素が等しく、1個目の要素が比較対象より小さいので、Lessを返す
    let result = x.0.cmp(&y.0).then_with(|| x.1.cmp(&y.1)).then_with(|| x.2.cmp(&y.2));
    println!("{:?}", result); // Less

}
```

# 余談
then_with()メソッドについて知ったのは、C++のpriotiry_queueにあたるstd::collections::binary_heapで、最大値ではなく最小値を根にする為の実装方法を調べているときでした。  
下記のページでthen_with()メソッドを使って、それを実現しているので、よかったらご参考下さい。
https://doc.rust-lang.org/std/collections/binary_heap/index.html
今日からWEB開発　１日目

はじめまして。エンジニアを目指しているPIROSHKIです。

今日からWEB開発ということで、勉強しながら１つWEBアプリの開発を目指していきます。
超初心者なので長い道のりですが、皆さんの記事を見ながら頑張ります。

少し前に学校で学んだRubu　Railsを使って開発をしようと思っています。

早速RubyとRailsのインストールから

①Rubyのインストール
 https://www.ruby-lang.org/ja/downloads/
 色々なバージョンがあってよくわからないので最新の3.1.1-1をダウンロード

②Rlaisのインストール
　 コマンドプロンプトで
 　 gem install rails　
　 を実行。

③作成するアプリ名のフォルダを作成
　rails new アプリ名


　・・・・
　とここで、

　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607524/8c4cfd31-bb9e-e5eb-3143-dba7ada7e94f.png)

　途中でファイル作成がストップ。。。
　調べたところ、どうやらgitコマンドとやらが使えない状態のために起きる現象のようです。
　使えるようにする方法がすぐにわからなかったので、スキップできるという
 -Gコマンドを使うことに

　rails new アプリ名　-G

　これで無事実行できました。。。一旦休憩にしよう。。。
ISOイメージのISOとは？

## 勉強前イメージ

拡張子？

## 調査

### そもそもISOイメージとは？

CD,DVDなど、ISO 9660形式の光ディスクを一つにまとめたファイルがisoファイルになります。
ISOファイルは光ディスクに書き込むかマウントするかで使用することができます。
OSのイメージが強いですが、ファイルは機種やOSを問わず光ディスクを一つにまとめたファイルという意味になります。
企業などが独自に拡張したものもいくつか存在します。

ISO 9660形式とはCD-ROMにファイルやディレクトリを記録する
ファイルシステムの標準規格の一つで、ISOが策定したものになります。

以下がwebで公開されているisoイメージの例で
こちらはCentOS7のイメージになります。

![1Index of _pub_linux_centos_7.9.2009_isos_x86_64 -.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/27ed9702-0fe7-e485-017d-5d3ea90f568e.png)


### ISO とは

ISOはスイスに本部があるの民間機関で
International Organization for Standardizationの略としてISOと呼ばれます。
日本語では国際標準化機構になります。
国際間の取引をスムーズにするために共通の基準を決めるということで、ISO規格を定めている団体です。

そこで国際的に定められた形式がISOイメージです。

## 勉強後イメージ

OSのファイルかと思ったけど、違ったw

## 参考

- [ISO 9660 【High Sierra】](https://e-words.jp/w/ISO_9660.html)
- [ISOファイルとは何か](https://pctrouble.net/knowledge/file_iso.html)
[JavaScript] 配列の中の配列の合計値を取得したい。



## 結論
flatMapを使用する。

```
// カテゴリー配列の中にitemsという配列オブジェクトがある。
const category = [
{id: 1,items: [{ id: 1, price: 500 },{ id: 2, price: 500 },],},
{id: 2,items: [{ id: 1, price: 1000 },{ id: 2, price: 300 },],},
]
```

```
// flatMapを使用して、itemsのみの配列を取得する
const items = category.flatMap((c) => c.items)

// 結果 (配列が1次元が下がる)
[ { id: 1, price: 500 },
  { id: 2, price: 500 },
  { id: 1, price: 1000 },
  { id: 2, price: 300 } ]

// reduceで合計値を算出する
const total = items.reduce((sum, item) => sum + item.price, 0)

console.log(total) // 2300

// 1度でやるならそのままつなげる。
const total = category
  .flatMap((c) => c.items)
  .reduce((sum, item) => sum + item.price, 0)
// 2300
```


## 概要
友達から、配列の中の配列の合計値を取得したいと言われて、「そんなん簡単やろ。手本見せたるわっ」と意気揚々と実演したが、詰まりかけたので解説。

### mapを使うが動かん。
わい「配列の操作をしたいときはmapを使って、合計値はreduce使うと簡単に求められるで」
とも「ほえーー」
わい「まずは、itemsだけの配列つくってreduceすると分かりやすいんや」

```
const category = [
{id: 1,items: [{ id: 1, price: 500 },{ id: 2, price: 500 },],},
{id: 2,items: [{ id: 1, price: 1000 },{ id: 2, price: 300 },],},
]

const items = category.map((c) => c.items)

// エラーが起きる。
cosnt total = items.reduce((sum, item) => sum + item.price, 0)

```
わい「あれうごかん。。」
### 原因
mapは配列を返すメソッドなので、取り出した結果は、結局配列の中の配列になってしまう。
```
const items = category.map((c) => c.items)
// 結果
[ [ { id: 1, price: 500 }, { id: 2, price: 500 } ],
  [ { id: 1, price: 1000 }, { id: 2, price: 300 } ] ] 
```

### 無理やりやるとこうなる。
わい「あ、見づらくなるけど、reduce2回やればできるで」
とも「ほえー」

```
const items = category.map((c) => c.items)

const total = items.reduce((sum, current) => sum + current.reduce((sum, current) => sum + current.price, 0),0)
```

とも「見づらくてワイには分かりづらいな。」
わい「ちーん」


## 再結論
flatMapを使えば、配列の次元が低くなる。あとは、reduceで合計値を算出する。
```
const total = category
  .flatMap((c) => c.items)
  .reduce((sum, current) => sum + current.price, 0)
```

[参考]
https://qiita.com/NeGI1009/items/4befe4695a4712d96d56

## 感想
mapとreduceで簡単に出来ると思ったら、思わぬ落とし穴がありました。。
for ofとか使っても出来ると思いますが、配列メソッドでやりたいですよね。

jsにはまだまだ便利なメソッドがあると思うので、今後も少しずつ勉強して綺麗なコードにしたいと思います。

また、人に教えるときは、調子乗らないようにします。
【備忘】.bash_profileに書いてた処理を失念してハマった

# 概要
- 眠らせていたラズパイを久しぶりに引っ張り出した際に、Bluetoothデバイスに自動接続しにいく処理が自動実行されエラーを吐いているのを発見(pulseaudioが起動してエラーを吐く)
- `pulseaudio.service`に仕込んでおいたものとして思い込んでいたため、`systemctl disable pulseaduio.service`で停止すれば止まると思いきや、止まってくれない
- ”pulseaudio”でgrepかけるもそれらしいシェルを見つけられず悩みまくった

# 結論
- `~/.bash_profile`にがっつり処理を書き込んでました。

# ハマった要因
- プログラムの自動実行＝daemonだと強く思い込んでいた。
    - [参考：Raspberry Piでプログラムを自動起動する5種類の方法を比較・解説](https://qiita.com/karaage0703/items/ed18f318a1775b28eab4)
- grepで普通に検索しても隠しファイル/フォルダが対象にならないことを知らなかった。
    - `grep hoge .* *` って書かないと駄目なんですね…言われてみれば当たり前か…。
- そもそも.bash_profileのことを全然知らなかった
    - よくよく確認すると”ユーザログイン時”に自動処理が走ってたんですが、.bash_profileの存在を全然知らなかったのでそこにたどり着くのに時間かかりました…。

# 反省
- `systemctl`に仕込めば良さそうな処理を`.bash_profile`に書かない
    - 普通は環境変数の引き継ぎとかの処理を書くのが一般的なんですね。
    - [参考：.bash_profileと.bashrcのまとめ](https://qiita.com/takutoki/items/021b804b9957fe65e093)
- 運用メモをきちんと残す
    - 勢いでが～っと作ってそのまま放置してたのが最悪でした。
    - 設計書とまでは言わずとも、簡単な運用メモ（設定ファイルの場所、仕込んだ自動処理のサービス名・内容など）くらいは残すようにしようと強く思いました。

# 自分なりまとめ（自動処理・定期処理の使い分け）
- 基本は`systemd`に登録する
- "ユーザがログインした時に1度だけ実行"なら`.bash_profile`
- "SSHログイン時に自動実行"なんてのもある`/etc/ssh/sshrc`
    - [SSHログインを監視したい（SSHログインがあったらメール通知）＋ メール送信はAmazon SESを使いたい](https://qiita.com/s-katsumata/items/0e0a6881b325505a70ba)
cscの作法 その124

# 概要
cscの作法、調べてみた。
managed directxやってみた。
xファイルを表示してみた。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/fe66bcdb-12ce-af9a-71eb-0e01e8f02cef.png)

# サンプルコード

```
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace mdx
{
	public partial class Form1 : Form {
		private Device device = null;
		private Mesh mesh = null;
		private Material[] meshMaterials;
		private Texture[] meshTextures;
		public Form1() {
			Text = "Form1";
			this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
			PresentParameters pp = new PresentParameters();
			pp.Windowed = true;
			pp.SwapEffect = SwapEffect.Discard;
			device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, pp);
			ExtendedMaterial[] materials = null;
			mesh = Mesh.FromFile("Dolphin1.x", MeshFlags.SystemMemory, device, out materials);
			if (meshTextures == null)
			{
				meshTextures = new Texture[materials.Length];
				meshMaterials = new Material[materials.Length];
				for (int i = 0; i < materials.Length; i++)
				{
					meshMaterials[i] = materials[i].Material3D;
					meshMaterials[i].Ambient = meshMaterials[i].Diffuse;
					if (materials[i].TextureFilename != null)
					{
						meshTextures[i] = TextureLoader.FromFile(device, materials[i].TextureFilename);
					}
				}
			}
			this.Paint += Form1_Paint;
		}
		private void Form1_Paint(object sender, PaintEventArgs e) {
			device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f, 1.0f, 1000.0f), new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f));
			device.Transform.Projection = Matrix.PerspectiveFovLH((float)(Math.PI / 4), 1.0f, 0.1f, 10000.0f);
			device.Lights[0].Type = LightType.Directional;
			device.Lights[0].Diffuse = Color.White;
			device.Lights[0].Ambient = Color.Red;
			device.Lights[0].Enabled = true;
			device.Clear(ClearFlags.Target, Color.DarkBlue, 1.0f, 0);
			device.RenderState.Lighting = true;
			Matrix rotateMatrix = Matrix.RotationY(Environment.TickCount / 5000.0f);
			device.BeginScene();
			device.SetTransform(TransformType.World, rotateMatrix * Matrix.Translation(new Vector3(0.0f, 0.0f, 0.0f)));
			for (int i = 0; i < meshMaterials.Length; i++)
			{
				device.Material = meshMaterials[i];
				device.SetTexture(0, meshTextures[i]);
				mesh.DrawSubset(i);
			}
			device.EndScene();
			device.Present();
			this.Invalidate();
		}
		[STAThread]
		public static void Main() {
			Application.Run(new Form1());
		}
	}
}





```


以上。
J 言語の構文解析

J 9.03 の modifier train が いまいち理解できていなかったので、備忘録も兼ねて整理してみます。https://code.jsoftware.com/wiki/Vocabulary/ModifierTrains の内容を、まとめ直したものです。

`N`・`V`・`A`・`C` は、それぞれ noun・verb・adverb・conjunction を表します。

`N`・`V` は、`#` のような単独のトークンだけでなく、`#~` のように modifier を適用したものも含みます。
逆に、単独のトークンだけが許容されるケース (`C` の直後) では `N-`・`V-` と書くことにします。
また、fork のような noun や verb を生成するトークン列を許容する箇所は、`N+`・`V+` と表記します。

大体以下のような規則で解釈されるようです。

- modifier の実行が一番優先される
- conjunction と その直後の noun・verb が次に強く結びつく (`N-`・`V-`)
- modifier train は基本的に fork・hook よりも優先される
  ただし、一部 fork・hook を優先的に作る位置がある (`N+`・`V+`)
- verb の実行は一番優先順位が低い

# verb の実行

|並び|意味|
|---|---|
|`N V N`|(dyad `V` を実行)|
|`V N`|(monad `V` を実行)|

# modifier の実行

|並び|意味|
|---|---|
|`N C N-`|(`C` を実行)|
|`N C V-`|(`C` を実行)|
|`V C N-`|(`C` を実行)|
|`V C V-`|(`C` を実行)|
|`N A`|(`A` を実行)|
|`V A`|(`A` を実行)|

# fork ベースの train

|並び|意味|
|---|---|
|`N V V+`|(fork を生成)|
|`V V V+`|(fork を生成)|
|`A V V+`|adverb `(u A) V V`|
|`A A V+`|conjunction `(u A) (v A) V`|
|`C V- V+`|conjunction `(u C v) V V`|
|`V V+ C`|conjunction `V V (u C v)`|
|`C V- C`|conjunction `(u C v) V (u C v)`|

# hook ベースの train

|並び|意味|
|---|---|
|`V V+`|(hook を生成)|
|`A V+`|adverb `(u A) V`|
|`C C`|conjunction `(u C v) (u C v)`|

# adverb ベースの train

|並び|意味|
|---|---|
|`A A A`|adverb `((u A) A) A`|
|`C A A`|conjunction `((u C v) A) A`|
|`A A`|adverb `(u A) A`|
|`C A`|conjunction `(u C v) A`|

# conjunction ベースの train

|並び|意味|
|---|---|
|`N+ C A`|adverb `N C (u A)`|
|`V+ C A`|adverb `V C (u A)`|
|`N+ C C`|conjunction `N C (u C v)`|
|`V+ C C`|conjunction `V C (u C v)`|
|`A C N-`|adverb `(u A) C N`|
|`A C V-`|adverb `(u A) C V`|
|`C C N-`|conjunction `(u C v) C N`|
|`C C V-`|conjunction `(u C v) C V`|
|`A C A`|conjunction `(u A) C (v A)`|
|`A C C`|conjunction `(u A) C (u C v)`|
|`C C A`|conjunction `(u C v) C (v A)`|
|`C C C`|conjunction `(u C v) C (u C v)`|
|`N+ C`|adverb `N C u`|
|`V+ C`|adverb `V C u`|
|`A C`|adverb `(u A) C u`|
|`C N-`|adverb `u C N`|
|`C V-`|adverb `u C V`|
[Go] Range

# Range

Range　は要素の値のコピーが返されるので、
返される要素を変更しても、元の値を影響しない
変更したい場合は、直接参照すれば

## サンプルコード

```go
package main

import "fmt"

func main() {
	users := [3]string{"A", "B", "C"}
	fmt.Printf("%#v\n", users)

	for _, u := range users {
		u += "edited"
	}
	fmt.Printf("%#v\n", users)

	for i := 0; i < len(users); i++ {
		users[i] += " edited"
	}
	fmt.Printf("%#v\n", users)
}
```

```shell:output
[3]string{"A", "B", "C"}
[3]string{"A", "B", "C"}
[3]string{"A edited", "B edited", "C edited"}
```
ターミナルのコマンド

# ファイルの操作
## ls  ファイルの一覧を取得する
### オプション
#### -a 隠しファイル等も出力する
#### -l 長（long）形式で出力する
#### -t 最終更新時間によって、ソートをして出力する
#### -r 逆順にソートする

# オプションの同時指定と順序
コマンドによって順序と文法を細かく決まっている場合がある
## cp ファイルのコピー ファイルを新しいファイルで上書き
### cp [オプション]　コピー元　コピー先
#### -i 確認の問い合わせがある
#### -r ディレクトリの中にあるすべてのファイル・ディrクトリに対してコピーを行うことができます。
#### -p 元のファイルを情報を保存します。

## mv ファイルの移動 ファイル、ディレクトリの名前を変更
### mv 移動元ファイル　移動先ファイル
#### -i 確認の問い合わせ
#### -f 強制的に処理を実行します。確認を無視し、強制的に処理を実行する

## rm ファイルの削除
### rm ファイル名
#### -i 処理を行うときの確認
#### -f 強制的に実行
#### -r ディレクトリの中の、ファイルやディレクトリも削除する

# ディレクトリの操作
現在いるディクトリを変更する　移動先を指定できる
## pwd 現在のディレクトリの表示

## cd ディレクトリの変更
### cd [ディレクトリ名]

## mkdir　ディレクトリの作成
### mkdir [オプション] ディレクトリ名
#### -p 指定されたディクトリの上位ディレクトリも作成する

## rmdir　ディレクトリの削除
### rmdir ディレクトリ名
#### -p 指定した階層までのディレクトリを一括で削除します。

## 特別なディレクトリ
### カレンとディレクトリ（.)　現在いるディレクトリ

### 親ディレクトリ（..)　一階層上のディレクトリ

### ホームディレクトリ（〜）　ユーザーの作業開始位置となるディレクトリです。

### ルートディレクトリ（/)　最上位のディレクトリ


# ファイルの内容を表示
## cat ファイルの内容を表示
### cat ファイル名
#### -n　行番号を付して表示します。

# ファイルの検索
## find　 ファイルの検索 パス内のファイルを名を検索する
### find パス　-name ファイル名

# コマンドのパス
コマンドもプログラム。 プログラムはファイルの一種です。　プログラム用のディレクトリに配置されています。
## which　コマンドのパスの表示　プログラムの実態を把握する
PATH環境変数に含まれるディレクトリは以下に配置されているコマンドのパスを表示することができます。?
### which コマンド名

# 疑問
コマンドのファイルの位置を知ることによってどうすることができる、わかるのか？
わからない。
わかる方がいればお教えください。
BevyのFixedTimestepが正常に動作しないバグについて

Bebyでは`FixedTimestep::step(timestep)`で実行間隔を調整することができます。
ですが、この動作はある条件下では現在（Version 0.7）機能しなくなるバグがあり、私もかなりの足止めを食らいました。
同じ過ちを犯さないためにも起こっている問題と解決案について掲載します。

環境
Bevy: 0.7

<details><summary>問題のバグ</summary>

サンプルコード
```rs
use bevy::{core::FixedTimestep, prelude::*};

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
enum GlobalState {
    FirstState,
    SecondState,
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        // Init state
        .add_state(GlobalState::FirstState)
        // First state systems
        .add_system_set(
            SystemSet::on_enter(GlobalState::FirstState)
                .with_system(first_state::enter_system)
                .with_run_criteria(FixedTimestep::step(1.0)),
        )
        .add_system_set(
            SystemSet::on_update(GlobalState::FirstState)
                .with_system(first_state::update_system)
                .with_run_criteria(FixedTimestep::step(1.0)),
        )
        // Second state systems
        .add_system_set(
            SystemSet::on_enter(GlobalState::SecondState)
                .with_system(second_state::enter_system)
                .with_run_criteria(FixedTimestep::step(1.0)),
        )
        .add_system_set(
            SystemSet::on_update(GlobalState::SecondState)
                .with_system(second_state::update_system)
                .with_run_criteria(FixedTimestep::step(1.0)),
        )
        .run()
}

mod first_state {
    pub fn enter_system() {
        println!("First state: Enter");
    }

    pub fn update_system() {
        println!("First state: Update");
    }
}

mod second_state {
    pub fn enter_system() {
        println!("Second state: Enter");
    }

    pub fn update_system() {
        println!("Second state: Update");
    }
}

```
結果
```
> 理想
First state: Enter
First state: Update
...
First state: Update

> 現実
Second state: Update
First state: Enter
First state: Update
Second state: Enter
Second state: Update
First state: Enter
First state: Update
Second state: Enter
Second state: Update
First state: Enter
Second state: Enter
First state: Update
Second state: Update
First state: Enter
Second state: Enter
First state: Update
```
:::note info
Issueでは開発者はこのバグを「致命的なバグ」と言っているので、バージョンアップの際に解決されると思います。
0.7では解決されてなかったけど...
:::
</details>

### 解決案

```
Version 0.7

fn playing_timestep_system_set(timestep: f64) -> SystemSet {
    SystemSet::new().with_run_criteria(
        FixedTimestep::step(timestep).chain(
            (|In(input): In<ShouldRun>, state: Res<State<GameState>>| {
                if state.current() == &GameState::Playing {
                    input
                } else {
                    ShouldRun::No
                }
            })
            .to_owned(),
        ),
    )
}
```

もし`Version 0.6`を使用するという場合は`.to_owned()`を`.sysytem()`に変更してください
Jetstreamで写真を使いたい

```config/jetstream.php
...
    'features' => [
        // Features::termsAndPrivacyPolicy(),
         Features::profilePhotos(),//コメントアウトされているので外す
        // Features::api(),
        // Features::teams(['invitations' => true]),
        Features::accountDeletion(),
    ],

...
```
```bash
php artisan storage:link
```
パーミッションを適切に設定して
```.env
...
APP_URL=
...
```
APP_URLを空に設定
configファイルを触ったので
```bash
php artisan cache:clear                                                                                                                 
php artisan config:clear
```
ローカルでは動いているがデプロイしても動くのか、、、
AngularでGoogleMap上のクラスター再描画が重くてハマった件

GoogleMapに0個～1000個のマーカー設置を切り替えるのをマーカーの追加/削除で対応していたらクラスターの再描画が重くて使い物にならなかったのでその対応方法。

# 結論
- マーカーの表示/消去はマーカーの追加/削除ではなくvisibleプロパティを使用する。

https://developers.google.com/maps/documentation/javascript/reference/marker#Marker.setVisible

- クラスターにはignoreHidden=trueを設定する。

https://googlemaps.github.io/v3-utility-library/classes/_google_markerclustererplus.markerclusterer.html#setignorehidden

# 環境
- Angular: 12.2.0
- @agm/core: 3.0.0-beta.0
- @agm/markercluster: 3.0.0-beta.0
- @google/markerclusterplus

# 修正概要
## 改善前
```
<agm-map [latitude]="lat" [longitude]="lng" [zoom]="zoom" [minZoom]="minZoom" [maxZoom]="maxZoom">
  <agm-marker-cluster imagePath="(クラスタ画像パス)">
    <agm-marker *ngFor="let marker of markerList" [latitude]="marker.lat" [longitude]="marker.lng" iconUrl="(マーカー画像パス)"></agm-marker>
  </agm-marker-cluster>
</agm-map>
```
改善前は`*ngFor`でマーカー一覧(`markerList`)を設定しており表示対象の項目が変わるたびに全マーカー一覧から`filter()`で絞った結果を`markerList`に設定していた。

この場合`markerList`が再設定されるたびに`<agm-marker>`のDOMが再構築され、それをトリガーにGoogleMap上に実際に表示されているクラスターのDOMが再構築されるのだが`<agm-marker>`が構築/削除されるたびにクラスター描画が行われているのか15秒程度GoogleMapが固まりさらにクラスター表示も一時的に変に重なったりして非常に重い。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133525/a0147573-61b7-3c06-e1ff-d45cf9792c6a.png)

## 改善後
マーカー一覧(`markerList`)の再設定をしてはいけないことが分かったので`markerList`には全マーカー一覧をまず設定しておく。
そのうえで表示するマーカーには`visible=true`、表示しないマーカーには`visible=false`を設定し、クラスター側でも`ignoreHidden=true`を設定することで`visible=false`のマーカーをクラスター対象外にする。
※表示するマーカー一覧を変更するときは`markerList`の配列インスタンスを変えずに`visible`プロパティのみを書き換える。
```
<agm-map [latitude]="lat" [longitude]="lng" [zoom]="zoom" [minZoom]="minZoom" [maxZoom]="maxZoom">
  <agm-marker-cluster imagePath="(クラスタ画像パス)" [ignoreHidden]="true">
    <agm-marker *ngFor="let marker of markerList" [latitude]="marker.lat" [longitude]="marker.lng" iconUrl="(マーカー画像パス)" [visible]="marker.visible"></agm-marker>
  </agm-marker-cluster>
</agm-map>
```
https://angular-maps.com/api-docs/agm-core/directives/agmmarker#visible
https://angular-maps.com/api-docs/markerclusterer/directives/agmmarkercluster#ignoreHidden

こうすることで`<agm-marker>`のDOM再構築がなくなるのでクラスターの再描画回数が減り問題ないレベルになった。
ただし、マーカーのvisibleを変更したら`MarkerClusterer.repaint().`もコールするようにと上記ドキュメントに記載されているが`<agm-marker-cluster>`ディレクティブのインスタンスをコンポーネント側でどうやって取得するのかがいまだ不明※。
そのため、クラスター画像の更新がされないのでZoom値を強制的に変えるなりして強制再描画を行っている。

※ClusterManagerをDIして`getClustererInstance()`で取得できそうだったが取得しても別のインスタンスっぽくてrepaint()しても再描画されなかったがやり方がよくなかったか。。
https://angular-maps.com/api-docs/markerclusterer/injectables/clustermanager#getClustererInstance

まあ、使用しているAgmMapライブラリは2年近くアップデートされていないのでこちらより`@angular/google-maps`を使用したほうがよいとは思う。

https://www.npmjs.com/package/@angular/google-maps

以下のサイトは大変参考になりました。上記のライブラリを使っているようでrepaint()も実施できている模様。

https://artisan.jp.net/blog/angular-googlemaps-2/
Vite + Vue3のプロジェクトをGithub Pagesを使って5分でデプロイする

# 目的
タイトルの通り今ちょっとしたwebアプリを開発していて、完成はしていないが一旦デプロイしようと思い、一番簡単なイメージかつまだ経験のないGithub Pagesを利用してみることにした。

# 結論
作業時間自体はうまくいけば5分もかからないが、詰まることはあるかもしれない。

# 前提
Githubにレポジトリを既に作成済みで、使い方もなんとなくわかっていること

# 方法
### mainもしくはmasterブランチに変更
去年masterからmainにデフォルトブランチ名が変更されたが、変わらずmasterを使っている場合はmasterに作業ブランチを切り替える。
このブランチがデプロイするブランチという認識。

### vite.config.tsをいじる
ブランチをmainかmasterに変更したら、vite.config.tsを少し変更する。
自分の場合はVuetifyとか使っているので、読者の方とは違うかもしれないが、```base: '<REPO_NAME>',```を```defineConfig```に追加すれば問題ない。
<REPO_NAME>にはGitのレポジトリ名を入れてくだちゃい。

```vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vuetify from '@vuetify/vite-plugin' 

const path = require('path')

// https://vitejs.dev/config/
export default defineConfig({
  base: 'QiitaSearch', //追加
  plugins: [
    vue(),
    vuetify({
      autoImport: true,
    }),
  ],
  define: { 'process.env': {} },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
})

```
### ビルドする
自分はyarnでpackage.jsonの```"scripts"```を参考に下記コマンドを実行する。
```terminal
yarn build
```
これでルートディレクトリにdistフォルダが追加され、その中にビルドされたindex.htmlとassets/jsファイルなど、が追加される。

### 変更をステージング、コミット
いつもは```git add .```とかで全ての変更をステージングしているかもしれない。
今回は```dist```だけステージングする、かつ```.gitignore```でdistがgitに追加できないように指定されている。
そのため-fで強制的にdistファイルを```gh-pages```ブランチにのせる

```
git add dist -f
git commit -m"adding dist"
```

### リモートにまだ存在しないgh-pagesブランチにpushする
```
git subtree push --prefix dist origin gh-pages
```

### 確認する
Settings > Pages >  Your site is published at https://USER_NAME.github.io/REPOGITORY_NAME
という形で表示されている

## もっと簡単にしたい人へ
実はステージング〜pushまでを簡単に行える方法がある。
gh-pagesというまさにこの作業のためのライブラリである。
こちらをインストールするっす。
```
npm install gh-pages
```
これでgh-pagesコマンドが使えるようになったため、package.jsonをいじって```yarn deploy```だけでステージング〜pushまでを行えるようにする。
```package.json
"scripts": {
    "serve": "vite preview",
    "build": "vite build",
    "dev": "vite",
    "preview": "vite preview",
    "deploy": "gh-pages -d dist" // 追加
  },
```
こうすることで、masterブランチ上で下記の二つのコマンドを入力するだけで、デプロイが可能になる、、
```
yarn build
yarn deploy
```

これからgithub actionとか使ってCI/CDをもっとガッツリ実装していくので、少々お待ちください！


# 参考
https://learnvue.co/2020/09/how-to-deploy-your-vue-app-to-github-pages/#how-does-github-pages-work

https://zenn.dev/sohhakasaka/articles/3e386fa9a650cd
エンジニア歴2年なりに感じている、仕事で良いアウトプットをするコツ

## はじめに

新卒で入った別業界を半年で辞め、エンジニアになってから2年が経ちます。

ありがたいことに入社してすぐプログラムを書かせていただき、
詳細設計やお客様とのやり取りも経験させていただきました。

まだまだ無限に学ぶことがあるのですが、2年経験したなりに「これのおかげで仕事がうまくいくようになったな〜」という勘所を書いていきます。

## シンプル イズ ベスト

シンプルな解決策がいっちゃん良いです。

シンプルなプログラムはバグりにくく、バグっても直しやすいです。
シンプルなコミュニケーションは分かりやすく、仕事が円滑に回るようになります。

人間が関わることは放っておくとどんどん複雑になっていきます。

だからこそ、思考停止せずに常にシンプルな解決を心がけるのが大事ですよね。

<br>
<br>

## ゴールから逆算する

プログラムもそれ以外のタスクも、ゴールから逆算して組み立てるのが良いです。

すべての作業はゴールを達成するためにあります。
つまりゴールから逆算すれば、適切な手段を適切な順番で組み立てられます。

僕の場合、コードを書くときは最終的に使いたい箇所（関数なら呼び出し箇所）から書くようにしていて、
こうすると手戻りなくスラスラと書き終えることができるようになりました。

あまりに未知の作業すぎてゴールからの逆算ができないときは、
ボトムアップで試行錯誤するのも大事だと思います。

ただある程度見通しの立つ作業であればゴールから逆算したほうが、質の良いアウトプットが素早く出せます。

<br>
<br>

## 「何をするか」よりも「何をしないか」

「やること」と同じくらい「やらないこと」を選び取る能力が大事です。

ゴールから逆算してシンプルな解決策を見出しても、
「その作業が実は重要じゃなかった・不要だった」となれば、無意味な時間を過ごしただけになります。

- 後になってみるとこのプログラム不要だった
- 意外とお客様にとって重要な作業じゃなかった（なんならやらなくてよかった）
みたいなことになると良いアウトプットも出せないし、なにより徒労感がエグいです。

作業をするときは「どうやるのが最適解か」に目がいきがちですが、

そういうときこそ「そもそもやらない」が一番の時短になることを思い出して、本当に必要な作業なのか立ち止まるのが大切だと思います。

<br>
<br>

## 疲れる前に休む

疲れる前に休みます。

一度疲れてしまうと、寝るまで回復しないからです。

僕の場合は、以下のようにして疲れる前に休んでいます。

・15分と1分のポモドーロ・テクニックを行う
・1時間に1回、5分ほど散歩する
・昼休みに15分昼寝する

これをやると、集中力が持続するんです。

作業にノッてるときに「休憩なんかしないでぶっ通しでやっちゃうぜ！」となる気持ちはわかります。

でも自分の感覚と実際の疲労はだいたいズレてます。

「実は体感より1.5倍疲れてる」くらいに思っておくのがちょうど良いんじゃないでしょうか。

疲れてからじゃ遅いです。疲れる前に休みましょう。

<br>
<br>

## おまけ：食事管理

頭の調子が良くなる食事をします。

「おまけ」にしている理由は、同僚に聞くと「何を食べても調子が変わらん」という方もいて、個人差がありそうだなと思ったからです。

僕の場合、以下のような食事の変遷を辿ってきました。
- gi値が低いものから食べる
- 1日2食
- 1日1食
---↓再び1日2食---
- 小麦抜き
- 糖質抜き・高脂質【現在】

人によって差はあるようですが、個人的に食事の影響はとても大きくて、最近は頭が常に冴えています。

ちなみに現在の食事は[こちらの本](https://www.amazon.co.jp/%E4%B8%96%E7%95%8C%E3%81%AE%E3%82%A8%E3%82%B0%E3%82%BC%E3%82%AF%E3%83%86%E3%82%A3%E3%83%96%E3%82%92%E5%A4%89%E3%81%88%E3%81%9F%E8%B6%85%E4%B8%80%E6%B5%81%E3%81%AE%E9%A3%9F%E4%BA%8B%E8%A1%93-%E3%82%A2%E3%82%A4%E3%82%B6%E3%83%83%E3%82%AF%E3%83%BB%EF%BC%A8%E3%83%BB%E3%82%B8%E3%83%A7%E3%83%BC%E3%83%B3%E3%82%BA-ebook/dp/B01FDD3OQK/ref=sr_1_1?adgrpid=102223221497&gclid=Cj0KCQjw0umSBhDrARIsAH7FCoe972gUhiSNuAtbLeEIXQKLbP67J1bAP7wYT5a6UCv1XjkkYBnEuz0aAnjMEALw_wcB&hvadid=448182610968&hvdev=c&hvlocphy=1009076&hvnetw=g&hvqmt=e&hvrand=6780319322100980329&hvtargid=kwd-333823056149&hydadcr=19661_11037022&jp-ad-ap=0&keywords=%E8%B6%85%E4%B8%80%E6%B5%81%E3%81%AE%E9%A3%9F%E4%BA%8B%E8%A1%93&qid=1650157645&sr=8-1)を参考にしています。

##  終わりに

いかがだったでしょうか。

もちろん、日々の勉強・ちゃんと寝る・チームメンバーと仲良く・作業の自動化...などが前提ですが、
今回はそれより少しニッチなトピックに絞ってみました。

ここに書いてあることがどなたかのお役に立てれば幸いです。
JavaScript　slice()　文字列から好きな場所だけ切り取る

## 用途
文字列から好きな場所だけ切り取りたい時に使う。

## 使用方法
```js:書き方
var hoge = "文字列";
hoge.slice( 開始位置, 終了位置 ); // 終了位置は省略可能
```

```js:例（終了位置を指定）
var hoge = "ABCDEFGHI";
var hogehoge = hoge.slice(3, 5);
console.log(hogehoge);

// 結果
// DE
```

```js:例（終了位置を指定しない）
var hoge = "ABCDEFGHI";
var hogehoge = hoge.slice(3);
console.log(hogehoge);

// 結果
// DEFGHI
// ※開始位置以降全て取得する
```

```js:例（開始位置がマイナス）
var hoge = "ABCDEFGHI";
var hogehoge = hoge.slice(-2);
console.log(hogehoge);

// 結果
// HI
```

**引数マイナスについて**
末尾が -1 から始まる
||||||||||
|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|
|A|B|C|D|E|F|G|H|I|
|-9|-8|-7|-6|-5|-4|-3|-2|-1|
Content-Type: application/jsonとはそもそも何なのか




## Content-Typeとは

- リソースのメディア種別を示すために使用する
- レスポンスにおいては、 クライアントに返されたコンテンツの実際の種類を伝える
- リクエストにおいては クライアントがサーバーに実際に送ったデータの種類を伝える
- **タイプ/サブタイプ**の形で表現される

### Content-Typeを指定しない場合、どうなるのか

推測してContent-Typeを決める。
よって、意図した形式でデータをやりとりできるように、Content-Typeを定めた方が良い。

## application/jsonとは

jsonフォーマットでデータが送信される。

例えば、以下のような形。

```
{"Name": "John Smith", "Age": 23}
```

## 補足：application/x-www-form-urlencodedとは

以下のようにエンコードされて送信される。

```
Name=John+Smith&Age=23
```

## 参考
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Type
https://portswigger.net/kb/issues/00800500_content-type-is-not-specified#:~:text=If%20a%20response%20does%20not,or%20other%20client%2Dside%20vulnerabilities.
https://stackoverflow.com/questions/9870523/what-are-the-differences-between-application-json-and-application-x-www-form-url
今週の学び　第2回

# 今週の学び

タグ関係

- navタグ
    - 主にheaderで使う
- sectionタグ
    - mainの中でh2の前など大きい括りで使う
- tableタグ
    - trとtdで使用、ul、liに似ている
- font-awesome
    - CDNが廃されたので、使用時に新しいソースが必要
- href="./style.css"
    - フォルダの場所を相対的に見なければ、読み込めなくなってしまう
- pictureタグ
    - CSSやJavaScriptを使わなくても、画像をレスポンシブに切り替えることができる
- articleタグ
    - 記事に対して使う

要素やクラスなど

- input
    - inputは最初からインラインブロックなのでmarginを効かせたいならブロック要素にする
- viewportの設定
    - 上手くレスポンシブ対応のcssが動作していなかったので、別のサイトで正しいコードを調べた
- レスポンシブ対応時の画像の大きさ指定
    - スマホ用ページの画像で最大の大きさの内、一部を表示するにはwidthではなくhightを画像に対して指定する
- クラス名の考え方
    - プレフィックス(接頭語)、そこに関連したものを付ける、headerならheader-logoなど
- object-fit: cover;
    - object-fitプロパティは、置換要素、例えば imgやvideoなどの中身を、コンテナーにどのようにはめ込むかを設定する
- table要素への下線
    - table 要素、th 要素、td 要素それぞれに 「border-bottom: 1px solid black;」を指定し、 td 要素間の枠線の隙間を無くすために「border-collapse: collapse;」 を指定

- インスタグラムのアイコンはfontawesomeから貼り付けているが、iタグとaタグを同時に使ってアイコンにインスタグラムのリンクを貼ることができるのか？
    - ちゃんとaタグの括弧内に紐づけたいワードが入っているか注意

- headerなどで要素同士が影響して上手く整理できない時
    - margin.flex.justify-contents: space-betweenなどを使う

- text-align
    - 基本的に直前の親要素に指定が普通だが、直接h2などに指定しても良い


- vertical-align
    - アイコンなど大きさを変えた時に位置が少しずれるので、調整の為に使用
- ページを2カラムにするやり方
    - cssにflexを使う
- ショートカットキー、command+tab→ページ切り替え command+R→更新
- widthなどの%指定
    - %指定は基本的に親要素をみてくる

margin.paddingなど

- margin.paddingには%も使える
- ulのデフォルトにpaddingが入っているのでpadding: 0;で上書きする
- liの点の余白はmargin.padding 0 で消す
- marginやpaddingにマイナスは使わない方が良い
- px.em.remの違い
    - px=サイズを絶対値指定する.
    - em=サイズを相対値値指定する(親要素基準)
    - rem=サイズを相対値値指定する(ルート要素基準)







今週のまとめ

勉強を進めるにつれて、難しくなっているので
覚えた事を忘れないようにしていく
Delta Sharingを用いたデータの共有・アクセスのトラブルシュート

[Troubleshoot sharing and accessing data using Delta Sharing \| Databricks on AWS](https://docs.databricks.com/data-sharing/delta-sharing/troubleshooting.html) [2022/4/6時点]の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

# 共有(share)内のデータへのアクセス

share内のデータにアクセスする際に以下のエラーが起こることがあります。

## File not foundあるいはcertificateのエラー

**問題**: ファイルが見つからない、あるいは証明書の例外を示すエラーに遭遇する。
**可能性のある原因**: お使いのバケット名でドット、あるいはピリオドを使用している。これはAWSの制限です。[Amazon S3 Path Deprecation Plan](https://aws.amazon.com/blogs/aws/amazon-s3-path-deprecation-plan-the-rest-of-the-story/)をご覧ください。
**推奨する修正案**: Unity CatalogとDelta Sharing向けに別のバケットを使う。

Sparkのエラー例:
```
FileReadException: Error while reading file delta-sharing:/%252Ftmp%252Fexample.share%2523example.tpc_ds.example/XXXXXXXXXXXXX/XXXXXXXX.

Caused by: SSLPeerUnverifiedException: Certificate for - <[workspace name].cloud.databricks.com.s3.us-east-1.amazonaws.com> doesn't match any of the subject alternative names [s3.amazonaws.com, *.s3.amazonaws.com…]:
```

Pandasのエラー例:
```
FileNotFoundError(path)
FileNotFoundError: https://xxxx.xxxxxx.s3.xx-xxxx-1.amazonaws.com/xxxxxx/part-00000-xxxxx-Amz-Algorithm=Axxxxxx-Amz-Date=xxxxxxxx&X-Amz-SignedHeaders=host&X-Amz-Expires=xxx&X-Amz-Credential=xxxxxxx_request&X-Amz-Signature=xxxxx
```

Power BIのエラー例:
```
DataSource.Error: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
Details:
    https://xxxx.xxxxxxxxx.s3.xx-xxxx-1.amazonaws.com/xxxxxxxx/part-00000-xxxxxxx.snappy.parquet
```

# 追加のリソース
- [Getting started with Databricks](https://docs.databricks.com/getting-started/index.html)
- [Delta Sharing overview](https://delta.io/sharing/)
- [Unity Catalog overview](https://docs.databricks.com/data-governance/unity-catalog/index.html)

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
Lambdaでindex.handler is undefined or not exportedが出た時の対処法


## 問題

Lambda実行時に以下のエラーが発生した

```bash
index.handler is undefined or not exported
```

## 対処法

以下の2つを実施した。

### ファイル名の変更

SAMでサンプルアプリケーションを作成したが、ファイル名がapp.jsとなっていたので以下を変更した。

- ファイル名変更
    - 	app.js -> index.js
- package.jsonの変更

```
"main": "app.js",
```
↓
```
"main": "index.js",
```

### ランタイム設定の見直し

アップロードしたファイル構造と、ランタイム設定が一致していることを確認する。

例えば、以下の構造の場合

```
|--src
   |--index.js
```

Lambdaの以下のようにランタイム設定のハンドラ部分をsrc/index.handlerとすれば良い

![markdown-linter - Lambda 2022-04-17 08-56-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882682/0f11ff9a-d912-6618-b3b8-e65b5fec87ef.png)
Minecraftサーバ（統合版：bedrock_server）に Nintendo Switchから接続させる

前回記事↓の続きです。
https://qiita.com/ABK28/items/df16c4822994527f4196

Nintendo Switch版のマイクラも買ったので、つなごうとしたら、いろいろあるみたいでした。
いろんなサイトのお世話になりましたが、毎回検索して、対応するの手間なので、自分用にメモっている投稿です。


# Switch のDNS設定の変更

具体的には、これ↓をやっています。
https://github.com/Pugmatt/BedrockConnect

## 実際にやってみる。

１）設定→インターネット設定を選ぶ
![00_インターネット設定.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186659/407cef96-b606-2f7d-95ce-698ae016a96e.jpeg)

２）普段つないでるWiFiを選ぶ
（写真撮り忘れ・・）

３）DNSを「手動」にして、下記のIPアドレスを設定する

```
優先DNS　　104.238.130.180
代替DNS　　8.8.8.8
```

![00_DNS変更.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186659/6031976b-c734-da62-49d3-a02106785d46.jpeg)


### スイッチ再起動！
たまに、インターネット接続で接続エラーになることもありますが、リトライすると代替DNSをみて繋がります。


# マイクラでサーバ追加
下記の手順で独自サーバを追加します。

１）起動
![1_TOPページ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186659/cc773d83-c05c-6a13-3aa3-3cc1cdbedf8f.jpeg)

２）サーバタブで、どこでもいいので特集サーバに参加する。
![2_サーバタブ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186659/3ba01c2c-91ca-a8ba-19ac-acf2eee8656d.jpeg)

３）しばらくすると下記画面になるので、「Manage Server List」を選ぶ
![3_サーバリスト.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186659/025690aa-be82-873a-17e2-ff86bf68b907.jpeg)

４）「Add a server」を選ぶ
![4_Managed_Server_List.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186659/7fb4ed7c-bb8f-e709-6069-2629d0447d92.jpeg)

５）独自サーバの情報を登録する。
![5_Add_a Server.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186659/67e3c8a3-7e44-f61d-eb22-f164b1525663.jpeg)

### 接続テスト
上記３番の画面にところにサーバが追加されてますので、それを選ぶと、独自サーバに繋がります。


# 普段のつなぎ方
上記サーバ追加の１〜３を毎回実施します！！


# 完成
めでたしめでたし