LINQで選択肢の全組み合わせ展開(クエリ式を使う)


LINQで選択肢の全組み合わせ展開の続き。
選択肢数が可変でもOK!とするために再帰でやったけど、
選択肢数も型も決まってるんなら、こっちの方がスマートかと。

というわけで実装。

クエリ式で実装

SQLで全組み合わせ作る時に、結合条件なしで直積を作ってたよね、ってことで、
クエリ式でもjoinを使わずに、選択肢の数だけ「from~in~」を並べて全組み合わせを展開。
その後で、重複が無いものだけを引っこ抜く。

Program.cs
        public static void Main(string[] args)
        {
            var items = new int[] { 1, 2, 3 };

            //結合条件なしで重複ありの全組み合わせを展開
            //展開結果は配列にする
            //q1{1,1,1}{1,1,2}{1,1,3}{1,2,1}…{3,3,3}
            var q1 = from s1 in items
                     from s2 in items
                     from s3 in items
                     select new int[] { s1, s2, s3 };

            //各要素の重複なしのレコードのみ抽出して返す
            //要素の個数と、各要素の値の種類数が一致していれば重複無しと判断
            // q1={1,3,1}の場合、要素の個数は3、値の種類数は2(1と3しかないから)→×:除外
            // q1={1,3,2}の場合、要素の個数は3、値の種類数は3→○:抽出
            // q1={1,1,1}の場合、要素の個数は3、値の種類数は1(1しかないから)→×:除外
            var q2 = q1.Where(ar => ar.Length == ar.Distinct().Count());

            q2.DisplayElement<int>();
        }

実行結果確認用のコード

結果確認用の拡張メソッド。(前回作ったそのままでよかったね。。。)

Extention.cs
        /// <summary>
        /// 結果表示用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        public static void DisplayElement<T>(this IEnumerable<T[]> query)
        {
            foreach (var q in query)
            {
                Console.WriteLine(string.Join(" ", q));
            }
        }

実行結果

おっけー!

修正前

albireoさんからアドバイスをいただき、修正しました。修正前は↓。
Selectの射影を匿名型で取る意味ってなかったね。。。

修正前のコード

【修正前】Program.cs
        public static void Main(string[] args)
        {
            var items = new int[] { 1, 2, 3 };

            //結合条件なしで重複ありの全組み合わせを展開
            //q1{1,1,1}{1,1,2}{1,1,3}{1,2,1}…{3,3,3}
            var q1 = from s1 in items
                     from s2 in items
                     from s3 in items
                     select new { N1 = s1, N2 = s2, N3 = s3 };

            //各要素の重複なしのレコードのみ抽出して返す
            //要素の個数と、各要素の値の種類数が一致していれば重複無しと判断
            // q1={1,3,1}の場合、要素の個数は3、値の種類数は2(1と3しかないから)→×:除外
            // q1={1,3,2}の場合、要素の個数は3、値の種類数は3→○:抽出
            // q1={1,1,1}の場合、要素の個数は3、値の種類数は1(1)→×:除外
            var q2 = q1.Where(v =>
            {
                var ar = new int[] { v.N1, v.N2, v.N3 };
                return ar.Length == ar.Distinct().Count();
            }
            );
            q2.DisplayElement();
        }

修正前の実行結果