Categories: C#

DataTableの重複する行を取得する

DataTableに属するDataRowの任意の列から、重複する行を除去する方法はあるのですが、

            DataTable dt = new DataTable();
            DataColumn dc = new DataColumn("id", typeof(ulong));
            dt.Columns.Add(dc);
            dc = new DataColumn("name", typeof(string));
            dt.Columns.Add(dc);

            NewMethod(dt, 10000, "nakata");
            NewMethod(dt, 10001, "honda");
            NewMethod(dt, 10002, "kagawa");
            NewMethod(dt, 10002, "nagatomo");
            NewMethod(dt, 10003, "okazaki");

            DataView dv = new DataView(dt);
            DataTable dt2 = dv.ToTable(true, "id");//trueでDistinct(重複を取り除く)

削除ではなく、同じ値のものを取得いたくて、その方法を調べたのですが、

たとえば、DataTable.Margeを使ってRowStatusで取得する方法
https://social.msdn.microsoft.com/Forums/ja-JP/3e46f64d-fed3-4370-a16a-0987c8710050/datatable-linq?forum=vbgeneralja

とか、なるほど!というのもあったのですが、これはprimary keyが設定してあるDataTable同志でなければいけないので、そうでない場合はどうすればいいか。というわけですが、DataTable.Selectで取り出してチェックするしかなさそう。

たとえば、こんな感じで。

            DataTable dt = new DataTable();
            DataColumn dc = new DataColumn("id", typeof(ulong));
            dt.Columns.Add(dc);
            dc = new DataColumn("name", typeof(string));
            dt.Columns.Add(dc);

            NewMethod(dt, 10000, "nakata");
            NewMethod(dt, 10001, "honda");
            NewMethod(dt, 10002, "kagawa");
            NewMethod(dt, 10002, "nagatomo");
            NewMethod(dt, 10003, "okazaki");

            DataView dv = new DataView(dt);
            DataTable dt_distinct = dv.ToTable(true, "id");//重複除去


            StringBuilder sb = new StringBuilder();
            foreach (DataRow _dr in dt_distinct.Rows)
            {
                string id = _dr["id"].ToString();
                if (id != "")
                {
                    if ((int)dt.Compute("COUNT(id)", "id=" + id) > 1)
                    {
                        sb.Append(((sb.Length > 0) ? " OR " : "") + "id=" + id);
                    }
                }
            }
            DataRow[] dr_array = dt.Select(sb.ToString());
            DataTable dt_overlap = dr_array.CopyToDataTable();

なんか、気持ち悪いですが、Selectで重複しているIDだけを取り出すという、ベタなやり方。

こんなときは、LINQでいきます。

            DataTable dt = new DataTable();
            DataColumn dc = new DataColumn("id", typeof(ulong));
            dt.Columns.Add(dc);
            dc = new DataColumn("name", typeof(string));
            dt.Columns.Add(dc);

            NewMethod(dt, 10000, "nakata");
            NewMethod(dt, 10001, "honda");
            NewMethod(dt, 10002, "kagawa");
            NewMethod(dt, 10002, "nagatomo");
            NewMethod(dt, 10003, "okazaki");

            var dr_array = from row in dt.AsEnumerable()
                            where (
                                from _row in dt.AsEnumerable() 
                                where (ulong)row["id"] == (ulong)_row["id"] 
                                select _row["id"]
                                ).Count() > 1 //重複していたら、2つ以上見つかる
                            select row;
            DataTable dt_overlap = dr_array.CopyToDataTable();

もっと、すっきりできそうだけど、こんな感じで。

nakaike