PHPのGinqについて纏めました


## はじめに
初めまして、[ｋ．ｓ．ロジャース](https://www.wantedly.com/companies/ks-rogers)の西谷です。
最近はPHPを中心に開発を進めております。
その中でGinqを採用しているプロジェクトがあり、以前から利用していたC#のLINQと同じことが出来るとのことでびっくりしています。

勉強をしていくなかで、[LINQのよく使う構文をまとめた記事](https://qiita.com/Marimoiro/items/0e119b47d65bf138789a)が非常に参考になったため、サンプル集としてGinqで書いていこうと思います。
C#の開発者からPHPに移行する方はあまり多くないと思いますが、少しでも手助けになればと思います。

間違い等があればコメントにてお知らせいただけたらと思います。

## 導入手順
詳しい導入手順は他記事にお任せします。
次のライブラリを導入すれば、Ginqを利用できるようになります。
https://github.com/akanehara/ginq


## GinqやLINQの利点
GinqやLINQを利用することで、開発者としてすぐに受けられるメリットはシンプルに配列操作が出来ることであると思います。

collection操作を利用しない場合は代入のためだけにforeach回して、その結果煩雑なコードになることが多々あります。
foreachと比べると少しパフォーマンスが落ちてしまいますが、可読性・生産性が向上すると思います。
例えば、次のようなコードをGinqやLINQに切り替えるだけでも、読みやすくなると思います。

```php
// 通常のループ
$data = [];
foreach($users as $key=>$value) {
   if ($value["age"] > 20) {
      $data[$key] = $value;
   }
}

// Ginqを使う
$data = Ginq::from($users)->where(function ($v) {
   return $v["age"] > 20;
});

```

```csharp
// 通常ループ
var data = new List<User>();
foreach (var user in users)
{
   if (user.age > 20)
   {
      data.Add(user);
   }
}

// LINQを使う
var data = users.Where(v => v.age > 20);
```

## 基本的な操作
```php
//0～9までの配列
$data = Ginq::Range(0, 9)->ToArray();

// 条件検索
// [0, 2, 4, 6, 8]
$evens = Ginq::from($data)->where(function ($i) {
   return $i % 2 == 0;
})->toList();

// 配列内の全てに対して処理を行う
// [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
$aquares = Ginq::from($data)->select(function ($i) {
   return $i * $i;
})->toList();

// 先頭からn個取得
// [0, 1, 2]
$oneTwoThree = Ginq::from($data)->take(3)->toList();

// n個スキップして残りを取得
// [7, 8, 9]
$sevenEightNine = Ginq::from($data)->drop(7)->toList();
$result[] = $sevenEightNine;

// 先頭から条件を満たさなくなるまでとる(Whereとは一度でも条件を満たさなくなったらストップする点でちがう)
// [0, 1, 2, 3]
$while4 = Ginq::from($data)->takeWhile(function ($i) {
   return ($i + 1) % 5 != 0;
})->toList();

// 条件を満たしている間スキップして残りをとる
// [4, 5, 6, 7, 8, 9]
$skip4 = Ginq::from($data)->dropWhile(function ($i) {
   return ($i + 1) % 5 != 0;
})->toList();

// 先頭を取得する
// 0
$first = Ginq::from($data)->first();

// 条件を満たす最初の要素を取得する(ない場合は例外)
// 1
$firstOdds = Ginq::from($data)->first(function ($i) {
   return $i % 2 == 1;
});

// 条件をみたすものがないときは先頭を返す
// 0
$default0 = Ginq::from($data)->firstOrElse(function ($i) {
   return $i < -10;
});

// 最後を取得する
// 9
$last = Ginq::from($data)->last();

// 条件を満たす最後の要素を取得する
// 8
$lastEven = Ginq::from($data)->last(function ($i) {
   return $i % 2 == 0;
});
```


## 配列の生成
```php
// 0~9の配列
$data = Ginq::Range(0, 9);

// 同じ値の配列を作成
// [4, 4, 4, 4, 4]
$repeat = Ginq::repeat(4, 5)->toList();

// 空の配列を返す
// []
$empty = Ginq::zero()->toList();

// [0, 1, 2, 3, 4]
$data = Ginq::range(0, 4)->toList();
// [0, 1, 4, 9, 16]
$square = Ginq::from($data)->select(function ($i) {
   return $i * $i;
});

// 2つの配列を繋げる
// [0, 1, 2, 3, 4, 0, 1, 4, 9, 16]
$concat = Ginq::from($data)->concat($square)->toList();

// 2つの配列を合成する
// ["0 with 0", "1 with 1", "2 with 4", "3 with 9", "4 with 16"]
$zip = Ginq::from($data)->zip($square, function ($d, $s) {
   return "$d with $s";
})->toList();

// join用のデータを用意する
$join_data = Ginq::from($data)->select(function ($d) {
   return ["id" => $d, "square_" => $d * $d];
})->toList();
$join_square = Ginq::from($square)->select(function ($s) {
   return ["id" => $s, "dummy" => "dummy-{$s}"];
})->toList();

// 2つの配列の結合
// join(結合先の配列, 結合元のkey, 結合先のkey, function)
// [["id" => 0, "dummy-0"], ["id" => 1, "dummy-1"] ...]
$join = Ginq::from($join_data)->join($join_square, "[square_key]", "[id]", function ($join_data, $join_square, $data_key, $square_key) {
   return [
         "id" => $join_data["id"],
         "dummy" => $join_square["dummy"]
   ];
})->toList();
```

## 数学関連
```php
// 0～9
$data = Ginq::Range(0, 9);

// 最大値
// 9
$max = Ginq::from($data)->max();

// 関数適用した後の最大値
// 81
$max2 = Ginq::from($data)->max(function ($i) {
   return $i * $i;
});

// 最小値
// 0
$min = Ginq::from($data)->min();

// 関数適用した後の最小値
// 5
$min2 = Ginq::from($data)->Min(function ($i) {
   return i + 5;
});

// 和集合
// 0,1,2,3,4,9,16
$union = Ginq::from($data)->union($square)->toList();

// 積集合
// 0,1,4
$intersect = Ginq::from($data)->intersect($square)->toList();

// 差集合
// 2,3
$except = Ginq::from($data)->except($square)->toList();

// 排他的論理和
// 2,3,9,16
$xor = Ginq::from($union)->except($intersect)->toList();
```

## 参考記事
以下の記事を参考、引用させて頂きました。
https://qiita.com/Marimoiro/items/0e119b47d65bf138789a
https://qiita.com/hidenorigoto/items/bee60836112e49eccadb


## 終わりに
今回はGinqについてご紹介させて頂きました。 
非常に便利な機能だと思いますので、利用プロジェクトが増えればと思います。

### Wantedlyでもブログ投稿してます
Techブログに加えて会社ブログなどもやっているので、気になった方はぜひ覗いてみてください。
https://www.wantedly.com/companies/ks-rogers