創作世界のいろいろ〜AdobeとかC#とか

絵を描く人がC#とか3Dとか動画編集とかやる為の備忘録

【連載 Unity初心者向け】歴史学習アプリを作る(5 string配列→List<カスタムクラス>変換)

本記事ではLinqの使い方配列→カスタムクラス使用のListへ変換する解説を行います。初心者の方にわかりづらいLinqと配列→複数型を使用したListへの変換ですが、とても使える構文だと思います。私も勉強中なので一緒に学習いただけると良いかもです。

下記の続き

matsrikagraphic.hatenablog.com

前回の記事で、NCMBのファイルストアからDLしたcsvをstring配列に格納するところまで終わりました。ただ、前回のままでは、最後の解説文と最初の問題IDが一つの値になってしまっています。なので、最後の解説文の最後に『' , '』を入れて再度アップロードします。

 

String配列の問題集をカスタムクラスを使用したListへ入れる

完了したら、次に前回記事のContentsDownloader.csを下記のように変更します。

public class ContentsDownloader : MonoBehaviour
{
    [SerializeField] string sourceFileName;//~~~.csvのファイル名をインスペクターから指定する
    [SerializeField] int sourceFileValues;//インスペクターから配列数を指定する

    private string[] _allQuestions;

    void Start()
    {
        QuizContentManager();
    }

    public void QuizContentManager()
    {
        var file = new NCMBFile(sourceFileName);// NCMBFileを初期化
        _allQuestions = new string[sourceFileValues];// 指定した値の数で配列を作成

        // データを取得する (FetchAsync は NCMBAPI)
        file.FetchAsync((byte[] fileData, NCMBException error) =>
        {
            if (error != null) //失敗
            {
                Debug.Log(error);
            }
            else //成功
            {
                QuizContentsDL(fileData);
            }
        });
    }

    public void QuizContentsDL(byte[] fd)
    {
        // 受け取った byte[] を string 化する
        var sourceText = System.Text.Encoding.UTF8.GetString(fd);
        // 指定した値の数で配列を作成
        //var allQuestoins = new string[sourceFileValues];

        _allQuestions = sourceText.Split(',');

        for (int i = 0; i < _allQuestions.Length; i++)
        {
            //Debug.Log(_allQuestions[i]);
        }
        var QuizContents = new List<QuizContent>();
        //カスタムクラスにそれぞれの値をaddする
        for (int n = 0; n < 3; n++)
        {
            QuizContents.Add(new QuizContent
            {
                ID = int.Parse(_allQuestions[0 + n * 11]),
                QuizSentence = _allQuestions[1 + n * 11],
                Image_filename = _allQuestions[2 + n * 11],
                Selection_a = _allQuestions[3 + n * 11],
                Selection_b = _allQuestions[4 + n * 11],
                Selection_c = _allQuestions[5 + n * 11],
                Answer = _allQuestions[6 + n * 11],
                Genre_Era = _allQuestions[7 + n * 11],
                Genre_About = _allQuestions[8 + n * 11],
                Stage = int.Parse(_allQuestions[9 + n * 11]),
                Infomation = _allQuestions[10 + n * 11],
            });
        }
    }

    public class QuizContent
    {
        public int ID { get; set; }
        public string QuizSentence { get; set; }
        public string Image_filename { get; set; }
        public string Selection_a { get; set; }
        public string Selection_b { get; set; }
        public string Selection_c { get; set; }
        public string Answer { get; set; }
        public string Genre_Era { get; set; }
        public string Genre_About { get; set; }
        public int Stage { get; set; }
        public string Infomation { get; set; }
    }
}

主に追加したことは、csvのカラムに合わせたpublic class QuizContentを作成したこと、 NCMB APIのFetchasyncのelseの中にあった命令を外部の関数(メソッド)へ移したことです。

そして、作成したカスタムクラスへstring配列をそれぞれ格納しています。

上記コードで、問題集のデータが、カスタムクラスのID毎に管理できるようになります。

 

問題毎に各値を抜き出せるかLinqを使って確認する。

更に、ContentsDownloader.csを下記のように修正します。全文を載せます。

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

public class ContentsDownloader : MonoBehaviour
{
    [SerializeField] string sourceFileName;//~~~.csvのファイル名をインスペクターから指定する
    [SerializeField] int sourceFileValues;//インスペクターから配列数を指定する

    private string[] _allQuestions;

    void Start()
    {
        QuizContentManager();
    }

    public void QuizContentManager()
    {
        var file = new NCMBFile(sourceFileName);// NCMBFileを初期化
        _allQuestions = new string[sourceFileValues];// 指定した値の数で配列を作成

        // データを取得する (FetchAsync は NCMBAPI)
        file.FetchAsync((byte[] fileData, NCMBException error) =>
        {
            if (error != null) //失敗
            {
                Debug.Log(error);
            }
            else //成功
            {
                QuizContentsDL(fileData);
            }
        });
    }

    public void QuizContentsDL(byte[] fd)
    {
        // 受け取った byte[] を string 化する
        var sourceText = System.Text.Encoding.UTF8.GetString(fd);
        // 指定した値の数で配列を作成
        //var allQuestoins = new string[sourceFileValues];

        _allQuestions = sourceText.Split(',');

        for (int i = 0; i < _allQuestions.Length; i++)
        {
            //Debug.Log(_allQuestions[i]);
        }
        var QuizContents = new List<QuizContent>();
        //カスタムクラスにそれぞれの値をaddする
        for (int n = 0; n < 3; n++)
        {
            QuizContents.Add(new QuizContent
            {
                ID = int.Parse(_allQuestions[0 + n * 11]),
                QuizSentence = _allQuestions[1 + n * 11],
                Image_filename = _allQuestions[2 + n * 11],
                Selection_a = _allQuestions[3 + n * 11],
                Selection_b = _allQuestions[4 + n * 11],
                Selection_c = _allQuestions[5 + n * 11],
                Answer = _allQuestions[6 + n * 11],
                Genre_Era = _allQuestions[7 + n * 11],
                Genre_About = _allQuestions[8 + n * 11],
                Stage = int.Parse(_allQuestions[9 + n * 11]),
                Infomation = _allQuestions[10 + n * 11],
            });
        }
        SortQuizContents(QuizContents);

    }

public void SortQuizContents(IEnumerable<QuizContent> qc)
    {
        IEnumerable<QuizContent> sortquizContents = from QuizContent in qc where QuizContent.ID == 9990 select QuizContent;

        foreach (QuizContent quizContent in sortquizContents)
        {
            Debug.Log(quizContent.ID);
            Debug.Log(quizContent.QuizSentence);

        }
    }


    public class QuizContent
    {
        public int ID { get; set; }
        public string QuizSentence { get; set; }
        public string Image_filename { get; set; }
        public string Selection_a { get; set; }
        public string Selection_b { get; set; }
        public string Selection_c { get; set; }
        public string Answer { get; set; }
        public string Genre_Era { get; set; }
        public string Genre_About { get; set; }
        public int Stage { get; set; }
        public string Infomation { get; set; }
    }
}

追加したのは下記の部分です

public void SortQuizContents(IEnumerable<QuizContent> qc)
    {
        IEnumerable<QuizContent> sortquizContents = from QuizContent in qc where QuizContent.ID == 9990 select QuizContent;

        foreach (QuizContent quizContent in sortquizContents)
        {
            Debug.Log(quizContent.ID);
            Debug.Log(quizContent.QuizSentence);

        }
    }

Linqを使用してフィルターをかけています。

 IEnumerable<QuizContent> sortquizContents = from QuizContent in qc where QuizContent.ID == 9990 select QuizContent;

この一行で最初の問題だけを抜き出しています。

IEnumerable は、列挙可能にするListというイメージで、Listに似た使い方ができます。

Enumという列挙型変数がありますよね。

matsrikagraphic.hatenablog.com

ableというのは「〜可能」という意味になるので、そのままEnumerable<T>は列挙可能リストという感じで覚えてもらうと良いと思います。それ以上詳しいことは初心者なので現時点では私は分かりません。。

 

=の後の右辺がLinq構文です。

from QuizContent in qc where QuizContent.ID == 9990 select QuizContent;

色分けすると分かりやすいと思います。

こうすると何が確認できるかというと、このForeach文を見るように、


        foreach (QuizContent quizContent in sortquizContents)
        {
            Debug.Log(quizContent.ID);
            Debug.Log(quizContent.QuizSentence);

        }

QuizSentenceが、ID9990に紐づいたQuizSentenceだけ呼ばれるようになります。

つまり起動すると、ID9990である一問目だけを抜き出して書き出してくれるのです。

後に何も続いていないですよね?

一行でデータにフィルタリングをかけられるのです。

今現在、foreachでくくってる意味はありません。

foreachでくくるのは、フィルタリングに対して複数の項目が該当する場合に繰り返すためです。なので、仮に二問目のID9991の問題もIDを9990に変更してみると、一問目と二問目のID番号と問題文が表示されます。

これがLinqのメリットです。

 

Q. 引数ってなんで使うの?
A. 使う理由がずっと私も分かりませんでした。引数を使ってるのは、関数を分けるためです。間接的にこうやってローカル変数からローカル変数へ渡してあげると、関数(メソッド)を分けて同じ値の変数を操作することができます。

次回はUI作成にすすもうと思います。

**Linqの解説とC#基礎学習にはコレが絶対おすすめ!!**

**私も↑を読んで勉強しています**

とくにピーコックアンダーソンさんの本は分かりやすいです。