C#のOfTypeメソッド相当の処理は、SwiftではflatMapで書ける、という話

[【C#】Where(it => it is Xxx).Select(it => it as Xxx)って書かず、OfType使おう！【LINQ】 - Qiita](http://qiita.com/RyotaMurohoshi/items/c82a20e261dd0682e1d4)を見て、Swiftとの対比記事を書いてみようと思いました。

元記事では、C#でコレクションから特定の型のオブジェクトだけ取り出したい場合は、`Where`・`Select`の組み合わせでも出来るものの、`OfType`を使うと簡潔に書けて良い、ということでした。

元記事のコード例から、Unity依存排除してシンプル化するなどして、コード例を書き直してみました。
([Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/)があるとParallelsでWindows仮想マシン起動せずに済んで便利でした(　´･‿･｀))

```csharp
using System;
using System.Linq;

class Program
{
	class Base { }
	class A : Base { }
	class B : Base { }

	static void Main(string[] args)
	{
		var objects = new Base[] { new A(), new B() };
		var arrayOfA = objects
			.Select(x => x as A)
			.Where(x => x != null)
			.ToArray(); // → Aオブジェクト1つだけの配列となる

        // 以下のように`OfType`を用いても同じ結果になるので、こう書く方がベター
        var arrayOfA2 = objects.OfType<A>();
	}
}
```

(元記事では、`Where` → `Select` で`is`・`as`という型比較系の処理が2回行われていたのが冗長に感じたので、`Select` → `Where`の順に書き換えています)

# Swift ではどう書く？

いくつかコード例書いていきます。

## `map` → `filter` (NG)

上のC#の例の`Select` → `Where`に相当する処理である`map` → `filter`に単純に置き換えてみました。

```Swift
class Base { }
class A: Base {}
class B: Base {}

let objects: [Base] = [A(), B()]
let arrayOfA = objects
    .map { $0 as? A }
    .filter { $0 != nil }
```
結果は、`[Optional(A)]`となり、求めていた`[A]`ではなくNGです。

以下のように、`filter`(`is`でフィルター) → `map`(フィルター済みなので一応`as!`でもOK…)すると、結果は求めていた`[A]`になりますが、イマイチなやり方ですよね。

```swift
let arrayOfA2 = objects
    .filter { $0 is A }
    .map { $0 as! A }
```

## `map`で`Optional(A)`のArrayにしたあと、がんばって`nil`を除去

`reduce`でいわゆる`flatten`ぽいことをしたコードが以下です。

```swift
let arrayOfA = objects
    .map { $0 as? A }
    .reduce([A]()) { sum, e in
        if let e = e {
            return sum + [e]
        }
        return sum
    }
```

とりあえず結果は求めていた`[A]`となりましたが、ちょっと処理が煩雑ですね。

## `flatMap`を使う

そこで、本命の`flatMap`を使うと、こんなに簡単に書けてしまいます👏

```swift
let arrayOfA = objects.flatMap { $0 as? A }
```

`flatMap`は、`map`して`flatten`(平坦化)の意味なので、まさしくやりたかったことに沿ったメソッドですね。
`flatMap`では、`Optional(A)`のArrayにして(`map`)、さらにアンラップ(nilは除去)する処理(`flatten`)が一度になされています。

## Swiftにも`ofType`を定義

`flatMap`でシンプルに書けるのであまり意味が無いですが、Swiftにも`ofType`を定義してみます。

```swift
extension Collection {
    func ofType<T>() -> [T] {
        return flatMap { $0 as? T }
    }
}
```
すると、次のように、SwiftでもC#の`ofType`が使えるようになりました。
ただ、`flatMap`をベタに書いた場合と記述量も読みやすさも変わらないので、基本的には`flatMap`をそのまま使えば良いと思います。

```swift
let arrayOfA = objects.ofType() as [A]
```

つまり、`flatMap`という汎用メソッドがあるだけで、`ofType`や類似の処理が簡潔に書ける、というキレイな世界になっています👏

(Swiftで`ofType`書くとしたら、上記のように`flatMap`ラップではなくベタに書いて`flatMap`より高パフォーマンスなメソッドにしておくと存在価値ありそう)

# C#では`flatMap`ぽいものは無いの？

## `SelectMany`で可能(Swiftの`flatMap`とまったく同じでは無い)

少し記述が混み合いますが、`SelectMany`で可能です。

```csharp
var arrayOfA = objects.SelectMany(x =>
		{
			var a = x as A;
			if (a == null)
			{
				return new A[0];
			}
			return new A[] { a };
		}).ToArray();
```

## nilの扱いが絡むと`Swift`の方がシンプル・堅牢に書けることが多い

上の例では`SelectMany`のラムダ式が複雑になりましたが、以下のように`[[1, 2], 3]` → `[1, 2, 3]`と変換するような単純なシーケンスの平坦化処理であれば、`swift`とあまり差が無くなります。
(配列の初期化部分は少し面倒な記述になっていますが、`SelectMany`の中身はシンプル)

C#:

```csharp
var results = (new int[][] { new int[]{ 1, 2 }, new int[]{ 3 } }).SelectMany(x => x).ToArray();
```

Swift:

```swift
let results = [[1, 2], [3]].flatMap { $0 }
```

Swiftではオプショナルが言語仕様に組み込まれていて、さらに`flatMap`が、ArrayもOptionalも同じ「入れ物」として抽象化しているので、「平坦化」処理の際、入れ子の配列を合体させるような感覚で、Optionalのアンラップが行われます。

## C# でも拡張メソッド足すことで`SelectMany`でシンプルに書けるように出来る

@neuecc さんからTweetいただきました。

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">flatMapの実装にそういうのがあるからって感じで言語仕様的に一緒だからってわけではない感。(しかしC#はnullableの考慮を足すのが面倒) <a href="https://t.co/cweyEGg5Y2">https://t.co/cweyEGg5Y2</a> / “C#のOfTypeメソッド…” <a href="https://t.co/V6rrs1zCoW">https://t.co/V6rrs1zCoW</a></p>&mdash; neuecc (@neuecc) <a href="https://twitter.com/neuecc/status/802125612978610177">November 25, 2016</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">元の文中には「言語仕様的に一緒」とは書かれてない（同じ「入れ物」として抽象化している、というのが正しい）ので、コメントはちょっと読み違え含むです、すみません。 <a href="https://t.co/ASzeq8TrKs">https://t.co/ASzeq8TrKs</a></p>&mdash; neuecc (@neuecc) <a href="https://twitter.com/neuecc/status/802128983240286208">November 25, 2016</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">RxがTaskと融合するってのもIObservable.SelectManyにTaskのオーバーロード足しただけっていう話で、こういうのは見せ方が大事というのがある。全体的にそういう見せ方で統一されているから、そういうものだと扱える。そもそもLINQがそういうものですからね。</p>&mdash; neuecc (@neuecc) <a href="https://twitter.com/neuecc/status/802126395459530752">November 25, 2016</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/neuecc/6cbec456dab4d0bb3c81976799b5fc6e

```csharp
using System;
using System.Collections.Generic;

class Base { }

class A : Base { }
class B : Base { }

class Program
{
    static void Main(string[] args)
    {
        var objects1 = new Base[] { new A(), new B() };
        var objects2 = new int?[] { 1, 2, 100, null, 1000 };

        var hoge = objects1.SelectMany(x => x as A);
        var huga = objects2.SelectMany(x => x);
    }
}


public static class FlatMapLikeExtensions
{
    public static IEnumerable<U> SelectMany<T, U>(this IEnumerable<T> source, Func<T, U> selector)
        where U : class
    {
        foreach (var item in source)
        {
            var v = selector(item);
            if (v != null) yield return v;
        }
    }

    public static IEnumerable<U> SelectMany<T, U>(this IEnumerable<T> source, Func<T, U?> selector)
        where U : struct
    {
        foreach (var item in source)
        {
            var v = selector(item);
            if (v.HasValue) yield return v.Value;
        }
    }
}
```

`SelectManay`でシンプル(`Swift`の`flatMap`と同等)に書けるようになって、ついでに拡張メソッドの実装もラムダ式では無くなるので`yield`使えて http://qiita.com/mono0926/items/b8ff59938a60ddf93c6e#selectmanyで可能swiftのflatmapとまったく同じでは無い のコードより少しシンプルになっています。

この拡張メソッド足すと、C#でも`OfType`いらないのでは🤔と思えてきちゃいます(　´･‿･｀) (※1)

まったく同じ結果:

```csharp
var hoge = objects.OfType<A>();
var fuga = objects.SelectMany(x => x as A);
```

(※1: ただしパフォーマンス的に違いがあったりはしそう。)

# 関連

関連として、以下の @koher さんの記事などがとても参考にあります。
1年ほど前の記事ですが、このあたりはSwift 2からほぼ変わっていないはずです(1→2の間は色々改良あった)。

- [Swift 2.0の新しいflatMapが便利過ぎる - Qiita](http://qiita.com/koher/items/6a95cb8edb04fb81f787)
    - 本記事書いた後に、そういえばこの記事あったなと思い出して読み返したら、似た内容でした(　´･‿･｀)
- [ArrayとOptionalのmapは同じです - Qiita](http://qiita.com/koher/items/68585cff691d87124077)
	- こういう話題に踏み込もうとしたものの、こちらの記事に充分詳しく書かれていて書く余地無かった(　´･‿･｀)
- [mapとflatMapという便利メソッドを理解する - Qiita](http://qiita.com/shimesaba/items/1a89cb5600454f91cc67)