UniRxとUniTask 相互変換の変わったパターン紹介

# はじめに

UniRxの`Observable`と、`UniTask`はそれぞれ相互変換することができます。
今回はその中でも少し変わった変換パターンを紹介したいと思います。

※ `System.Linq` `UniRx` `UniRx.Async` のusingを忘れずに

# `IEnumerable<UniTask<T>>` -> `IObservable<T>`

複数の`UniTask<T>`をまとめて、1つの`IObservable<T>`にする方法です。
やり方が何パターンかあります。


```cs
// 対象
IEnumerable<UniTask<string>> tasks = CreateSample();

// ---

// 並列にまとめる（要素の順序を無視して、終わったものから結果を返す）
IObservable<string> parallel = tasks
    .Select(x => x.ToObservable()) // IE<IO<T>>
    .Merge(); // IO<T>

// 直列にまとめる（要素の先頭から順番に結果を返す)
IObservable<string> sequential = tasks
    .Select(x => x.ToObservable()) // IE<IO<T>>
    .Concat(); // IO<T>


// 全部終わってからまとめて結果をとるなら（IO<IE<T>>）
IObservable<IList<string>> whenAll = tasks
    .Select(x => x.ToObservable()) // IE<IO<T>>
    .Zip(); // IO<IList<T>>

// でも、全部まとめてとるならUniTask.WhenAllでいいのでは?
UniTask<string[]> whenAll2 = UniTask.WhenAll(tasks);
```

# `UniTask<IEnumerable<T>>` -> `IObservable<T>`

さっきとネストの仕方が逆のパターン。
`UniTask<IE<T>>`を分解して1つの`IObservable<T>`にする方法です。
2パターンあるけど結果は同じです。

```cs
// 対象
UniTask<IEnumerable<string>> task = CreateSample();

// パターン1
IObservable<string> p1 = task
    .ToObservable() // IO<IE<T>>
    .Select(x => x.ToObservable()) // IO<IO<T>>
    .Merge(); // IO<T>

// パターン2
IObservable<string> p2 = task
    .ToObservable() // IO<IE<T>>
    .SelectMany(x => x.ToObservable()); // IO<T>
```

# `IObservable<T>` -> `UniTask<IEnumerable<T>>`

`IObservable<T>`が発行するすべてのメッセージを「まとめて」待ち受ける`UniTask<T>`を作りたい場合。

```cs
// 対象
IObservable<string> observable = CreateSample();

// ToArray()してからToUniTask()でOK
UniTask<string[]> task = observable.ToArray().ToUniTask();
```

# `IObservable<UniTask<T>>` -> `IObservable<T>`

`Observable`が`UniTask`を扱う場合に、それを1つの`Observable`にまとめる。

```cs
// 対象
IObservable<UniTask<string>> observable = CreateSample();

// 並列（終わった順に結果を出すなら）
IObservable<string> parallel = observable.SelectMany(x => x.ToObservable());

// 直列（もとのIO<T>から発行された順序を維持するなら）
IObservable<string> sequential = observable.Select(x => x.ToObservable()).Concat();
```

# `IObservable<UniTask<T>>` -> `UniTask<IEnumerable<T>>`

`Observable`が`UniTask`を扱う場合に、それを`UniTask`側にまとめる。

```cs
IObservable<UniTask<string>> observable = CreateSample();

// 結果は先に終わったUniTaskの順番になる
UniTask<string[]> task = observable
    .SelectMany(x => x.ToObservable()) // IO<IO<T>>
    .Merge() // IO<T>
    .ToArray() // IO<T[]>
    .ToUniTask(); // UniTask<T[]>
```

 
# `UniTask<IObservable<T>>` -> `IObservable<T>`

`UniTask`の中に`Observable`が入り込んじゃった場合。

```cs
// 対象
UniTask<IObservable<string>> task = CreateSample();

// taskをIO<IO<T>>に変換してからMerge()
IObservable<string> observable = task.ToObservable().Merge();
```

# `UniTask<IObservable<T>>` -> `UniTask<IEnumerable<T>>`

`UniTask`の中に`Observable`が入り込んじゃったものを、今度は`UniTask`側にまとめる場合。

```cs
// async/await使っちゃうのが楽
private async UniTask<IEnumerable<string>> Unwrap(UniTask<IObservable<string>> task)
{
    var observable = await task;
    return await observable.ToArray();
}
```

# `IObservable<IObservable<T>>` -> `UniTask<IEnumerable<T>>`

こんなシチュエーションあるのかよくわからないけど。

```cs
IObservable<IObservable<string>> observable = CreateSample();

UniTask<string[]> task = observable
    .SelectMany(x => x) // IO<T>
    .ToArray() // IO<T[]>
    .ToUniTask(); // UniTask<T[]>
```

# まとめ

`UniRx`と`UniTask`はだいたいどんなパターンでもそれぞれに変換することができます。
両者を組み合わせて使い、必要に応じて変換をかけるとよいでしょう。