はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】LINQのZipメソッドで複数のコレクションを合体する

はじめに

今回はLINQのZipメソッドで複数のコレクションを合体する記事になります!

たとえばこのような状況はないでしょうか?
f:id:hanaaaaaachiru:20191016170237p:plain

一次元配列やリストといった二つのコレクションを一つに合体させたり、2つのシーケンスに対応した要素から何らかの操作を加えて一つのシーケンスを作成したりなどです。

そんなときは、LINQのZipを用いることで実現できます。

ということで早速みていきましょう!

Zipメソッド

定義はこちらになります。

public static IEnumerable<TResult> Zip<TFirst,TSecond,TResult> (this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst,TSecond,TResult> resultSelector);

Enumerable.Zip Method (System.Linq) | Microsoft Docs

めちゃ長く分かりにくいですね…。

簡潔にまとめると

  • 第一引数はマージするシーケンス
  • 第二引数は2つのシーケンスの要素をマージする方法を指定する関数

といった感じです。

百聞は一見に如かずということで、実際の例を見てみましょう。

using System.Linq;
using UnityEngine;

public class LINQTest : MonoBehaviour
{
    private void Start()
    {
        var names = new string[] { "スライム", "ドラキー", "ももんじゃ" };
        var hp = new int[] { 5, 7, 10 };
        var exe = new int[] { 2, 4, 8, 10 ,12};

        var results = names.Zip(hp, (name, h) => new { Name = name, Hp = h });

        foreach(var item in results)
        {
            print(item.Name);       //スライム  ドラキー    ももんじゃ
            print(item.Hp);         //5     7     10
        }

        //要素数が少ない方に合わせられる
        var results2 = names.Zip(exe, (name, e) => new { Name = name, Exe = e });

        foreach(var item in results2)
        {
            print(item.Name);       //スライム  ドラキー    ももんじゃ
            print(item.Exe);        //2     4     8
        }

    }
}

このように匿名クラスを用いれば簡潔に書くことができます。

ただ注意してしたいところは、合体するシーケンスの要素数が一方だけ少ないときは少ない方に合わせられるという点です。

3つ以上のシーケンスを合体する

ここはまだ解決していないのですが、もし3つ以上のシーケンスを合体したいという場合はこのように書くこともできます。

var names = new string[] { "スライム", "ドラキー", "ももんじゃ" };
var hp = new int[] { 5, 7, 10 };
var exe = new int[] { 2, 4, 8, 10 ,12};

var monsters = names.Zip(hp, (name, h) => new { Name = name, Hp = h })
    .Zip(exe, (t, e) => new { Name = t.Name, Hp = t.Hp, Exe = e });

foreach(var item in monsters)
{
    print(item.Name);       //スライム  ドラキー    ももんじゃ
    print(item.Hp);         //5     7      10
    print(item.Exe);        //2     4       8
}

ただ結構冗長なような気がするので、何か良い方法があったら是非コメント等で教えていただけると嬉しいです。

拡張メソッドを作るというのも手でしょうか。