2014年07月16日

C#で重複要素を取り除く

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書く?orgに感謝を込めて」シリーズ その25

■問題 (出題者:にしお さん)
与えられたリストxsの中から、 2回以上出現するものを全部取り除いてください。
サンプル入力
[3, 1, 4, 1, 5, 9, 2, 6, 5]
サンプル出力
[3, 4, 9, 2, 6]

これはアレイのuniqの派生問題です。 リストとかアレイという言葉は言語によってまちまちの意味で使われているので、 「配列のようなもの」という漠然とした意味にとって構いません。

重複要素を取り除く3つの異なるメソッドを書いてみました。どれもその要素の出現数を求めて、出現数が1の要素だけを返すようにしています。
1番目のバージョンは、同じ値でグルーピングして、出現数をカウントしています。
2番目のバージョンは、Dictionaryで、出現数をカウントしています。
3番目のバージョンのメソッドは、LINQのCountメソッドで出現数をカウントしています。ワンライナーでコード量は少なめですが、効率が気になります。

もっと簡潔で効率の良い書き方があるような気もしますが...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Doukaku.Org {
    class Program {
        static void Main(string[] args) {
            int[] array = { 5, 7, 3, 1, 4, 1, 5, 9, 2, 6, 5,1,7 };
            var r = UniqOnlyone01(array);
            foreach (var x in r) {
                Console.WriteLine(x);
            }
            Console.WriteLine();

            r = UniqOnlyone02(array);
            r.ToList().ForEach(x => Console.WriteLine(x));
            Console.WriteLine();

            r = UniqOnlyone03(array);
            r.ToList().ForEach(x => Console.WriteLine(x));
            Console.WriteLine();

            Console.ReadLine();
        }

        static IEnumerable<T> UniqOnlyone01<T>(IEnumerable<T> list) {
            var q = from n in list
                    group n by n;
            var q2 = from g in q
                     where g.Count() == 1
                     select g.Key;
            return q2;
        }

        static IEnumerable<T> UniqOnlyone02<T>(IEnumerable<T> list) {
            Dictionary<T, int> mem = new Dictionary<T, int>();
            foreach (var n in list) {
                if (mem.ContainsKey(n))
                    mem[n] = mem[n] + 1;
                else
                    mem[n] = 1;
            }
            return list.Where(e => mem[e] == 1);
        }

        static IEnumerable<T> UniqOnlyone03<T>(IEnumerable<T> list) {
            return list.Where(n => (list.Count(x => x.Equals(n)) == 1));
        }
    }
}


■追記 (2014/07/17)
 
Twitterで指摘がありました。
最初に示したクエリ式使ったコードを、拡張メソッド形式にするのが一番良さそうですね。

 return list.GroupBy(n => n).Where(g => g.Count() == 1).Select(g => g.Key);

Enumerable .Empty<> との Unionとれば良いのではという意見もあったようですが、
これだと、Distinctと同じ動きになり、問題の要求を満たしません。


 

この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/gushwell/52377889