MRが楽しい

MRやVRについて学習したことを書き残す

EnumerableのSelectを使って条件に合致する値のインデックスを同時に取得する

本日は C# の小ネタ枠です。
EnumerableのSelectを使って条件に合致する値のインデックスを同時に取得する方法について記事に残します。

Selectメソッド

Select はシーケンスの各要素を指定したフォームに変更します。
また要素の変更だけでなく、Func の selector を利用することで各要素のインデックスを取得してフォームに組み込むことができます。
learn.microsoft.com

前回記事

今回は First メソッドで指定した条件に合致する最初の要素とそのインデックスを返すサンプルを作成します。
以下の First メソッドに関する前回記事のサンプルを流用します。
bluebirdofoz.hatenablog.com

サンプルコード

指定した条件に合致する最初の要素とそのインデックスを返すスクリプトを作成しました。
・SelectTest.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class SelectTest : MonoBehaviour
{
    [SerializeField] private List<MessageProfile> messageProfiles;
    [SerializeField] private string getType;
    
    /// <summary>
    /// First メソッドで条件に合致する値とそのインデックスを取得する
    /// </summary>
    [ContextMenu("GetFirstMessage")]
    public void GetFirstMessage()
    {
        var (targetProfile, targetIndex) = messageProfiles
            .Select((profile, index) => (profile, index)) // シーケンスの要素とインデックスにフォームを変換する
            .FirstOrDefault(data => data.profile.Type == getType); // 条件に合致する最初の値を取得する
        
        Debug.Log($"First : index = {targetIndex}, message = {targetProfile?.Message}");
    }
    
    /// <summary>
    /// メッセージ定義
    /// </summary>
    [Serializable]
    private class MessageProfile
    {
        [SerializeField] private string type;
        [SerializeField] private string message;

        public string Type => type;
        public string Message => message;

        public MessageProfile()
        {
            this.type = "NONE";
            this.message = "None Message";
        }
    }
}

スクリプトを起動すると以下の通り、合致する最初の要素とそのインデックスを取得することができました。

因みに条件に合致する値が見つからなかった場合、FirstOrDefault を利用していると、インデックスには int の規定値を返すため、0 が返却されます。
合致する値が見つからない場合、インデックスは正しくないため注意が必要です。

前回記事の DefaultIfEmpty で条件が合致しなかった場合の値を指定する方法を使えば、条件が合致しなかった場合のインデックスの値も指定できます。

    /// <summary>
    /// DefaultIfEmpty メソッドでデフォルト値を指定して条件に合致する値とそのインデックスを取得する
    /// </summary>
    [ContextMenu("GetDefaultIfEmptyMessage")]
    public void GetDefaultIfEmptyMessage()
    {
        var (targetProfile, targetIndex) = messageProfiles
            .Select((profile, index) => (profile, index)) // シーケンスの要素とインデックスにフォームを変換する
            .Where(data => data.profile.Type == getType) // 条件に合致する最初の値を取得する
            .DefaultIfEmpty((new MessageProfile(), -1)) // 条件に合致する値がなかった場合の値を指定する
            .First(); 
        
        Debug.Log($"First : index = {targetIndex}, message = {targetProfile?.Message}");
    }