1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C#】Loto6当選データで学ぶ、プチLINQ入門

Posted at

1.LINQ初見の印象は「めんどくさい」

運用しているシステムにバグが見つかって、ソースを調査することになった。
そこで初対面したLINQ。
率直な感想は。

「面倒くさい。」

LINQの面倒くさい理由

  • メソッド(x=>x.col1)などの記述。
    ⇒この表現が謎でわかりにくい。
  • WhereやSelect、OrderByなどDB操作で見かけるキーワード。
    ⇒DBじゃないのに、何で使う?
  • 使わなくても、それなりの処理を書ける。
    ⇒新しいことは覚えたくない。

担当者が、バグの原因をすぐに突き止めて対応をしたけど、今後対応する事が出てくるかもしれない。
避けてばかりもいられない。

自分なりに理解を深めるために、ロト6の当選データのチカラを借りることにした。
高額当選金額のシャワーを浴びて、いつか私も!!

2.LINQとは

任意のデータの集合に対して、DB操作する感覚で情報を抽出できる仕組みだと思っている。

ここで要素x1(第一当選数字),x2(第二当選数字),x3(一等当選金額)とういう要素を持つデータ集合list_sampleについて考えてみる。
個人的に使い方に戸惑ったメソッドについてあげてみた。

Select

抽出したいデータの要素の列を指定する。
データの集合.Select(x=>x.x1)

複数の列名(x1(第一当選数字),x2(第二当選数字)で)で抽出したいとき
list_sample.Select(x=> new {x.x1,x.x2})
newがポイント

OrderByDescending

データの集合.OrderByDescending(x => 指定列)で記述する。
データを指定列の降順で並べる。

例)x3(一等当選金額)列を降順で並べる
var o1=list_sample.OrderByDescending(x => x.x3).Select(x=>x.x3);
※最高当選金額を目にして夢を持ちましょう。

ThenBy

データの集合に対して複数項目で並べる時に使う(2番目が昇順)

例)一等当選があった時のx1(第一当選数字),x2(第二当選数字)の昇順で並べる
var o1=list_sample.OrderBy(x => x.x1).ThenBy(x=>x.x2).Where(x=>x.x3>0).Select(x=> new {x.x1,x.x2});

朗報
where 条件に入っている列名を
selectしなくてもエラーはでません。

ThenByDescending

データの集合に対して複数項目で並べる時に使う(2番目が降順)

例)x1(第一当選数字)が昇順,x2(第二当選数字)が降順で並べる
var o2=list_sample.OrderBy(x => x.x1).ThenByDescending(x=>x.x2).Select(x=> new {x.x1,x.x2});

GroupBy

ある要素でグループ化して、集計したい場合に利用する。

例)x1(第一当選数字)の数値で分類分けして、出現回数を抽出する。
var o3 = list_sample.GroupBy(x=>x.x1).Select(x=> new{x.Key,Count0=x.Count()});

※分類分けに指定した列をSelectで指定するとき、x.Keyと記述する。

例)x1,x2の数値で分類分けして、出現回数を抽出する。
var o3 = list_sample.GroupBy(x=>new{x.x1,x.x2}).Select(x=> new{x.Key,Count0=x.Count()});

※上記でx1=1,x2=2の出現回数を知りたいときは
varo33=o3.Where(x=>x.Key.x1==1 && x.Key.x2==2 ).Count0
という指定方法になる。

LINQのメソッドを使って、次の項目ではプログラムを作成する。

3.プログラム概要

(1)参考URL(KYO's LOTO)で、ダウンロードしたLoto6.csv任意の場所(ソースではC:\work_c#)に配置し、これを読み込む。

(2)当選数字系データリスト(loto6_num_cnt)や、
当選金額系データリスト(loto6_amount)を作成する。

ロト6のCSVファイルの構成は下記のようになっている。

loto6_num_cnt:水色と緑色の枠の部分
loto6_amount:ピンク色と緑色の枠の部分

loto6_linq1_1.jpg

loto6_linq2_1.jpg
(3)下記の6項目についてファイルに出力する。

  • 最大一等当選金額
  • 最大キャリーオーバー金額(これが多いと夢も膨らむ!)
  • 過去100回の一等当選金額合計額(大金のシャワーを浴びよう)
  • 第一当選数字の最大数字
  • 第六当選数字の最小数字
  • 出現回数トップ2の当選数字と出現回数(出やすいのはどの数字?)

出力ファイルの内容

image.png

4.ソース

loto6_num_cnt.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace con_loto6_linq
{
 internal class Loto6_num_cnt
 {
  //数字
  public int Num2 { get; set; }
  //何番目の数字
  public int Type2 { get; set; }
  //キャリーオーバーかどうか
  public int Flg2 { get; set; }
  //開催回
  public int Kai2 { get; set; }
 }
}
loto6_amount.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace con_loto6_linq
{
 internal class Loto6_amount
 {
  //開催回
  public int No { get; set; }
  //開催日
  public string Date { get; set; }
  //ボーナス数字 
  public int Num_b { get; set; }
  //一等口数
  public int Num_of_word { get; set; }
  //当選金額
  public long Win_amount { get; set; }
  //キャリーオーバー
  public long Carry_over1 { get; set; }
 }
}
Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace con_loto6_linq
{
 internal class Program
 {
  static void Main(string[] args)
  {

   List<Loto6_amount> list_amount= new List<Loto6_amount>();
   List<Loto6_num_cnt> list_cnt= new List<Loto6_num_cnt>();
   // 読み込みたいCSVファイルのパスを指定して開く
   string fname = @"C:\work_c#\loto6.csv";
   StreamReader sr = new StreamReader(fname);
   //ヘッダを読み飛ばす
   string line_header = sr.ReadLine();

   // 末尾まで繰り返す
   while (!sr.EndOfStream)
   {
    // CSVファイルの一行を読み込む
    string line = sr.ReadLine();
    // 読み込んだ一行をカンマ毎に分けて配列に格納する
    string[] line_csv = line.Split(',');

    Loto6_amount lt6_amount = new Loto6_amount();
    lt6_amount.No= int.Parse(line_csv[0]);
    lt6_amount.Date = line_csv[1];
    lt6_amount.Num_b = int.Parse(line_csv[8]);
    lt6_amount.Num_of_word = int.Parse(line_csv[9]);
    lt6_amount.Win_amount = long.Parse(line_csv[14]);
    lt6_amount.Carry_over1 = long.Parse(line_csv[19]);
    list_amount.Add(lt6_amount);

    for (int j = 1; j < 7; j++)
    {
     Loto6_num_cnt lt6_cnt = new Loto6_num_cnt();
     lt6_cnt.Kai2 = lt6_amount.No;
     lt6_cnt.Num2 = int.Parse(line_csv[j+1]);
     lt6_cnt.Type2 = j;
     lt6_cnt.Flg2 = lt6_amount.Carry_over1 == 0 ? 0 : 1;
     list_cnt.Add(lt6_cnt);
    }
   }

   //★出力ファイル
   string filepath2 = @"C:\work_c#\analysis_loto6.txt";
   StreamWriter writer = new StreamWriter(filepath2, false, Encoding.UTF8);

   //★★LINQ     
   //最大一等賞金
   long max_amount = list_amount.Select(x=>x.Win_amount).Max();

   //最大キャリーオーバー額
   long max_carry1 = list_amount.Select(x => x.Carry_over1).Max();

   //最新開催回
   long newNo = list_cnt.Select(x => x.Kai2).Max();
   //一等当選合計金額(過去100回の)
   var order_total_win_amount = list_amount.Where(x => x.No >= newNo-99 && x.No <= newNo)
    .Select(x => new { x.Win_amount, x.Num_of_word, multi0 = x.Win_amount * x.Num_of_word });

   long total_win_amount = order_total_win_amount.Select(x => x.multi0).Sum();

   writer.WriteLine("★★当選金額系データ");
   writer.WriteLine("★最高1等当選金額:" + max_amount.ToString() + ",★最高キャリーオーバー:" + max_carry1.ToString());
   writer.WriteLine("★過去100回の一等当選金額合計:"+total_win_amount.ToString());

   //最大の第1数字
   int max_no1 = list_cnt.Where(x => x.Type2 == 1).Select(x => x.Num2).Max();

   //最小の第6数字
   int min_no6 = list_cnt.Where(x => x.Type2 == 6).Select(x => x.Num2).Min();
    
   //当選数字と出現回数
   var max_pair = list_cnt.GroupBy(x => x.Num2).Select(x=>new { x.Key,count0=x.Count()}).OrderByDescending(x => x.count0);
   //出現回数トップ2
   var order_max_2cnt = max_pair.Select(x=>x.count0).Distinct().Take(2);

   writer.WriteLine("★★当選数字系データ");
   writer.WriteLine("★最大第一当選数字"+max_no1.ToString()+ ",★最小第六当選数字"+min_no6.ToString());

   writer.WriteLine("★出現回数top2リスト");
   foreach (var nn in order_max_2cnt)
   {
    var o2 = max_pair.Where(x => x.count0 == nn).Select(x => new { x.Key, x.count0 });
    foreach (var o22 in o2)
     writer.WriteLine(o22.count0.ToString() + "回," + o22.Key.ToString());
   }
   writer.Close();
   Console.WriteLine("出力しました");
   Console.ReadKey();
  }
 }
}

5.LINQを使用した感想

  • listへの複雑な処理が、 DB操作する感覚で書ける!!
    ⇒DB処理の経験があるとイメージがわきやすい。
  • 憂鬱なラムダ式への抵抗がなくなる。
  • 余分な判定、ループに伴うインデントがないので、見やすい上に短い。
  • 多くのメソッドを実装して、その魅力を存分に味わいたい。

6.参考url

はじめての LINQ
豊富なメソッドサンプルと説明でLINQの基礎が学べます。

KYO's LOTO
当選データのCSVファイルがダウンロードできる
とても有難いサイト。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?